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

# Authentication

> Two Bearer tokens that never substitute for each other, and how each is verified.

Every request carries a Bearer token. Which token depends on which route.

| Route prefix                          | Credential                                                      |
| ------------------------------------- | --------------------------------------------------------------- |
| `/v1/ingest`                          | An ingest key, `mp_live_…`                                      |
| `/v1/mcps`, `/v1/keys`, `/v1/account` | A Supabase access token                                         |
| `/mcp`                                | An OAuth 2.1 access token — see [MCP authentication](/mcp/auth) |
| `/health`                             | None                                                            |

The middleware is scoped to exactly the prefixes each route file owns, so ingest's auth is never shadowed by a broader wildcard.

## Ingest keys

```bash theme={null}
curl -X POST .../v1/ingest \
  -H "Authorization: Bearer mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

The key is hashed with SHA-256 and looked up against `api_keys`, which yields exactly one MCP id. **That is where the payload lands** — no value in the request body selects an MCP, so telemetry cannot be posted into someone else's account by any means.

Resolutions are cached in memory for five minutes so ingest never costs a database round trip per batch. Misses are cached for 30 seconds, so a misconfigured client cannot hammer the table. Revoking a key clears the cache immediately.

A revoked or unknown key returns `401`.

## Supabase access tokens

```bash theme={null}
curl .../v1/mcps \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

Verified against the project's published JWKS, and checked for **issuer, audience and algorithm** (`ES256`) as well as signature. A valid signature alone only proves the project minted the token, not that it was minted for this API.

Your account and role are derived **from the token**, never from the request. Read [Roles and permissions](/team/roles) for what each role may do.

### Your first request settles your account

Account resolution runs before every account-scoped route, not only on a write. If you have no membership yet:

* an invitation exists for your address → you claim it, joining that account with that role
* nobody knows you → a new account is created and you become its owner

This is why a fresh user who signs in and only reads gets real data rather than a wall of `404`s.

## Getting a token

There is no client-credentials flow and no personal access token. The dashboard obtains a token through Supabase magic-link sign-in and sends it on every request.

For automation, the [MCP endpoint](/mcp/auth) is the supported path — it is OAuth, so nothing is copied and nothing is leaked.

## Failures

| Status |                                                               |
| ------ | ------------------------------------------------------------- |
| `401`  | Missing, malformed, expired, invalid or revoked               |
| `403`  | Valid, but your role does not permit this                     |
| `404`  | The resource does not exist — or it exists in another account |

A `404` for another account's resource is deliberate. MCPulse never confirms that an id exists outside your account.

## Related

* [Authentication (product)](/authentication)
* [Errors](/api/concepts/errors)
