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

# Authentication

> Secure your agent's HTTP endpoints and messaging interfaces with API keys, JWT, allowlists, or composite auth.

When using `agent.serve()`, you can protect HTTP endpoints with authentication. Set `agent.auth` to an auth provider and all requests to `/run` and webhook endpoints require valid credentials.

## API Key Authentication

```python theme={null}
from definable.agent.auth import APIKeyAuth

agent.auth = APIKeyAuth(keys={"sk-my-secret-key"})
agent.serve(port=8000)
```

Clients send the key via the `X-API-Key` header or the `Authorization: Bearer <key>` header:

```bash theme={null}
# X-API-Key header
curl -X POST http://localhost:8000/run \
  -H "X-API-Key: sk-my-secret-key" \
  -H "Content-Type: application/json" \
  -d '{"input": "Hello"}'

# Or Authorization header
curl -X POST http://localhost:8000/run \
  -H "Authorization: Bearer sk-my-secret-key" \
  -H "Content-Type: application/json" \
  -d '{"input": "Hello"}'
```

<ParamField path="keys" type="str | Set[str]" required>
  A single API key or a set of allowed keys.
</ParamField>

<ParamField path="header" type="str" default="X-API-Key">
  HTTP header name to read the key from. Falls back to the `Authorization` header if the primary header is empty.
</ParamField>

User ID is derived from the key hash: `apikey_<sha256[:12]>`.

## JWT Authentication

```python theme={null}
from definable.agent.auth import JWTAuth

agent.auth = JWTAuth(secret="your-secret-key")
agent.serve(port=8000)
```

Clients send a Bearer token in the `Authorization` header:

```bash theme={null}
curl -X POST http://localhost:8000/run \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"input": "Hello"}'
```

<ParamField path="secret" type="str" required>
  Secret key or public key for token validation.
</ParamField>

<ParamField path="algorithm" type="str" default="HS256">
  JWT algorithm.
</ParamField>

<ParamField path="audience" type="str">
  Expected audience claim.
</ParamField>

<ParamField path="issuer" type="str">
  Expected issuer claim.
</ParamField>

User ID is extracted from token claims, checking `sub`, then `user_id`, then `id`. Remaining claims are available in `AuthContext.metadata`.

<Note>
  Requires `pyjwt`: `pip install pyjwt` or `pip install 'definable[jwt]'`
</Note>

## Allowlist Authentication

For messaging interfaces (Telegram, Discord), use `AllowlistAuth` to restrict access by user ID:

```python theme={null}
from definable.agent.auth import AllowlistAuth

agent.auth = AllowlistAuth(
  user_ids={"user-123", "user-456"},
  platforms={"telegram"},
)
```

<ParamField path="user_ids" type="Set[str]" required>
  Set of allowed user IDs.
</ParamField>

<ParamField path="chat_ids" type="Set[str]">
  Optional set of allowed chat/group IDs.
</ParamField>

<ParamField path="platforms" type="Set[str]">
  Optional platform filter. When set, the provider only applies to requests from these platforms (e.g., `{"telegram", "discord"}`). Returns `None` for other platforms.
</ParamField>

AllowlistAuth only applies to `AuthRequest` instances (messages from interfaces). It returns `None` for raw HTTP requests, making it safe to combine with API key auth.

## Composite Authentication

Chain multiple providers with `CompositeAuth`. It tries each in order and returns the first successful result:

```python theme={null}
from definable.agent.auth import APIKeyAuth, AllowlistAuth, CompositeAuth

agent.auth = CompositeAuth(
  APIKeyAuth(keys={"sk-my-api-key"}),
  AllowlistAuth(user_ids={"user-123"}, platforms={"telegram"}),
)
```

This is the recommended pattern for agents that serve both HTTP endpoints and messaging interfaces — API keys protect `/run` while the allowlist controls messaging access.

<ParamField path="*providers" type="AuthProvider">
  One or more auth providers. At least one is required. Supports mixed sync/async providers.
</ParamField>

## Per-Webhook Auth Override

Individual webhooks can override the agent-level auth:

```python theme={null}
@agent.on(Webhook("/public-hook", auth=False))    # No auth required
async def public(event): ...

@agent.on(Webhook("/private-hook"))                # Uses agent.auth
async def private(event): ...
```

## AuthContext

When authentication succeeds, the provider returns an `AuthContext`:

| Field      | Type             | Description                            |
| ---------- | ---------------- | -------------------------------------- |
| `user_id`  | `str`            | Canonical user identifier              |
| `metadata` | `Dict[str, Any]` | Extra claims or info from the provider |

## Custom Auth Provider

Implement the `AuthProvider` protocol to create your own auth backend:

```python theme={null}
from definable.agent.auth import AuthProvider, AuthContext

class MyAuth:
  async def authenticate(self, request) -> AuthContext | None:
    token = request.headers.get("X-Custom-Token")
    if verify(token):
      return AuthContext(user_id="user-123", metadata={"role": "admin"})
    return None

agent.auth = MyAuth()
```

The `authenticate` method can be sync or async. Return `AuthContext` on success, `None` on failure (returns 401).

## Auth Bypass

* `/health` is always public
* Webhooks with `auth=False` bypass authentication
* In dev mode, `/docs`, `/redoc`, and `/openapi.json` are also public
