Authentication
The public API authenticates with workspace API keys. The dashboard and mobile app use short-lived session tokens instead.
API keys#
Keys belong to a workspace, not a person — they keep working when the person who created them leaves the team.
Send the key either way; both are equivalent:
# Preferred
x-api-key: pk_live_1a2b3c4d…
# Also accepted
Authorization: Bearer pk_live_1a2b3c4d…Keys are shown once
We store only a SHA-256 hash, so a key cannot be recovered — not by you, not by us. If you lose it, revoke it and create another. That also means a database leak on our side cannot be replayed as working credentials.Live and test keys#
| pk_live_ | pk_test_ | |
|---|---|---|
| Consumes credits | Yes | No — always free |
| Watermark | Follows your plan | Always watermarked |
| Rate limit | Plan limit | Reduced |
| Rendering path | Real | Real — identical code |
| Counts toward key quota | Yes | No |
Test mode is not a mock. It runs the same pipeline, returns the same shapes and produces a real file — so an integration built against it behaves identically when you swap the key.
Scopes#
Each key carries a set of scopes. Give a key only what it needs: a service that renders should not be able to delete templates.
| Scope | Grants |
|---|---|
renders:read | List and retrieve renders, estimate cost |
renders:write | Create and cancel renders |
templates:read | List templates and read field schemas |
templates:write | Import a design as a new template |
assets:read | List media library assets and transcriptions |
assets:write | Upload and delete media, import from Drive, generate a voice-over, transcribe audio |
webhooks:read | List webhook endpoints |
webhooks:write | Register, edit, test and delete webhook endpoints |
integrations:read | List connections, browse and read connected accounts |
integrations:write | Create and modify data in connected accounts |
A request missing the required scope returns 403 with INSUFFICIENT_SCOPE.
templates:write is narrower than it sounds. It grants exactly one thing — POST /v1/templates/import, which creates a private draft — and no endpoint behind it can edit, publish or delete a template you already have. Editing stays in the dashboard.Restricting a key#
- IP allowlist — plain addresses or CIDR blocks. A call from anywhere else returns
403 IP_NOT_ALLOWED. Worth setting for a key used only by your own servers. - Expiry — set a date and the key stops working then. Useful for contractors and short-lived integrations.
- Revocation — immediate, from the dashboard. There is no grace period.
If a key leaks
Revoke it first, then rotate. A live key can spend your credits, so the order matters — creating the replacement before revoking the old one leaves a window where both work.Session authentication#
The dashboard and mobile app use JWT session tokens rather than API keys. You do not normally interact with these directly, but they are documented because a few endpoints — credits history, billing — accept only session auth.
# Phone (WhatsApp OTP) — the only sign-in method.
POST /api/auth/send-otp { "phone": "+919876543210" }
POST /api/auth/verify-otp { "phone": "+919876543210", "otp": "123456" }
# verify-otp returns: { token, refreshToken, user, organization, plan, credits }Access tokens last 12 hours; refresh tokens 60 days. Exchange an expired one at POST /api/auth/refresh — the dashboard does this automatically. Signing out revokes every token the account holds, on every device, immediately.
Authentication errors#
| Status | Code | Fix |
|---|---|---|
| 401 | NO_API_KEY | Send the x-api-key header |
| 401 | INVALID_API_KEY | Key unknown, revoked, inactive or expired — check which environment you are using |
| 403 | INSUFFICIENT_SCOPE | Add the scope to the key, or use a different key |
| 403 | IP_NOT_ALLOWED | Add the calling IP to the key’s allowlist |
| 403 | ORG_SUSPENDED | Contact support — the workspace is suspended |
| 401 | TOKEN_EXPIRED | Session auth: refresh the token |