Key format
Every key is prefixed with environment and scoped to a single project:ndsk_test_ key in your
production env is a clear bug. Our SDKs and dashboards both surface the prefix
so it’s hard to confuse the two.
Where to get a key
1
Open Settings → API keys
From the dashboard at app.neuraldraft.io.
2
Click Create key
Pick a memorable name (e.g.
vercel-prod, lovable-preview,
claude-code-laptop). Future-you reading the audit log will thank present-you.3
Choose the scopes
Default is read+write across CMS, blog and social. Tighten to least
privilege when the key has a known job — for example, a static-site build
only needs
content:read and blog:read.4
Copy it once
Keys are shown once. Paste straight into your secret manager. We store
only a hash — we cannot recover a lost key.
Sending the key
A standard Bearer token in theAuthorization header. No query-param fallback,
no body field. HTTPS is required.
Scopes
Scopes are wildcard-prefixed. A key withcontent:* can do every action under
content; * is full access (use it carefully — only for trusted backend
services).
A
403 Forbidden with code: "forbidden" is returned when the key lacks the
required scope; the response includes the scope you’d need.
Common errors
Full error catalog: errors.
Best practices
One key per environment
One key per environment
Mint separate keys for production, staging, preview, and each developer
laptop. The audit log shows last-used timestamps per key — anomalies become
obvious.
Rotate quarterly
Rotate quarterly
Mint a new key, deploy with both old and new accepted (we accept multiple
valid keys per project), then revoke the old one once nothing 401s. The
dashboard has a one-click rotation flow that automates the overlap window.
Never commit a key
Never commit a key
Use environment variables. On Vercel/Netlify/Render, the encrypted env UI
is the right place. For local dev, a
.env file in .gitignore and
dotenv-style loading. Our CLI refuses to write a key to disk outside of
a recognised secret manager.Use least-privilege scopes
Use least-privilege scopes
A static-site builder needs
content:read and blog:read, nothing more.
A scheduler-only service needs social:write and social:publish. Keys
leak; small blast radius is the only mitigation that works at scale.Treat `ndsk_test_` as throwaway
Treat `ndsk_test_` as throwaway
Test keys are fine to share with contractors, paste into Lovable previews,
or include in a public GitHub issue when reproducing a bug. They have a
separate credit pool that resets monthly and never bills.
Watch the audit log
Watch the audit log
Every key has a
last_used_at and a per-IP usage stripe in the dashboard.
Suspicious activity = a chance to rotate before damage compounds. Suspect
leakage? Revoke immediately; it takes effect within 60 seconds globally.Revoking a key
A revoked key starts returning401 unauthorized within ~60 seconds globally.
Active jobs queued against the key continue to completion (they were
authorised at submit time); new requests fail.
Multiple keys per project
A project can hold up to 20 active keys. Use this for environment separation and friction-free rotation:
A 21st create attempt returns
409 Conflict with code: "key_limit_reached" —
revoke an unused one first.
Central login (multi-workspace accounts)
If a single email is registered against more than one project (common for agencies and people running multiple sandboxes), the central login flow atapp.neuraldraft.io needs to know which
workspace to drop the user into. Two endpoints power this — both live on
the central host (app.<central_domain>), neither is a per-tenant API
surface, and neither uses an API key: they’re for the dashboard /
admin SPA, not for server-to-server traffic.
The v1 Project API documented above is unchanged — these endpoints are a
separate auth path the dashboard uses to issue Sanctum tokens. Skip this
section if you’re only consuming
/v1/*.GET /central/api/tenants-for-email
Returns every workspace the email is registered against. Always 200; the
response is constant-time across known/unknown emails to defeat
enumeration. An unknown email returns { tenants: [] }.
tenants.length > 1, render a workspace picker; otherwise log straight in.
POST /central/api/login
Email + password authentication that issues a Sanctum token. The optional
tenant_id disambiguates when the email is registered to multiple
workspaces — pass it whenever the picker has a selection so iteration
across candidates can’t pick the wrong one (e.g. password rotated on one
tenant but a stale row still points at another).
tenant_id is optional — if you omit it, the server iterates each candidate
workspace and uses the first one whose password verifies. Iteration is
case-insensitive on email. A wrong-password attempt always returns
422 Unprocessable Entity with auth.failed, regardless of whether the
email exists.
Where Sanctum and OAuth fit in
The v1 Project API documented here is API-key only. The separate Admin API — used by white-label dashboards and agencies managing multiple projects — uses scoped Sanctum tokens (tenants:read,
posts:write, …) and is documented in its own reference. If you’re building
your own admin UI on top of Neural Draft, that’s the auth model you want; for
everything else, stay here.