REST API

Base URL: https://<your-site>/api (through the website) or the API service directly. The machine-readable OpenAPI 3 document is served at /docs/json on the API service, with an interactive explorer at /docs. Export it with npm run openapi -w @skein/api.

Authentication

Client How
Browser skein_session cookie + x-csrf-token header (value of the skein_csrf cookie) on non-GET requests
Unity Editor Authorization: Bearer <access token> from the device flow; refresh with POST /v1/auth/token/refresh
Tools / CI Authorization: Bearer sk_… project API key

Errors

Errors are JSON: { "error": { "code": "PLAN_LIMIT", "message": "…human readable…", "details": { … } } }. message is written to be shown to users as-is. Validation errors (VALIDATION_FAILED) include details.fields. Missing and inaccessible resources both return 404.

Status Code examples
400 VALIDATION_FAILED, BAD_REQUEST, device-flow codes authorization_pending, slow_down, access_denied, expired_token
401 UNAUTHENTICATED, INVALID_CREDENTIALS
402 PLAN_LIMIT (with details.limit)
403 FORBIDDEN, CSRF_FAILED
404 NOT_FOUND
409 EMAIL_TAKEN, ALREADY_MEMBER, LAST_OWNER, SESSION_ENDED
429 RATE_LIMITED

Endpoints

Auth

  • POST /v1/auth/signup {email, password, displayName} · POST /v1/auth/login · POST /v1/auth/logout · GET /v1/auth/csrf
  • POST /v1/auth/verify-email {token} · POST /v1/auth/verify-email/resend
  • POST /v1/auth/password/forgot {email} · POST /v1/auth/password/reset {token, password}
  • Device flow: POST /v1/auth/device/code → GET /v1/auth/device/{userCode} → POST /v1/auth/device/approve → POST /v1/auth/device/token
  • POST /v1/auth/token/refresh {refreshToken} · POST /v1/auth/token/revoke
  • OAuth: GET /v1/auth/oauth/providers, GET /v1/auth/oauth/{provider}/start?redirect=/path, GET /v1/auth/oauth/{provider}/callback

Account

  • GET /v1/me · PATCH /v1/me · POST /v1/me/password · GET /v1/me/sessions · DELETE /v1/me/sessions/{id}

Organizations

  • GET /v1/orgs · POST /v1/orgs · PATCH /v1/orgs/{id}
  • GET|POST /v1/orgs/{id}/members · DELETE /v1/orgs/{id}/members/{userId}
  • GET /v1/orgs/{id}/audit-logs (Studio)

Projects & members

  • GET /v1/projects (with live online counts and active sessions) · POST /v1/projects · GET|PATCH|DELETE /v1/projects/{id}
  • GET /v1/projects/{id}/activity
  • GET /v1/projects/{id}/members · PATCH|DELETE /v1/projects/{id}/members/{userId}
  • POST /v1/projects/{id}/invitations · DELETE /v1/projects/{id}/invitations/{invitationId}
  • GET /v1/invitations/{token} · POST /v1/invitations/accept

Sessions

  • GET|POST /v1/projects/{id}/sessions · GET /v1/sessions/{id} · POST /v1/sessions/{id}/end
  • POST /v1/sessions/{id}/ticket → {ticket, url, expiresIn} — see protocol.md
  • GET /v1/sessions/{id}/participants — participation history

History & snapshots

  • GET /v1/projects/{id}/changes?sessionId&objectId&userId&before&limit (cursor = change id)
  • GET|POST /v1/projects/{id}/snapshots — create with Content-Type: application/x-skein-scene+gzip, metadata in the query (name, scenePath, sceneGuid, sessionId?, description?, unityVersion?)
  • GET|DELETE /v1/snapshots/{id} · GET /v1/snapshots/{id}/content · POST /v1/snapshots/{id}/restore {sessionId}

API keys

  • GET|POST /v1/projects/{id}/api-keys · DELETE /v1/projects/{id}/api-keys/{keyId}

Billing

  • GET /v1/billing/plans (public) · GET /v1/orgs/{id}/billing
  • POST /v1/orgs/{id}/billing/checkout {plan, interval} → {url} · POST /v1/orgs/{id}/billing/portal → {url}
  • POST /v1/billing/webhook (Stripe, signature-verified)

Operations

  • GET /healthz · GET /readyz · GET /metrics (bearer METRICS_TOKEN, or loopback only)

Example

curl -s https://app.skein.dev/api/v1/projects/$PROJECT/changes?limit=20 \
  -H "Authorization: Bearer $SKEIN_API_KEY" | jq '.changes[] | "\(.user.name) \(.kind) \(.objectName)"'