Skip to content

SDK Endpoints

The SDK-facing API provides four endpoints used by the Spotlight frontend SDK to fetch configuration, retrieve content, record user events, and track progress. All endpoints are prefixed with /v1/sdk/.

Authentication

All SDK endpoints require:

  • X-API-Key header for tenant identification.
  • Authorization: Bearer <jwt> header for user authentication.

See Authentication for details.

Base URL

https://api.{region}.spotlight.example.com/v1/sdk/

Local development: http://localhost:8000/v1/sdk/


GET /v1/sdk/config

Fetches the SDK configuration for the authenticated tenant, including active tours, content, themes, and feature flags applicable to the current user and page context.

Request

bash
curl -X GET "https://api.example.com/v1/sdk/config?url=/dashboard/analytics&locale=en" \
  -H "X-API-Key: sk_live_..." \
  -H "Authorization: Bearer <jwt>"

Query Parameters

ParameterTypeRequiredDescription
urlstringYesCurrent page path (for URL pattern matching)
localestringNoUser's locale for i18n content (default: tenant default)

Response 200 OK

json
{
  "data": {
    "tenant_id": "tenant_abc",
    "user_id": "user_123",
    "is_admin": false,
    "theme": {
      "theme_id": "theme_default",
      "tokens": {
        "colors": { "primary": "#4F46E5" },
        "typography": { "font_family": "'Inter', sans-serif" },
        "spacing": { "border_radius": "8px" }
      }
    },
    "tours": [
      {
        "tour_id": "tour_456",
        "title": "Analytics Dashboard Tour",
        "version": 3,
        "steps": [
          {
            "step_id": "step_1",
            "title": "Welcome",
            "body": "This is your analytics dashboard.",
            "element_target": {
              "data_attribute": "analytics-overview"
            },
            "positioning": {
              "placement": "bottom",
              "offset_x": 0,
              "offset_y": 8,
              "arrow": true
            }
          }
        ],
        "trigger": {
          "trigger_type": "page_load",
          "delay_ms": 2000
        }
      }
    ],
    "content": [
      {
        "content_id": "cnt_789",
        "content_type": "tooltip",
        "title": "New Feature",
        "body": "Check out our new reporting tools.",
        "element_target": {
          "element_id": "reports-tab"
        },
        "dismiss_behavior": "once"
      }
    ],
    "user_progress": {
      "tour_456": {
        "status": "in_progress",
        "current_step": "step_1",
        "completed_steps": [],
        "started_at": "2025-05-10T10:00:00Z"
      }
    },
    "feature_flags": {
      "show_analytics_tour": true,
      "enable_checklist": false
    }
  }
}

Response Fields

FieldDescription
tenant_idThe resolved tenant identifier
user_idThe authenticated user's ID (from JWT sub)
is_adminWhether the user has admin privileges
themeActive theme tokens for rendering UI components
toursTours matching the current URL and audience criteria
contentNon-tour content (tooltips, spotlights, banners) for the page
user_progressUser's progress state for each returned tour
feature_flagsFeature flag values relevant to returned content

Filtering Logic

The config endpoint applies several filters server-side:

  1. URL matching -- only tours/content whose url_patterns match the url parameter.
  2. Audience -- only items targeting the user's audience segment.
  3. Rollout -- percentage-based rollout check (deterministic per user ID).
  4. Schedule -- only items within their scheduled date range.
  5. Feature flags -- items gated behind a disabled flag are excluded.
  6. Status -- only published items are returned.
  7. Dismiss state -- content with dismiss_behavior: "once" is excluded if already dismissed.

POST /v1/sdk/content

Fetches content for a specific content ID. Used when the SDK needs to load content on demand (e.g., triggered by a custom event or element visibility).

Request

bash
curl -X POST "https://api.example.com/v1/sdk/content" \
  -H "X-API-Key: sk_live_..." \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "content_ids": ["cnt_789", "tour_456"],
    "url": "/dashboard",
    "locale": "en"
  }'

Request Body

