> ## 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.

# Workspaces

# Workspaces

The Scripe API is **workspace-scoped** end-to-end: every read returns
data only the active workspace owns. `/v1/workspaces/me` resolves the
active workspace plus the principal that's calling; `/v1/workspaces`
lists every workspace the caller can reach.

**API keys** authorise exactly one workspace — to act on a different
workspace, mint a key there. **OAuth tokens** can reach every workspace
the consenting user belongs to: the workspace pinned at consent is the
default, and a per-request `Scripe-Workspace-Id` header targets any
other (see [Multi-workspace access](#multi-workspace-access)).

***

## `GET /v1/workspaces/me`

Returns the authenticated workspace's metadata, the principal making
the request, and the feature flags currently enabled for the workspace's
plan.

```bash theme={null}
curl -i https://api.scripe.io/v1/workspaces/me \
  -H "Authorization: Bearer scripe_sk_live_…" \
  -H "Scripe-Api-Version: 2026-08-01"
```

### Response

```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json
Scripe-Api-Version: 2026-08-01
X-Request-Id: req_01J9Z…

{
  "workspace": {
    "id": "org_2pYJfL3VpQK4G2J7nE9b6Vw",
    "name": "Acme Corp",
    "plan": "ADVANCED",
    "features": {
      "apiAccess": true,
      "amplifiers": false,
      "engagementAutomation": false
    }
  },
  "principal": {
    "type": "api_key",
    "id": "key_01J9ZAB12CD34E56F7G8H9",
    "scopes": ["notes:read", "posts:read"]
  }
}
```

### Field reference

| Path                           | Type             | Notes                                                                 |
| ------------------------------ | ---------------- | --------------------------------------------------------------------- |
| `workspace.id`                 | string (`org_*`) | Clerk organisation id. Stable for the lifetime of the workspace.      |
| `workspace.name`               | string \| null   | Human-readable label set in the dashboard. Cached for 60 s.           |
| `workspace.plan`               | string           | One of `FREE`, `SOLO`, `ADVANCED`, `BUSINESS`, `ENTERPRISE`, `TRIAL`. |
| `workspace.features`           | object           | Boolean feature flags currently enabled by the plan.                  |
| `workspace.features.apiAccess` | boolean          | If `false`, the API would have already 401'd. Always `true` here.     |
| `principal.type`               | `"api_key"`      | Reserved for future OAuth-token (`"oauth"`) and impersonation flows.  |
| `principal.id`                 | string (`key_*`) | The canonical id of the API key in use.                               |
| `principal.scopes`             | string\[]        | The scopes the key carries (subset of the global catalogue).          |

The `features` object grows over time. Treat unknown keys as `false`
when introspecting; a `true` for a flag we haven't shipped yet means
your client predates the dashboard's option.

### Caching

The response is **not cacheable** — it depends on the request's principal
and reflects live plan state. Don't memoise it for longer than it takes
to render the next page.

If you need workspace metadata frequently (e.g. you're showing the plan
in your UI), cache the response in your own client for a minute and
invalidate on plan-change webhooks.

### Why not just decode the API key?

Two reasons:

1. **Plan changes propagate.** The plan in the response reflects the
   workspace's current Stripe state, not what was true when you minted
   the key. A workspace that downgrades will see `apiAccess: false` here
   before any other endpoint starts 403'ing.
2. **Forward-compat.** When OAuth tokens land, the same endpoint will
   return `principal.type: "oauth"` and surface the user delegating
   access. Clients that already use this endpoint Just Work.

### Errors

| Status | Code                                                             |
| ------ | ---------------------------------------------------------------- |
| 401    | `unauthenticated`, `invalid_token`, `key_revoked`, `key_expired` |
| 403    | `plan_not_eligible`                                              |
| 429    | `rate_limited`                                                   |
| 503    | `service_unavailable`                                            |

See [errors/](./errors/) for full descriptions.

***

## `GET /v1/workspaces`

Lists every workspace the caller can reach. For an **OAuth token** that
is every Clerk organisation the consenting user is currently a member of
(live membership); for an **API key** it is the single workspace the key
is bound to.

```bash theme={null}
curl -i https://api.scripe.io/v1/workspaces \
  -H "Authorization: Bearer scripe_oat_…" \
  -H "Scripe-Api-Version: 2026-08-01"
```

### Response

```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json

{
  "workspaces": [
    { "id": "org_2pYJfL3VpQK4G2J7nE9b6Vw", "name": "Acme Corp", "isDefault": true },
    { "id": "org_2qKd…",                    "name": "Client A",  "isDefault": false },
    { "id": "org_2rLe…",                    "name": "Client B",  "isDefault": false }
  ]
}
```

### Field reference

| Path                     | Type             | Notes                                                                 |
| ------------------------ | ---------------- | --------------------------------------------------------------------- |
| `workspaces[].id`        | string (`org_*`) | Clerk organisation id. Use as the `Scripe-Workspace-Id` header value. |
| `workspaces[].name`      | string           | Human-readable label. Cached for 60 s.                                |
| `workspaces[].isDefault` | boolean          | The workspace used when no `Scripe-Workspace-Id` header is sent.      |

***

## Multi-workspace access

A single OAuth token can operate against any workspace the consenting
user belongs to. Select the target per-request with the
`Scripe-Workspace-Id` header (value = the `org_*` id from
`GET /v1/workspaces`):

```bash theme={null}
# Read Client A's posts with the same token used for the default workspace
curl -i "https://api.scripe.io/v1/posts?projectId=proj_…" \
  -H "Authorization: Bearer scripe_oat_…" \
  -H "Scripe-Workspace-Id: org_2qKd…" \
  -H "Scripe-Api-Version: 2026-08-01"
```

* **Default.** Omit the header → the request runs against the
  consent-pinned (default) workspace. `/v1/workspaces/me` reflects the
  active workspace, so it changes with the header.
* **Live membership.** Access is validated against the user's current
  Clerk membership on every request (60 s cache). A header naming a
  workspace the user is **not** a member of returns `workspace_unavailable`.
* **Plan.** Gating inherits the **default** workspace's plan, so client
  workspaces on cheaper plans still work under an API-enabled token.
* **API keys are single-workspace.** A `Scripe-Workspace-Id` header that
  disagrees with the key's workspace is rejected with
  `workspace_unavailable`.

***

## What's NOT here (yet)

* **Switching workspaces with an API key.** Not supported — API keys are
  bound to one workspace. Use an OAuth token (with `Scripe-Workspace-Id`)
  or mint a key in the target workspace.
* **Updating workspace metadata** (renaming the workspace, changing the
  plan). Use the Scripe dashboard. Phase 3 may add a write
  `PATCH /v1/workspaces/me` for the subset of fields that make sense
  programmatically.
