API reference
REST reference for the Shadow API — auth, sites, captures, tutorials, videos, and webhooks.
The Shadow API is a REST JSON API mounted under /api. Authentication uses session cookies for browser clients, and long-lived bearer tokens for server-to-server calls.
All timestamps are ISO-8601 UTC. All id values are 24-character opaque identifiers unless stated otherwise.
Authentication
GET /api/orgs HTTP/1.1
Host: byshadow.ai
Cookie: better-auth.session_token=...
For server integrations, send a bearer token issued from the dashboard:
GET /api/sites/:siteId/videos HTTP/1.1
Authorization: Bearer ta_...
Organizations
List organizations
GET /api/orgs
Response:
[
{ "id": "661fcc...", "name": "Acme Inc", "slug": "acme", "plan": "growth" }
]
Sites
Create a site
POST /api/orgs/:orgId/sites
Content-Type: application/json
{ "name": "My SaaS", "url": "https://app.example.com" }
Response includes the secret key once:
{ "id": "...", "publicKey": "...", "apiKeySecret": "ta_secret_..." }
Verify install
POST /api/sites/:siteId/verify-install
Returns { verified: true, lastEventAt } once the SDK has posted at least one event.
Tutorials
Create a tutorial
POST /api/sites/:siteId/tutorials
Content-Type: application/json
{ "title": "How to create a project", "prompt": "Walk through creating a project from scratch." }
Compose the storyboard
POST /api/sites/:siteId/tutorials/:tutorialId/compose
Render the video
POST /api/sites/:siteId/tutorials/:tutorialId/render
Videos
List rendered videos
GET /api/sites/:siteId/videos
Get video detail (with presigned playback URL)
GET /api/sites/:siteId/videos/:videoId
Update video (publish, rename, feature)
PATCH /api/sites/:siteId/videos/:videoId
Content-Type: application/json
{ "embedEnabled": true, "isMarketingDemo": false, "title": "New title" }
Public endpoints
These endpoints don’t require authentication and are safe to call from browsers.
Get a public video
GET /api/public/videos/:slug
Returns the playback URL, title, duration, and brand overlay.
Record a video event (beacon)
POST /api/public/videos/:slug/events
Content-Type: application/json
{ "sessionId": "...", "event": "progress_50", "positionMs": 34200 }
Marketing demo
GET /api/public/marketing-demo
Returns { slug, title } for the currently featured demo video, or nulls.
Contact form
POST /api/public/contact
Content-Type: application/json
{ "name": "...", "email": "...", "company": "...", "message": "..." }
Errors
All errors return a structured JSON body:
{ "error": { "code": "NOT_FOUND", "message": "Video not found" } }
Common codes: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION, RATE_LIMITED, INTERNAL.