Skip to main content

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 keysposts: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

The type 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 type is 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. Treat test as a label for your own bookkeeping, not as a sandbox.
Side-effect suppression for test keys is planned and not yet shipped. Until it is, isolate by pointing CI at receivers you own and at a workspace you don’t mind writing to. Both flavours ship with the same scopes. There is no sandbox data set — your test key reads and writes the same workspace data your live key does. If you want isolation, mint a key in a dedicated test workspace.

1.4 Expiry and rotation

A key can have an optional expiresAt 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

We accept the 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.
The full secret is shown to you exactly once, immediately after creation. After that, only the prefix and last 4 characters are visible:

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.
  1. 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.
  2. Go to Settings → Developer → API keys.
  3. Click New API key.
  4. Step through the wizard:
    1. Name + mode — give the key a human label (e.g. “Zapier production”) and choose live or test. The label is visible to all org admins; it’s not a secret.
    2. Scopes — tick only the scopes the integration actually needs. You can change the scope set later in Edit.
    3. Advanced — optional expiry date. Leave blank for no expiry.
    4. Reveal — copy the full secret to your password manager or secret store. The dashboard never shows the secret again.
  5. All organisation admins receive a New API key created email 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

A 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.
Both inherit the revocation timing in §5: next request in practice, and a 5-minute cache TTL as the outer bound if the cache bust fails. You cannot edit the secret itself. To rotate the secret, mint a new key, deploy it, then revoke the old one (see §6). Every edit is recorded in the audit log under the actor who saved the change.

5. Revoking a key

From Settings → Developer → API keys → Revoke:
  1. The dashboard shows the last 7 days of usage so you can confirm the key you’re revoking is the right one.
  2. Type the key’s name to enable the Revoke button. This guard makes accidental revocation hard — there is no undo.
  3. Optional: leave a reason. It’s stored on the key row and surfaced in the email that goes to all org admins.
Revocation is immediate at the API edge: we bust the auth cache before returning, so in practice the next request after the mutation gets a 401. The hard worst case is 5 minutes — the auth-cache TTL, which only becomes the bound if that cache bust fails. Size your incident response against the 5 minutes, not the happy path.

6. Rotation playbook

When you need to rotate a key (suspected compromise, scheduled rotation, employee departure):
  1. Mint a new key with the same scopes and the same mode.
  2. 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.
  3. Verify with traffic — watch the new key’s “Last used” timestamp on the dashboard tick over.
  4. 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.
  5. (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:
  1. 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.
  2. Mint a replacement if the integration still needs to run.
  3. Audit. Review the key’s recent usage from the revoke modal’s activity chart and the workspace audit log.
  4. 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 the Scripe-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.