Authentication — API keys
Status: Public preview. API access is an entitlement of the
workspace’s plan: Advanced and Business are the plans sold with it, and
Enterprise, the grandfathered Agency tier and admin-assigned Trial
workspaces carry it too. Everything else is rejected with
403 plan_not_eligible. Breaking changes only ship in a new dated API
version with at least 90 days of overlap — see
Conventions § Versioning.
This page covers API keys (scripe_sk_*): the wire format, the scope
model, how to mint and rotate keys from the dashboard, and how to recover
from a compromise. If you’re building a multi-tenant product that acts on
behalf of a Scripe user — or connecting an MCP host — you want
OAuth 2.1 instead; the MCP transport does not accept API
keys at all.
TL;DR
- One key per integration. Treat it like a password.
- Keys are workspace-scoped and act only inside the workspace they were minted in. There is no cross-workspace key.
- A key has zero permissions until you grant it scopes.
- Revocation takes effect on the next request — we bust the auth cache on save. If that invalidation fails, the cache TTL bounds the lag at 5 minutes.
1. Concepts
1.1 Workspace-scoped keys
Every API key belongs to exactly one Scripe workspace and authorises actions inside that workspace only. There is no organisation-wide super-key. If you have multiple workspaces (e.g. an agency), mint one key per workspace in the workspace’s own Developer settings — or use an OAuth token, which can reach every workspace its user belongs to.1.2 Scopes
Scopes are least-privilege by default: a key has zero permissions until you tick the scopes it needs. The full vocabulary is shared with OAuth —<resource>:<verb> strings, checked on
every request. A <resource>:write scope satisfies the matching
<resource>:read requirement, so you don’t need to tick both.
Scopes an API key can hold today:
The key wizard offers exactly this list. Two shipped REST endpoints
require a scope it cannot mint, so both need an
OAuth integration and answer
403 scope_missing to every
API key: registering a webhook endpoint takes webhooks:manage,
which stays grantable to OAuth tokens only for now, and
DELETE /v1/sources/{sourceId} takes sources:destroy, one of the
never-grantable *:destroy family below.
Rows marked “any valid token” in
the endpoint catalogue (workspaces,
projects, jobs, /health/auth) work with any key and need no scope.
posts:generate, calendar:write, community:read / community:write
and jobs:* gate MCP tools only —
no REST endpoint requires them (REST post generation rides
posts:write, the community profile-list tools have no REST route, and
the jobs family needs no scope) — so the wizard does
not offer them either.
When a scope appears in the wizard it behaves identically to its OAuth
twin.
Where an endpoint lists more than one scope, all of them are
required — see scope_missing. The
write → read implication above is the only implication a key carries;
tick every other scope your integration needs.
Never grantable to API keys — posts:publish, settings:write, and
the *:destroy family. Publishing to LinkedIn, settings writes, and
permanent deletion require a human-consented OAuth grant, and each of
those scopes must be requested by name: they are never implied by
write, never implied by the matching *:write, and consents granted
before they existed never gain them. Every call that is irreversible —
publishing, a destructive delete, or a settings write that would replace
text a person already wrote — additionally confirms per action; a
settings write that only fills an empty field lands directly. See
oauth.md §5 and
MCP tools §2 for the per-tool contract.
1.3 Live vs test keys
Thetype field on every key is either live or test. Both hit the
same /v1 endpoints, but:
- Live keys trigger external side effects (publishing to LinkedIn, sending emails, webhook deliveries).
- Test keys currently do too. The
typeis recorded on the key and every audit-log row references the key that made the call, but no side effect is suppressed today — a test key’s writes deliver webhooks to your real receiver. Treattestas a label for your own bookkeeping, not as a sandbox.
1.4 Expiry and rotation
A key can have an optionalexpiresAt date. After that day at 00:00 UTC
the key fails requests with 401 (key_expired) — the row stays in the
database for audit, it just stops authenticating.
Best practice: rotate any long-lived key every 90 days. The dashboard’s
New API key wizard lets you set an expiry up front, which is the
cheapest way to enforce rotation without writing custom code.
2. Wire format
2.1 Header
Bearer scheme only. We do not accept the API key in a
query parameter, a cookie, or a custom header — those routes leak
credentials into logs, browser history, and proxies.
2.2 Key format
scripe_— vendor prefix. Lets secret-scanning tools (GitHub, GitGuardian) recognise leaked keys.sk_— “secret key”.live_/test_— mode discriminator. Visible to humans; no security meaning.- The trailing 24+ characters are the actual entropy. We store an HMAC-SHA-256 of the full key, never the plaintext.
2.3 Authentication response codes
A 401 always means “fix your authentication”. A 403 needs the
error.code to tell you which of five things went wrong:
scope_missing (grant more scopes), plan_not_eligible (the plan does
not carry API access), admin_required, forbidden_project, or
workspace_mismatch. Branch on the code — the message is
human-facing prose, not part of the contract. For scope_missing, the
scope catalogue below tells you which scope the endpoint
needs.
3. Minting a key from the dashboard
You need to be an organisation admin in Scripe and on a plan that carries API access — Advanced, Business, Enterprise, the grandfathered Agency tier, or an admin-assigned Trial workspace.- Open the Scripe dashboard and switch to the workspace you want the key to act in. The key is scoped to this workspace; it cannot read or write other workspaces in the same organisation.
- Go to Settings → Developer → API keys.
- Click New API key.
- Step through the wizard:
- Name + mode — give the key a human label (e.g. “Zapier
production”) and choose
liveortest. The label is visible to all org admins; it’s not a secret. - Scopes — tick only the scopes the integration actually needs. You can change the scope set later in Edit.
- Advanced — optional expiry date. Leave blank for no expiry.
- Reveal — copy the full secret to your password manager or secret store. The dashboard never shows the secret again.
- Name + mode — give the key a human label (e.g. “Zapier
production”) and choose
- All organisation admins receive a
New API key createdemail immediately. This is a security signal — if you didn’t expect a co-admin to mint a key, click through and revoke it.
3.1 Verifying the key
200 OK with your workspace’s id confirms the key works. This
endpoint needs no scope, so it verifies the credential independently of
the scope set.
4. Editing scopes and expiry
From Settings → Developer → API keys → Edit you can change:- The display name.
- The scopes the key carries. Both adding and removing take effect on the next request — we bust the auth cache immediately on save.
- The expiry date. Removing an expiry takes effect the same way.
5. Revoking a key
From Settings → Developer → API keys → Revoke:- The dashboard shows the last 7 days of usage so you can confirm the key you’re revoking is the right one.
- Type the key’s name to enable the Revoke button. This guard makes accidental revocation hard — there is no undo.
- Optional: leave a reason. It’s stored on the key row and surfaced in the email that goes to all org admins.
6. Rotation playbook
When you need to rotate a key (suspected compromise, scheduled rotation, employee departure):- Mint a new key with the same scopes and the same mode.
- Deploy it to the integration. Most integrations support a rollover window — set the new key as primary and keep the old one in a fallback slot.
- Verify with traffic — watch the new key’s “Last used” timestamp on the dashboard tick over.
- Revoke the old key. In practice the next request using it 401s, but size the fallback-slot removal against the hard 5-minute auth-cache TTL — don’t drop the old key the instant you revoke it.
- (If rotating because of a leak) review the audit log for the old key to confirm no unexpected actions happened during the exposure window.
7. Compromise / incident response
If you suspect a key has leaked:- Revoke immediately. In practice the next request after the revocation gets a 401 — we bust the auth cache before returning. Size your response against the hard worst case of 5 minutes, the auth-cache TTL, which becomes the bound only if that bust fails.
- Mint a replacement if the integration still needs to run.
- Audit. Review the key’s recent usage from the revoke modal’s activity chart and the workspace audit log.
- Notify. All org admins automatically get the revocation email;
for security incidents, copy
[email protected].
8. FAQ
Can I share a key across workspaces? No. Mint one key per workspace, or use an OAuth token — those reach every workspace their user can reach (membership, or an agency they own pays for it) via theScripe-Workspace-Id header.
Can I scope a key to one project inside a workspace?
Not currently. Per-project scoping is on the roadmap and will ship
without breaking existing keys.
Can I IP-allowlist a key?
Not currently.
What happens to keys when a workspace downgrades to a plan without API
access?
Requests start failing with 403 plan_not_eligible as soon as the plan
change lands. The key rows are kept, so an upgrade restores them without
re-minting.
Can I see who minted or revoked a key?
Yes. Every key row records its creating and revoking actor, the
dashboard shows both, and every email to org admins names the actor.
What’s the rate limit?
Per-credential sliding windows — 120 reads/min, 30 writes/min, 10 job
submissions/min. See
Conventions § Rate limits.