Skip to content

Admin API Endpoints ​

The admin API provides CRUD operations for managing tours, content, themes, audiences, analytics, and admin users. All endpoints require admin authentication (API key + JWT + admin strategy check).

Authentication ​

Every admin endpoint runs the require_admin dependency:

  1. API key resolves the tenant.
  2. JWT is validated against the tenant's JWKS.
  3. The tenant's configured admin strategy determines whether the user is an admin.

Requests from non-admin users receive 403 Forbidden.

bash
# All admin requests require both headers:
-H "X-API-Key: sk_live_..."
-H "Authorization: Bearer <admin-jwt>"

Tours ​

List Tours ​

http
GET /v1/admin/tours?limit=20&offset=0
ParameterTypeDefaultDescription
limitint20Items per page (1-100)
offsetint0Pagination offset

Response 200 OK

json
{
  "data": {
    "items": [
      {
        "tour_id": "tour_456",
        "title": "Analytics Dashboard Tour",
        "status": "published",
        "version": 3,
        "step_count": 5,
        "created_at": "2025-05-01T00:00:00Z",
        "updated_at": "2025-05-10T14:22:00Z"
      }
    ],
    "total": 12,
    "limit": 20,
    "offset": 0
  }
}

Create Tour ​

http
POST /v1/admin/tours
json
{
  "title": "Getting Started",
  "description": "Onboarding tour for new users",
  "url_patterns": [
    { "pattern": "/dashboard*", "is_regex": false }
  ],
  "steps": [
    {
      "step_id": "welcome",
      "order": 0,
      "title": "Welcome",
      "body": "Welcome to the dashboard.",
      "element_target": {
        "data_attribute": "main-header"
      },
      "position": "bottom"
    }
  ],
  "trigger": {
    "trigger_type": "page_load",
    "delay_ms": 1000
  },
  "audience_id": null,
  "rollout": { "enabled": true, "percentage": 100 },
  "schedule": { "start_at": null, "end_at": null }
}

Response 201 Created -- returns the full tour object with generated tour_id.

Each step requires step_id (any unique string), order (zero-based integer), title, body, and element_target (must include at least one of data_attribute / element_id / css_selector; selector is accepted as an alias for css_selector).

Trigger types (trigger.trigger_type, alias trigger.type): page_load · manual · first_visit · event · delay · element_visible · element_click · hover · scroll_depth · idle. See backend/src/spotlight/config/constants.py:TriggerType for the canonical list.

Audit event: tour.create

Get Tour ​

http
GET /v1/admin/tours/{tour_id}

Response 200 OK -- full tour object including all steps and configuration.

Update Tour ​

http
PUT /v1/admin/tours/{tour_id}

Accepts a partial update -- only provided fields are modified. The updated_at timestamp is set automatically.

Audit event: TourUpdatedByAdmin (includes before/after snapshots)

Publish Tour ​

http
POST /v1/admin/tours/{tour_id}/publish

Creates an immutable version in Spotlight.Tours.Versions and sets the tour status to published. The tour becomes visible to end users matching the targeting criteria.

Response 200 OK

json
{
  "data": {
    "tour_id": "tour_456",
    "status": "published",
    "version": 4,
    "published_at": "2025-05-10T15:00:00Z"
  }
}

Audit event: TourPublishedByAdmin

Archive Tour ​

http
POST /v1/admin/tours/{tour_id}/archive

Removes the tour from user-facing content. Archived tours cannot be edited but can be duplicated.

Audit event: TourArchivedByAdmin

Copy Tour ​

http
POST /v1/admin/tours/{tour_id}/copy

Creates a new draft tour with the same configuration as the source. The new tour gets a fresh tour_id.

Audit event: TourCopiedByAdmin

Version History ​

http
GET /v1/admin/tours/{tour_id}/versions

Returns all published versions for the tour, ordered by version number descending.

Rollback Version ​

http
POST /v1/admin/tours/{tour_id}/rollback
json
{
  "version": 2
}

Restores the tour definition to a previous version and publishes it as a new version.

Audit event: TourVersionRolledBackByAdmin


Content ​

Non-tour content items: tooltips, spotlights, banners, modals.

List Content ​

http
GET /v1/admin/content?limit=20&offset=0

Create Content ​

http
POST /v1/admin/content
json
{
  "content_type": "tooltip",
  "title": "New Feature Alert",
  "body": "Check out our new reporting tools.",
  "url_patterns": [
    { "pattern": "/reports*", "is_regex": false }
  ],
  "element_target": {
    "element_id": "reports-tab"
  },
  "priority": 100,
  "dismiss_behavior": "once",
  "cta_text": "Learn More",
  "cta_url": "/docs/reports",
  "trigger": {
    "trigger_type": "page_load",
    "delay_ms": 500
  },
  "rollout": { "enabled": true, "percentage": 100 },
  "schedule": { "start_at": null, "end_at": null }
}

