--- title: "Authentication" description: "Two kinds of bearer key open the API, and an agent token opens the memory routes for one person. Each route accepts the credentials it names." canonical: https://past.dev/docs/memory-api/authentication last-updated: 2026-10-09 --- # Authentication > Two kinds of bearer key open the API, and an agent token opens the memory routes for one person. Each route accepts the credentials it names. Product: past.dev Memory API. Source: https://past.dev/docs/memory-api/authentication ```header Authorization: Bearer $PAST_API_KEY ``` | Credential | Opens | | --- | --- | | **Project key** (past_sk_) | Ingest, completion, recall, data points, audiences and identities. The key selects one project, so `projectId` is never sent in a request body. Your application holds this key. | | **Management key** (past_mk_) | Projects and project keys, and the identity routes with `?project=`. It reads no memory. One exists per organization. | | **Agent token** (short-lived access token) | Ingest, completion, recall, identities, usage and the debugging reads, for the person who approved the agent. The call names its project in the `X-Past-Project` header. See Agent tokens below. | The webhook route takes none of the three: the token in a webhook URL is its credential. [Webhooks](/docs/memory-api/webhooks) describes it. A `401` names the check that refused the key. No `Authorization` header, or `Bearer` with no value, returns `401 missing-api-key`. A header that does not read `Bearer `, or a bearer that is not a past key, returns `401 malformed-api-key`, and `debugMessage` says which. A well-formed key that is unknown, expired or revoked returns `401 unauthorized`, one answer for all three. A key of the kind a route does not accept returns `401 unauthorized` too, and `debugMessage` names the kind. On the identity routes, a management key without `?project=` returns `400 project-required`, and a project key with a `?project=` that names another project returns `400 project-mismatch`. ### Where keys are created Project keys are created on the API keys screen of the console at https://app.past.dev. A key belongs to the project shown in the header when it is created, and it never changes project. The console can reveal a key again. The API returns a key value once, in the response of `POST /api/v1/projects/{slug}/keys`. The management key is created under Settings, Organization, Management key, by an Owner or Admin, and rotated there; rotation replaces its value at once. For a fresh organization: sign in, keep `default-project` or create a project, create a project key, and start with the [Quickstart](/docs/memory-api/quickstart). Create the management key only when a script must create projects or keys. Key values and credentials are never accepted as fields in request bodies. Call the API from your server. Browser requests are refused by the CORS policy, whatever their origin, and a key in a browser is exposed to its users. A key for an archived project returns `403 project-archived` until the project is unarchived in the console. ### Agent tokens An AI agent can get its own credential for one person, so nobody pastes a key into the agent. The person approves the agent once in a browser. The agent then holds a short-lived access token that the memory routes accept. `https://api.past.dev/auth.md` gives the procedure as commands that an agent can run. 1. Call a `/api/v1` route with no credential. The `401` response carries a `WWW-Authenticate` header that names the resource metadata document. 2. Read that document. It names the authorization server and the two scopes, `memory:read` and `memory:write`. 3. Read `https://api.past.dev/auth.md` and register at the authorization server with the email address of the person. 4. Give the person the verification link. The person signs in, reads a code on the page, and gives the code to the agent. 5. Exchange the stored identity assertion for an access token. 6. Send the access token as the bearer on each call. Exchange the assertion again when the token expires. ```401unauthorized WWW-Authenticate: Bearer resource_metadata="https://app.past.dev/.well-known/oauth-protected-resource/api/v1" ``` ```resourcemetadata { "resource": "https://app.past.dev/api/v1", "resource_name": "past memory API", "authorization_servers": ["https://sso.past.dev"], "scopes_supported": ["memory:read", "memory:write"], "bearer_methods_supported": ["header"] } ``` | Scope | Routes | | --- | --- | | `memory:read` | `POST /api/v1/recall`, `GET /api/v1/ingest/{ingestionId}`, `POST /api/v1/ingest/estimate`, `GET /api/v1/identities`, `GET /api/v1/identities/{identity}`, `GET /api/v1/usage` and the debugging reads: explain, requests and trace. | | `memory:write` | `POST /api/v1/ingest`, `POST /api/v1/ingest/batch`, `POST /api/v1/ingest/{ingestionId}/retry`, `DELETE /api/v1/ingest/{ingestionId}`, and `PUT`, `PATCH`, `DELETE` and bulk on `/api/v1/identities`. | An agent token names a person and not a project. Send `X-Past-Project: ` to select the project. Without the header, the only live project of the organization is used. An organization with two or more live projects returns `400 project-required`. A slug that names no live project returns `404 project-not-found`, and an archived project returns `403 project-archived`. ```curl curl -X POST https://api.past.dev/api/v1/recall \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "X-Past-Project: default-project" \ -H "Content-Type: application/json" \ -d '{ "query": "What is the Acme pilot budget?", "identity": "dana@example.com" }' ``` A token that is invalid or expired returns `401 unauthorized`. A token without the scope of the route returns `403 insufficient-scope`. The audience routes, the deletions by source id and the management routes accept keys only: an agent token returns `401 malformed-api-key` there. A call with an agent token spends the same credits as a call with a project key, and the Requests screen of the console lists it. The managed service issues agent tokens.