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

# Authentication

> Authenticate API requests with an API key and understand access scopes, entitlements, and auth failures.

For the complete documentation index, see [llms.txt](https://docs.shodai.network/llms.txt).

API requests use the `X-API-Key` header as the canonical credential shape. The TypeScript SDK sends that header when you pass `apiKey` to `ApiClient`.

Hosted MCP uses the same API key as `Authorization: Bearer cns_pk_...` because remote MCP clients commonly support bearer-style auth. This is an API-key compatibility alias only. OAuth bearer tokens, JWT bearer tokens, and non-API-key bearer values are not supported.

For testnet access, you can self-onboard at [developers.shodai.network/portal](https://developers.shodai.network/portal) and create an API key. You can use that key in your own integration or try it immediately in the [Agreements API Playground](https://developers.shodai.network/api-playground).

Production API keys are provisioned by your API operator, who hands the plaintext key to the recipient once.

<Warning>
  Store the plaintext key when it is issued. The API stores only the hashed key afterward.
</Warning>

## Send the API key

For [Quickstart with TypeScript SDK](/sdks/quickstart-with-typescript-sdk), pass the key when constructing the client:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { ApiClient } from '@cns-labs/agreements-api-client';

const client = new ApiClient({
  environment: 'testnet',
  apiKey: process.env.API_KEY,
});
```

For [Quickstart with MCP](/sdks/quickstart-with-mcp), configure the hosted server with a bearer-style API key:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
Authorization: Bearer cns_pk_...
```

For raw HTTP debugging, send the same value as `X-API-Key`:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -sS "$BASE_URL/v0/agreements" \
  -H "X-API-Key: $API_KEY"
```

For clients that cannot set custom API-key headers, the bearer-style alias also works:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -sS "$BASE_URL/v0/agreements" \
  -H "Authorization: Bearer $API_KEY"
```

## Scopes and entitlements

API access is controlled by entitlements on the API principal. Testnet keys created through the Developer Portal are intended to include the default agreement read and write scopes. Manually provisioned principals should be checked for the scopes your integration needs.

| Scope              | Use                                                   |
| ------------------ | ----------------------------------------------------- |
| `agreements.read`  | Read agreement records, state, and input history.     |
| `agreements.write` | Validate, deploy, and submit agreement inputs.        |
| `agreements.*`     | Wildcard access for agreement scopes.                 |
| `*`                | Broad wildcard access. Use only for internal testing. |

Entitlement modes are:

| Mode             | Result                                                                                                                    |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `free_allowlist` | Allows the scoped operation.                                                                                              |
| `blocked`        | Returns `403 Forbidden` for the scoped operation.                                                                         |
| `paid_required`  | Returns `402 Payment Required`; per-call x402 settlement is not implemented. Treat this as an entitlement/operator issue. |

If the principal has no active entitlement for the requested scope, the API returns `403 Forbidden`.

## Common authentication failures

| Status | Meaning                                                                                                                                    | What to check                                                                                                                                      |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing, invalid, revoked, disabled, or unsupported API credential.                                                                        | Confirm `X-API-Key` is present with the current plaintext key, or use `Authorization: Bearer cns_pk_...` only as an API-key alias.                 |
| `402`  | The authenticated API principal has `paid_required` entitlement mode for the requested scope. Per-call x402 settlement is not implemented. | Treat this as an entitlement/operator issue and ask your API operator to review the requested scope.                                               |
| `403`  | The key is authenticated but not allowed for this scope or resource.                                                                       | Confirm the principal has `agreements.read`, `agreements.write`, `agreements.*`, or `*` as appropriate, and that no `blocked` entitlement applies. |

## Header casing

API auth header names are case-insensitive at the HTTP layer, but examples use `X-API-Key` consistently.

## Related pages

* [Quickstart with TypeScript SDK](/sdks/quickstart-with-typescript-sdk)
* [Quickstart with MCP](/sdks/quickstart-with-mcp)
* [TypeScript client reference](/sdks/typescript-client)
* [Errors and troubleshooting](/reference/errors-and-troubleshooting)
* Use the API Reference group in the sidebar for generated request and response details.