Response 201 Created -- returns the full content object with generated content_id (format: cnt_{12-char-hex}).

Audit event: ContentCreatedByAdmin

Get Content ​

http
GET /v1/admin/content/{content_id}

Update Content ​

http
PUT /v1/admin/content/{content_id}

Partial updates supported. Accepts any combination of fields.

Audit event: ContentUpdatedByAdmin

Publish Content ​

http
POST /v1/admin/content/{content_id}/publish

Sets status to published, making the content visible to end users.

Audit event: ContentPublishedByAdmin

Archive Content ​

http
POST /v1/admin/content/{content_id}/archive

Audit event: ContentArchivedByAdmin


Themes ​

List Themes ​

http
GET /v1/admin/themes

Create Theme ​

http
POST /v1/admin/themes
json
{
  "name": "Ember",
  "variables": {
    "--spot-bg": "#15151d",
    "--spot-primary": "#ff6a4d",
    "--spot-text": "#f8fafc",
    "--spot-radius": "10px"
  }
}

variables is a flat map of CSS custom properties (the --spot-* design-token set the SDK reads at render time). Call GET /v1/admin/themes/presets to see a built-in palette to start from, or GET /v1/admin/themes/{theme_id} on any existing theme to discover the full token set. Omit variables to seed with the default Indigo palette and tweak via PUT below.

light / dark variant objects are accepted for explicit light + dark token sets; variables alone implies the light variant only.

Audit event: theme.create

Update Theme ​

http
PUT /v1/admin/themes/{theme_id}

Body shape mirrors create — pass variables (flat map) or light / dark (variant objects) to replace the corresponding token set. Omit a field to leave it unchanged.

Audit event: theme.update

Set Default Theme ​

http
POST /v1/admin/themes/{theme_id}/set-default

Marks this theme as the active palette for the tenant. The SDK on every page picks it up on the next page load. Unsetting happens automatically when another theme is set as default.

Audit event: theme.set_default

Delete Theme ​

http
DELETE /v1/admin/themes/{theme_id}

Returns 204 No Content on success. Refuses with 409 Conflict when:

  • The theme is the tenant's current default (set-default another theme first).
  • The theme is a shared / built-in preset (is_shared: true) — these are read-only.

Audit event: theme.deleted


Audiences ​

Audience rules live inline on each piece of content via the audience field on tours, modals, banners, hotspots, spotlights, tooltips, and checklists. See the relevant content-type page for the rule schema.


Analytics ​

Selector Health ​

http
GET /v1/admin/analytics/selector-health?window_days=7&failure_threshold=3

See Element Selector: Selector Health Monitoring.

Per-tour and per-content analytics are surfaced in the dashboard under Analytics.


Admin Users ​

Manage admin access for the tenant (only when using the table_lookup admin strategy).

List Admin Users ​

http
GET /v1/admin/users

Add Admin User ​

http
POST /v1/admin/users
json
{
  "user_id": "auth0|user_789",
  "email": "admin@example.com",
  "role": "admin",
  "name": "Jane Doe"
}

Audit event: AdminUserAddedByAdmin

Update Admin User ​

http
PUT /v1/admin/users/{user_id}

Audit event: AdminUserUpdatedByAdmin

Remove Admin User ​

http
DELETE /v1/admin/users/{user_id}

Audit event: AdminUserRemovedByAdmin


Admin Activity ​

Activity Log ​

http
GET /v1/admin/activity?limit=50

Returns the admin's recent activity from the Audit.AdminActions table.

Activity by Entity ​

http
GET /v1/admin/activity/entity/{entity_type}/{entity_id}

Returns the full change history for a specific entity (tour, content, theme, etc.).


Configuration ​

Get Tenant Config ​

http
GET /v1/admin/config

Returns the tenant's configuration including admin strategy, allowed origins, and feature flags.

Update Tenant Config ​

http
PUT /v1/admin/config

Toggle Circuit Breaker ​

http
POST /v1/admin/config/circuit-breaker
json
{
  "feature": "tours",
  "enabled": false
}

Emergency kill switch that disables all tours or content for the tenant without archiving individual items.

Audit event: CircuitBreakerToggledByAdmin


Error Responses ​

All admin endpoints return errors in the standard envelope:

json
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Content not found",
    "request_id": "req_abc123"
  }
}
StatusCodeDescription
400VALIDATION_ERRORInvalid request body
401UNAUTHORIZEDMissing or invalid credentials
403FORBIDDENUser is not an admin
404NOT_FOUNDResource does not exist
409CONFLICTResource state conflict (e.g., publishing a published tour)
422UNPROCESSABLE_ENTITYValid JSON but invalid data
429RATE_LIMITEDToo many requests
500INTERNAL_ERRORServer error

Spotlight