> ## Documentation Index
> Fetch the complete documentation index at: https://apidocs.scripe.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Auth

# Scripe API — Authentication (v1)

> Status: **Public preview.** The Scripe API is rolling out to Advanced and
> Business plans. Behaviour and scopes documented here are stable for v1; new
> resources will land in additive minor releases (v1.1, v1.2…). Breaking
> changes will only ship in v2 with a deprecation window of at least 90 days.

This document covers everything you need to authenticate against the Scripe
public API: the key concept, the wire format, how to mint and rotate keys
from the dashboard, and how to recover from a compromise.

If you're integrating an MCP server, the same key works — see
[mcp.md](./mcp.md) for transport-specific notes (coming with Phase 4).

***

## TL;DR

```http theme={null}
GET /v1/notes
Host: api.scripe.io
Authorization: Bearer scripe_sk_live_8f3a…d401
```

* One key per integration. Treat it like a password.
* Live mode and test mode are both real keys against the real API; the
  difference is that test keys are flagged in audit logs and (from Phase 4
  onwards) won't trigger external side-effects (e.g. LinkedIn publishing).
* Keys are workspace-scoped and only act inside the workspace they were
  minted in. There is no cross-workspace token.
* Revocation is **immediate** (≤ 5 seconds in the worst case).

***

## 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 cohort), mint one key per workspace
in the workspace's own Developer settings.

This is the same boundary the dashboard uses — a request authenticated by an
API key sees the same data the bearer of that key would see if they signed
in interactively as the workspace.

### 1.2 Scopes

Scopes are **least-privilege** by default. A key has zero permissions until
you explicitly tick the resource scopes it needs. v1 ships with these
scopes; the catalog grows with each phase:

| Scope         | What it grants                            | Available in |
| ------------- | ----------------------------------------- | ------------ |
| `notes:read`  | Read notes the workspace owns             | v1.0         |
| `notes:write` | Create/update/delete notes                | v1.0         |
| `posts:read`  | Read post drafts and scheduled posts      | v1.0         |
| `posts:write` | Create post drafts, trigger post creation | v1.1         |

Granting a write scope does **not** automatically include the matching read
scope — most write endpoints don't need to read anything back. Tick both if
your integration needs both.

### 1.3 Live vs test keys

The `type` field on every key is either `live` or `test`. Both keys hit the
same `/v1` endpoints, but:

* **Live** keys count against your workspace's request-rate limits and may
  trigger external side effects (publishing to LinkedIn, sending emails to
  end-users, etc.).
* **Test** keys are tagged in audit logs and will not trigger external side
  effects from Phase 4 onwards. Use them for CI, staging, or local dev.

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 open requests with `401 Unauthorized` — 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

```http theme={null}
Authorization: Bearer <key>
```

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_sk_<live|test>_<24+ url-safe random chars>
```

* `scripe_` — vendor prefix. Lets secret-scanning tools (GitHub, GitGuardian)
  recognise leaked keys.
* `sk_` — "secret key". Reserved for future flavours like `pk_`
  (publishable, read-only, browser-safe).
* `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:

```
scripe_sk_live_…d401
```

### 2.3 Authentication response codes

| Code | Meaning                                                            |
| ---- | ------------------------------------------------------------------ |
| 401  | No key, malformed key, unknown key, revoked key, or expired key    |
| 403  | Valid key, but missing the required scope for the endpoint         |
| 429  | Valid key, but you've exceeded the rate limit for this workspace   |
| 5xx  | Scripe-side problem. Retry with backoff; we'll surface them in the |
|      | status page                                                        |

A 401 always means "fix your authentication". A 403 always means "ask the
dashboard for more scopes".

***

## 3. Minting a key from the dashboard

You need to be an **organisation admin** in Scripe and on a plan that
includes API access (Advanced or Business at the time of writing).

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. Tick **"I've saved this key in a safe place"** to close
      the modal. The dashboard never shows the secret again.
5. All organisation admins receive a `New API key created` email
   immediately after step 4. 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

Once you have the secret, the cheapest sanity check is an authenticated
read of your own workspace:

```bash theme={null}
curl -i https://api.scripe.io/v1/me \
  -H "Authorization: Bearer scripe_sk_live_…"
```

A `200 OK` with your workspace's id confirms the key works. A `401` means
the key was mistyped or revoked; a `403` means the key is fine but lacks
the required scope (in this case, none — `/v1/me` is unauthenticated for
scopes).

***

## 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 scopes takes
  effect within 5 seconds — we bust the auth cache immediately on save.
* The **expiry date**. Removing an expiry takes effect within 5 seconds.

You **cannot** edit the secret itself. To rotate the secret, mint a new
key, deploy it to the integration, 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 is here
   to make accidental revocation hard — there is no undo.
3. Optional: leave a reason. The reason is 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 Redis auth cache
before returning. The hard worst case is 5 seconds (Redis cache TTL), but
in practice the cache flush is synchronous and the next request after the
mutation 401s.

***

## 6. Rotation playbook

When you need to rotate a key (suspected compromise, scheduled rotation,
employee departure, etc.):

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. Any in-flight requests using it will start
   failing with 401 within seconds.
5. (If rotating because of a leak) **review the audit log** for the old
   key's `keyId` to confirm no unexpected actions happened during the
   exposure window.

For long-lived keys, schedule step 1 for every 90 days — the cheapest
defence against silent leaks is a key that simply expires.

***

## 7. Compromise / incident response

If you suspect a key has leaked:

1. **Revoke immediately.** A 5-second propagation delay is the worst case.
2. **Mint a replacement** in the same wizard if the integration still
   needs to run.
3. **Audit.** Pull the request log filtered by `keyId` from
   **Settings → Developer → API keys → \[key] → Activity** (Phase 6 ships
   the dedicated audit page; for now use the revoke modal's chart and the
   Sentry breadcrumb stream).
4. **Notify.** All org admins automatically get the revocation email; for
   security incidents, copy `security@scripe.io`.

For more detail (cache invalidation internals, hash chain anchoring, and
the ops side of "the key cache is unavailable"), see the operational
runbook at [packages/api-public/docs/runbook.md](../../../packages/api-public/docs/runbook.md).

***

## 8. FAQ

**Can I share a key across workspaces?**
No. Mint one key per workspace.

**Can I scope a key to one project inside a workspace?**
Not in v1. The roadmap has per-project scoping behind the
`api_v1_project_scoping` flag; it'll ship without breaking existing keys.

**Can I IP-allowlist a key?**
Not in v1. Per-key IP allowlists land with Phase 6.

**What happens to keys when a workspace downgrades to a plan without API
access?**
We invalidate every live key in the workspace's organisation
synchronously when the Stripe webhook fires. Subsequent requests 401. The
key rows are kept (not deleted) so they can be re-enabled if the
workspace upgrades again — but the secrets are still HMAC-only, so the
customer would need to mint new keys to actually use them.

**Can I see who minted or revoked a key?**
Yes. Every key row carries `createdByUserId` / `revokedByUserId`, which
the dashboard resolves to a name, and every email to org admins names the
actor.

**What's the rate limit?**
Workspace-level. Live keys count, test keys count, the dashboard does
not. Limits are documented in [rate-limits.md](./rate-limits.md) (lands
with Phase 2).
