- Authenticated sessions — start from a Clerk JWT and exchange it for an opaque session token tied to your Clerk user id.
- Anonymous sessions — start from a stable device id (no sign-in required) and receive a session token tied to an anonymous principal. When the user later signs in, a single rebind call re-owns every anonymous artifact to the authenticated account.
Step 1 — Get a Clerk JWT
Obtain a short-lived Clerk JWT using the Clerk frontend SDK appropriate for your platform. Refer to the Clerk documentation for platform-specific instructions. You’ll use this JWT exactly once: to mint a session token.Step 2 — Exchange for a session token
CallPOST /auth/session with your Clerk JWT in the Authorization header. The server verifies the JWT, creates a session, and returns an opaque session token.
string
required
Your Clerk JWT. Format:
Bearer <clerk_jwt>.200 response
string
required
Opaque session token. Treat this like a password — store it securely and never log it.
number
required
Seconds until the token expires. Always
1800 (30 minutes).Step 3 — Attach the session token to requests
Include the session token as a Bearer token on every protected endpoint:Refreshing session tokens
Session tokens expire after 30 minutes. To refresh, callPOST /auth/session again with a fresh Clerk JWT before the current token expires. You can do this proactively — for example, refresh 5 minutes before expiry rather than waiting for a 401 Token expired response.
Logging out
Revoking a token immediately invalidates it server-side. Any in-flight requests using the revoked token will fail with401.
200 response
Bootstrap — hydrate your app on launch
After authenticating, callGET /auth/bootstrap as your first API request. It returns your profile, subscription details, and integration secrets in a single round trip. If this is your first sign-in, the server auto-creates your profile.
200 response
object
required
object
required
Current subscription state. Mirrors the credit fields in
profile for convenience.object
required
Start an anonymous session
CallPOST /auth/anonymous with a stable device_id to mint a session token without any sign-in. Use this on first launch to let users try the product before creating an account. The returned token behaves like any other session token — attach it with Authorization: Bearer <token> on subsequent requests.
This endpoint is unauthenticated (no Authorization header required) and idempotent on device_id: calling it again for the same device returns a session bound to the same anonymous principal.
string
required
Stable per-device identifier (1–200 chars). On iOS, use
identifierForVendor or a Keychain-persisted UUID so the same device keeps the same anonymous principal across launches.200 response
string
required
Opaque session token for the anonymous principal. Use it the same way as an authenticated session token.
number
required
Seconds until the token expires. Always
1800 (30 minutes).Anonymous writes are tagged internally with a sentinel
user_id of the form anon:<uuid>. You should not rely on that string shape — treat the session token as opaque. Once the user signs in and you call POST /auth/rebind, every anonymous artifact is re-owned by the Clerk user in a single transaction.Rebind an anonymous session to a signed-in user
After a user signs in with Clerk, callPOST /auth/rebind with the original device_id and the authenticated session token (or Clerk JWT) in the Authorization header. Every row written under the anonymous principal is re-owned by the authenticated user in a single database transaction, and a durable PostHog alias call is enqueued so conversion attribution survives transient analytics failures.
string
required
The same
device_id used when calling POST /auth/anonymous. Must be 1–200 chars.200 response
boolean
required
true when this call transferred artifacts to the authenticated user. false when the call was a no-op (for example, the device had no anonymous principal, or it was already rebound to the same user).number
required
Total number of rows re-owned across all user-scoped tables.
0 when rebound is false.string | null
required
The anonymous principal UUID that was rebound, or
null when there was nothing to rebind.