# Authentication (https://docs2.smao.ai/docs/authentication)



The two REST APIs use independent key types — a Public API key never works
against Distributor API v1 and vice versa. Both are sent the same way:
`Authorization: Bearer <key>`. Newly issued Public API keys are \~43-character
base64url strings with no prefix; legacy `smao_`-prefixed Public API keys remain
valid.

## MCP Connector — OAuth or organization API key [#mcp-connector--oauth-or-organization-api-key]

For an interactive AI client, add `https://api2.smao.ai/mcp` as a remote MCP
connector and use OAuth. The client discovers SMAO's authorization server,
opens the browser login and consent flow, and binds the connection to the
organization you select. Disconnect and reconnect to switch organizations.

Clients that support custom HTTP headers can instead send a Public API
organization key as `Authorization: Bearer <key>`. Both methods apply the
current organization role and plan entitlements on every request. See the
[MCP setup and access rules](/docs/mcp) for client examples, scopes, and
revocation.

## Public API — organization API keys [#public-api--organization-api-keys]

Create keys yourself in the dashboard under **Settings → API Keys → New key**.
Each key gets a **Role** that scopes what it can do — pick the narrowest role
that covers your use case. Keys belong to one organization; every request is
scoped to that organization's data.

```bash
curl https://api2.smao.ai/api/v1/ping \
  -H "Authorization: Bearer YOUR_API_KEY"
```

A missing or invalid key returns `401`:

```json
{ "error": { "code": "unauthorized", "message": "Missing API key" } }
```

A valid key whose role lacks permission for the endpoint returns `403` with
`error.code: "forbidden"`. See [Errors](/docs/errors) for the full envelope.

<Callout type="warn">
  The raw key is shown once, at creation. Store it in a secret manager — it cannot be retrieved later.
</Callout>

Key handling:

* **One key per integration.** Separate keys let you revoke one consumer without breaking the others.
* **Rotate by overlap.** Create a new key, deploy it, then delete the old one. Both work in parallel in between, so rotation needs no downtime.
* **Revoke immediately on leak.** Deleting a key invalidates it at once.

## Distributor API v1 — distributor keys [#distributor-api-v1--distributor-keys]

Distributor keys are issued by SMAO during onboarding — there is no self-service
issuance. The raw key is shown exactly once at creation; the server stores a
SHA-256 hash plus the first 12 characters as an identification prefix — never
the full key.

Every v1 key carries these fixed permissions:

* `organizations:read`
* `organizations:write`
* `usage:read`
* `partners:read`
* `partners:write`

Permission sets are immutable after issuance. Keys expire, by default after
**365 days**. Request a replacement key from SMAO before expiry and switch over
— old and new keys work in parallel until the old one expires or is revoked, so
rotation needs no downtime.

```bash
curl https://api2.smao.ai/api/distributor/v1/partners \
  -H "Authorization: Bearer YOUR_DISTRIBUTOR_KEY"
```

Auth failures return `401` in the Distributor error envelope, with an
`errorCode` telling you why:

```json
{ "status": "error", "errorCode": "INVALID_API_KEY", "message": "Invalid or revoked API key." }
```

| `errorCode`               | Meaning                                                     |
| ------------------------- | ----------------------------------------------------------- |
| `MISSING_API_KEY`         | No `Authorization: Bearer` header, or an empty key.         |
| `INVALID_API_KEY`         | Key unknown or revoked.                                     |
| `API_KEY_EXPIRED`         | Key past its expiry date — request a replacement from SMAO. |
| `API_VERSION_NOT_ALLOWED` | Key metadata does not authorize Distributor API v1.         |

Only `GET /health` is public. Every other Distributor endpoint requires a valid
v1 key and its documented permission. Continue with the
[Distributor v1 quickstart](/docs/distributor-v1/quickstart).

## REST API comparison [#rest-api-comparison]

|             | Public API                                                            | Distributor API v1                                                          |
| ----------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Issuance    | Self-service: dashboard **Settings → API Keys**                       | By SMAO during onboarding                                                   |
| Scoping     | One organization                                                      | All organizations managed by the distributor                                |
| Permissions | Role-based, chosen per key                                            | Five fixed v1 scopes                                                        |
| Expiry      | None                                                                  | 365 days by default                                                         |
| Rotation    | Create new key, deploy, delete old                                    | Request replacement from SMAO; parallel until old key expires or is revoked |
| Rate limits | Per key defaults: read 1000/min, write 200/min, `POST /calls` 60/hour | Per key: 60 req/min default (configurable per key)                          |
| 401 shape   | `{ "error": { "code": "unauthorized", ... } }`                        | `{ "status": "error", "errorCode": "...", "message": "..." }`               |

For the Public API, matched operations that pass authentication and the feature
gate return `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and
`X-RateLimit-Reset` after the limiter succeeds. Auth failures, feature-gate
failures, unmatched routes, and limiter-storage failures do not carry the full
header set. Details in [Errors](/docs/errors).
