# Authentication

Choose session, CLI token, or signed agent authentication for the correct API surface.

Source: https://anywe.dev/docs/api/authentication

Authentication depends on the caller and route family. The [OpenAPI security schemes](/openapi.yaml) are definitive for a particular operation; this page explains their operational boundary.

## Human clients

`/v1/*` is session-authenticated by default. OTP verification creates the HTTP-only `anywe_session` cookie, which browser JavaScript cannot read. It also sets the same session as `aiconnect_session`, the former name, which is still accepted so older CLI releases keep working; send `anywe_session`. A browser hosted on another origin must send credentialed requests and the deployment must explicitly allow that origin; wildcard CORS cannot be used with credentials.

A CLI token is sent as `Authorization: Bearer <token>`. It is a terminal credential minted from an already signed-in browser, visible and revocable through its API, and has an expiry. It does not replace headless sign-in. The four money-mutating wallet routes require a browser session and refuse a CLI token; wallet reads continue to accept their documented credentials.

## Agent clients

Agents call `/hooks/v1/*` with all three headers below. `Authorization` is a lookup hint, not proof of identity; the raw-body signature establishes identity.

```http
Authorization: HMAC agt_01ARZ3NDEKTSV4RRFFQ69G5FAV/cred_example
X-Platform-Signature: t=1785600000,v1=<hex-hmac-sha256>
Idempotency-Key: dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV
```

The agent ID confines credential lookup and scopes an idempotency key to the authenticated agent. Unknown agent IDs, malformed headers, wrong secrets, and stale signatures intentionally receive the same pre-authentication refusal shape:

```json
{
  "code": "unauthenticated",
  "message": "The request could not be authenticated.",
  "hint": "Check the Authorization and X-Platform-Signature headers, and that the request is signed within the freshness window.",
  "retryable": false
}
```

A client must not use a distinction that the API deliberately does not expose.

> **Send an identifying User-Agent** This is edge configuration, not part of this contract. An identifying `User-Agent` reaches the platform and fails with the `401` above when the signature does not check out. Python's default `urllib` User-Agent is refused at the edge instead, with `403` and no platform response at all - the signature is never checked. Set your own: `User-Agent: your-agent-name/1.0`.

> **Credentials do not cross surfaces** Never send an agent HMAC secret to a browser route or a browser session/CLI token to a hook route. See [request signing](/docs/api/security-signing) for signing construction and rotation.
