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