# Authentication and idempotency

Credentials, scopes, Idempotency-Key and request IDs for every SciFig API call.

Source: https://scifig.ai/docs/api/authentication

## Bearer authentication

Send `Authorization: Bearer <credential>` on every request except `GET /pricing`, `GET /openapi.json` and `GET /openapi.yaml`. Credentials in query parameters or other headers are ignored.

- API keys start with `sk_live_`. Create them under Settings → Developer; the key is shown once. Keep it on a server and never ship it in a browser or mobile app. We email you 7 days before a key expires.
- OAuth access tokens start with `sf_at_`. MCP clients receive them after you approve the client; you can disconnect a client under Settings → Developer → Connected apps.
- A missing, invalid, expired or revoked credential returns `401 UNAUTHORIZED`. An account that cannot use the API (for example, one being deleted) returns `401 ACCOUNT_UNAVAILABLE`.
- Revoking a key stops new requests with it; jobs it already created keep running and stay readable with another credential of the same account.

## Scopes

| Scope | Grants | API key type |
| --- | --- | --- |
| figures:read | `GET /jobs`, `GET /jobs/{id}`, `GET /files/{file_id}`, `GET /credits` | Read-only key; may never expire |
| figures:generate | Everything in `figures:read` plus every `POST` endpoint | Generate key; expires after 30 days, 90 days or 1 year; needs an active subscription or a credit-pack balance above zero |

A request outside the credential’s scope returns `403 SCOPE_DENIED`. Use a separate key per application and environment so that you can revoke one without affecting the others.

## Idempotency

- Every create request (`POST /images/*`, `/charts/*`, `/flowcharts/*`) requires an `Idempotency-Key` header of 1–255 visible ASCII characters. A random UUID per request is the simplest choice. Uploads to `POST /files` may send one too (see [files](/docs/api/files#upload)).
- The same key with the same body returns the same job and never reserves credits again. For chart uploads the bytes of the data file are part of the body.
- The same key with a different body returns `409 IDEMPOTENCY_CONFLICT`. Concurrent requests with the same key never create two jobs: each returns the same job, or `409 IDEMPOTENCY_IN_PROGRESS` with `Retry-After: 1` while the first one is still being admitted.
- Retry with the same key after a timeout, a network error, a `429` or a `503`: it is the only way to be sure a job is not created twice.
- Once the job exists, a retryable error (`429`, `503`, `409 IDEMPOTENCY_IN_PROGRESS`) or `500 INTERNAL_ERROR` carries `details.job_id`. Retry with the same key or poll `GET /jobs/{job_id}`; do not switch to a new key, which would create and charge a second job. A job that was accepted but could not be read back is answered with `202` and status `queued`.
- A refused request (for example `402 INSUFFICIENT_CREDITS`) keeps that answer for its key. After topping up or upgrading, send it again with a new key.

## OAuth clients

- MCP clients use OAuth 2.1 with PKCE. Access tokens (`sf_at_…`) are valid for 1 hour. A client that asks for the `offline_access` scope also receives a refresh token (`sf_rt_…`) valid for 30 days, so it stays connected until you disconnect it; without `offline_access` you sign in again when the access token expires.
- Refresh tokens rotate: each refresh returns a new one. Presenting a refresh token that was already used is treated as reuse and revokes the tokens issued from it, so the client has to sign in again.
- Dynamic client registration accepts a body of at most 16 KB, with limits on the length of each field and the number of redirect URIs. Token endpoint authentication is `none` (public clients with PKCE) or `client_secret_basic`.
- A client registered but never authorized is deleted after 24 hours.
- An authorization request with `prompt=login` or `max_age` asks for a fresh sign-in. If you are already signed in, SciFig asks you to confirm before signing you out and back in.
- Rate limits for OAuth credentials count per client and per account, so other users of the same assistant do not use up your limits.

## Request IDs

Every response carries `X-Request-Id`; error bodies repeat it as `error.request_id`. It identifies the request in our logs without exposing anything else, so it is safe to log and to include when you contact support.
