> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://mediacontext.dev/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://mediacontext.dev/docs/_mcp/server.

# Authentication and errors

The API reference documents the typed v1 resources under `/api/v1`. Start with the **API resource guide** for discovery and evidence semantics. Run it locally from the repository root:

```sh
npm run server --workspace @openstack/dashboard
```

The development base is `http://127.0.0.1:8787`. Hosted application activation is held. Use a hosted base only after your operator confirms it is serving and grants access.

For programmatic reads, create a key in **Settings > API access** and set `MEDIACONTEXT_API_KEY` in your shell or secret store:

```sh
curl --fail-with-body \
  -H "X-Api-Key: $MEDIACONTEXT_API_KEY" \
  'http://127.0.0.1:8787/api/v1/publishers?limit=25&offset=0'
```

Keys use the `mck_` prefix and `read` or `export` scopes. An explicit `X-Api-Key` takes precedence over a Clerk bearer token and never falls back to it. Invalid or revoked keys fail. Key access also requires a current pro entitlement for the owning user. There is no per-key expiry field; revocation and entitlement expiry are separate checks.

Pro workspaces share a limit of **10,000 API requests per UTC calendar month**, across all keys and API-key REST and HTTP MCP traffic. The counter is stored in the database and reserved atomically, so concurrent requests and application processes cannot overspend the monthly allowance. It resets at 00:00 UTC on the first day of the next month.

Each authenticated, eligible API-key request reserves one `api_calls` unit before its handler runs. Later scope denials, workspace minute-limit rejections and handler errors still count. Invalid, revoked or ineligible keys and browser sessions do not count. Once the monthly limit is reached, additional requests return `429 api_quota_exceeded` without increasing usage. If accounting is unavailable, access fails with `503 usage_unavailable`.

`X-API-Quota-Limit`, `X-API-Quota-Remaining` and `X-API-Quota-Reset` describe the monthly allowance; reset is a Unix timestamp in seconds. A quota rejection includes `Retry-After` in seconds until reset and structured details containing `limit`, `used`, `remaining`, `periodStart` and `resetAt`. The period fields are UTC timestamps. This monthly allowance is separate from the 60-second workspace rate limit.

MCP is metered by authenticated HTTP request, not by tool name. Its initialize and tools-list requests each consume one unit. A tool call consumes one unit for its MCP request plus one for each underlying REST request. Each v1 tool makes one REST request, so a successful HTTP tool call consumes two units. The legacy `get_publisher` tool still makes three REST reads and can consume four units. Authentication and limits are checked again for those reads; a tool can fail after consuming part of its allowance. A key-backed stdio client consumes units for its REST requests without an outer HTTP MCP charge. HTTP MCP responses carry the lowest remaining allowance observed across their underlying reads and the latest applicable reset or retry time. If an underlying request is quota-limited, the tool reports an error even when the MCP envelope uses HTTP 200; inspect the tool error and `Retry-After`.

API-key requests share a limit of 100 requests per 60-second window per workspace, across all keys, client addresses and routes. The window begins with the first request. Excess requests return `429 workspace_rate_limited` and `Retry-After` in seconds. `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix seconds) describe that workspace window. Browser sessions do not consume it. The existing per-address/path limit also applies and can reject requests before authentication or usage accounting.

The workspace limiter runs in memory in the pilot's single active application process and resets on restart. Authentication and usage accounting happen before this check, so it bounds downstream API work rather than all database work. Fixed windows can admit bursts on either side of a reset. Before serving traffic through multiple application processes or replicas, move this counter to shared storage so the allowance cannot multiply.

Workspace changes require a Clerk member session with `Authorization: Bearer <session-token>`. The active Clerk organization must map to an accessible workspace. Keys cannot add or remove Shortlist members or mutate account lists. Never pass a workspace ID to try to override authentication scope.

V1 list resources are read-only. Existing list mutations remain on the unversioned API. To create a list with a session token stored as `MEDIACONTEXT_BEARER`:

```sh
curl --fail-with-body -X POST \
  -H "Authorization: Bearer $MEDIACONTEXT_BEARER" \
  -H 'Content-Type: application/json' \
  --data '{"name":"Research","domains":["example.com"]}' \
  http://127.0.0.1:8787/api/account-lists
```

A successful result has a `list` object with its `id`, `revision`, `domains`, `createdAt` and `updatedAt`. To update or delete it, send that current `revision`. A 409 means a concurrent change won; fetch the current list before retrying.

V1 collection responses contain `data`, `page` and `meta`. Offset pagination defaults to `limit=25` and `offset=0`, with a maximum limit of 100. `page.total` counts matching records before pagination; `page.hasMore` indicates another page. Signal events use a fixed page size of 50 and an opaque `page.nextCursor`.

Detail responses contain `data` and `meta`. Preserve source provenance, nullable timestamps and unknown evidence rather than converting them to zero or false. Dataset discovery identifies the current served publisher directory; it does not support historical selection. Legacy intelligence reads retain their separate market-snapshot selectors.

Errors currently use either a string (`{"error":"not found"}`) or a structured object:

```json
{"error":{"code":"insufficient_scope","message":"Requires 'export' scope","requestId":"request-id"}}
```

| Status    | Meaning                                                    | Action                                                                                     |
| --------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| 400       | Invalid JSON, domain, filter or rule                       | Correct the request.                                                                       |
| 401       | Missing credentials or `invalid_api_key`                   | Sign in or replace the revoked/invalid key.                                                |
| 403       | `insufficient_scope`, `pro_required` or membership denial  | Check scope, entitlement and organization access.                                          |
| 404       | Record absent in the accessible scope                      | Check the domain, list or snapshot.                                                        |
| 408       | MCP request body did not arrive within 10 seconds          | Send the complete body promptly before retrying.                                           |
| 409       | `revision_conflict`                                        | Reload and reapply your change.                                                            |
| 413 / 415 | MCP body exceeds 65,536 bytes or uses the wrong media type | Send a smaller JSON request with `Content-Type: application/json`.                         |
| 429       | `workspace_rate_limited` or `api_quota_exceeded`           | Respect `Retry-After`; a monthly quota rejection requires waiting for the UTC month reset. |
| 500       | Internal error                                             | Keep the request ID for support.                                                           |
| 503       | Required database, source or service unavailable           | Retry later; do not treat it as an empty result.                                           |

Inspect the HTTP status before parsing success data. Retrying a mutation after an ambiguous connection failure can repeat the operation; read the current resource before retrying. Keep secrets out of URLs and logs.

Operators can pause all API-key access by setting `MC_API_KEY_ACCESS_ENABLED=false` in the server runtime environment and restarting the application. Key requests, including the key-backed stdio MCP proxy, then return `503 api_access_disabled` before credential lookup or usage accounting. Browser/session authentication remains available. Restore access by removing the setting or setting it to `true` and restarting; existing keys are preserved. This switch does not disable session-authenticated clients.

The v1 built-in MCP implementation uses `POST /api/v1/mcp` with the same `X-Api-Key` and `read` scope. Manufact is selected for managed MCP hosting but is not activated; this route is an implementation reference, not the selected production endpoint. It exposes 13 read-only tools over stateless Streamable HTTP. See [MCP client setup](/docs/developers/mcp-client-setup) for the endpoint, client header requirements and local stdio alternative. The demo preview reports MCP as not configured; it is not a test of real API-key access.