FieldTypeRequiredDescription
content_idsstring[]YesIDs of content to fetch
urlstringNoCurrent page path for context
localestringNoLocale for i18n content

Response 200 OK

json
{
  "data": {
    "items": [
      {
        "content_id": "cnt_789",
        "content_type": "tooltip",
        "title": "New Feature",
        "body": "Check out our new reporting tools."
      }
    ],
    "not_found": ["tour_456"]
  }
}

POST /v1/sdk/events

Records user interaction events. The SDK batches events and sends them periodically or on page unload.

Request

bash
curl -X POST "https://api.example.com/v1/sdk/events" \
  -H "X-API-Key: sk_live_..." \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "event_type": "TourStarted",
        "content_id": "tour_456",
        "timestamp": "2025-05-10T14:22:00Z",
        "metadata": {
          "page_url": "/dashboard/analytics",
          "session_id": "sess_abc123"
        }
      },
      {
        "event_type": "StepViewed",
        "content_id": "tour_456",
        "step_id": "step_1",
        "timestamp": "2025-05-10T14:22:02Z",
        "metadata": {
          "page_url": "/dashboard/analytics",
          "session_id": "sess_abc123"
        }
      }
    ]
  }'

Request Body

FieldTypeRequiredDescription
eventsEvent[]YesArray of events to record

Event Object

FieldTypeRequiredDescription
event_typestringYesEvent type (see Analytics)
content_idstringYesTour or content ID
step_idstringNoStep ID (for step-level events)
timestampstringYesISO 8601 timestamp
metadataobjectNoAdditional context (page URL, session ID, etc.)

Response 202 Accepted

json
{
  "data": {
    "accepted": 2,
    "rejected": 0
  }
}

Events are written to the Events.Outbox table transactionally. The outbox processor publishes them to EventBridge asynchronously.

Supported Event Types

TourStarted      StepViewed       StepCompleted
TourCompleted    TourDismissed    TourRestarted
ContentViewed    ContentDismissed ContentCtaClicked
target_not_found

POST /v1/sdk/progress

Updates the user's progress state for a tour or checklist. Called when the user advances steps, completes, or dismisses content.

Request

bash
curl -X POST "https://api.example.com/v1/sdk/progress" \
  -H "X-API-Key: sk_live_..." \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "content_id": "tour_456",
    "action": "complete_step",
    "step_id": "step_1",
    "metadata": {
      "time_on_step_ms": 4500
    }
  }'

Request Body

FieldTypeRequiredDescription
content_idstringYesTour or content ID
actionstringYesProgress action (see below)
step_idstringConditionalRequired for step-level actions
metadataobjectNoAdditional context

Progress Actions

ActionDescription
startMark the tour as started
complete_stepMark a specific step as completed
completeMark the entire tour as completed
dismissMark the tour as dismissed
restartReset progress and restart the tour

Response 200 OK

json
{
  "data": {
    "content_id": "tour_456",
    "status": "in_progress",
    "current_step": "step_2",
    "completed_steps": ["step_1"],
    "started_at": "2025-05-10T14:22:00Z",
    "updated_at": "2025-05-10T14:22:05Z"
  }
}

Progress Storage

Progress is stored in the Spotlight.Progress.UserState table:

PK: tenant_user_id    = "tenant_abc#user_123"
SK: content_id         = "tour_456"

A GSI (gsi-content-users) enables querying all users' progress for a specific tour:

GSI PK: tenant_content_id = "tenant_abc#tour_456"
GSI SK: user_id            = "user_123"

Rate Limiting

SDK endpoints are rate limited at the API Gateway level:

SettingDefault Value
Rate limit100 requests/second
Burst limit50 requests

When the limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header.

The X-RateLimit-Remaining header is included in responses to help clients implement client-side throttling.

Response Envelope

All SDK endpoints return responses in a standard envelope:

json
{
  "data": { },
  "meta": {
    "request_id": "req_abc123",
    "timestamp": "2025-05-10T14:22:00Z"
  }
}

Error responses:

json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid API key",
    "request_id": "req_abc123"
  }
}

Spotlight