> For the complete documentation index, see [llms.txt](/llms.txt).
> A full single-fetch corpus is available at [llms-full.txt](/llms-full.txt).
---
title: "Authentication"
description: "Authenticate requests to the AGNT5 API with a personal API key or a bearer token, and understand the error envelope and rate limits every endpoint shares."
last_verified: 2026-07-27
---


**Auth**: `X-API-KEY: <personal_api_key>` (required) or `Authorization: Bearer <jwt_or_opaque_token>` — exactly one is required on every request
**Error envelope**: most 4xx/5xx responses return `{"error": string, "message": string, "code": int, "hint"?: string}`; a missing or invalid credential returns a plain-text `401` instead
**Rate limits**: per-credential, tier-based (30-3,000 requests/minute); a `429` response includes a `Retry-After` header


Every request to the AGNT5 API must carry one of two credentials: a personal **API key** or a **bearer token**. This page covers both, the shape of error responses, and the rate limits applied per credential.

## Choose a credential

* **API key** (`X-API-KEY` header): a personal key issued in Studio. This is what the **`agnt5`** CLI and most integrations use.
* **Bearer token** (`Authorization: Bearer <token>` header): a session JWT issued when you sign in to Studio, or an OAuth 2.1 opaque token for MCP-based integrations.

```bash
curl https://api.agnt5.com/api/v1/projects \
  -H "X-API-KEY: <your_api_key>"
```

```bash
curl https://api.agnt5.com/api/v1/projects \
  -H "Authorization: Bearer <your_jwt>"
```

Send exactly one of the two headers. If neither is present, or the credential is invalid, the API responds `401 Unauthorized` with a **plain-text body** (for example `Invalid or missing authorization header`) — this is the one place in the API that doesn't use the JSON error envelope described below, because the failure happens before request routing.

## Error envelope

Once a request is authenticated, every error response — validation failures, not-found errors, database errors — uses the same JSON shape:

```json {{ title: 'Error response' }}
{
  "error": "Invalid query parameters",
  "message": "page_size must be between 1 and 100",
  "code": 400,
  "hint": "Pass page_size between 1 and 100"
}
```

`hint` is optional and only present when the API can suggest a concrete fix.

> **Note:** A few observability endpoints ([Runs & Traces](/docs/api-reference/runs-and-traces.md)) predate this shared envelope and return a narrower `{"error": string, "message": string}` shape with no `code` field. Each affected endpoint calls this out individually.

## Success envelope

Successful responses wrap their payload in a consistent envelope:

```json {{ title: 'List response' }}
{
  "success": true,
  "message": "Projects retrieved successfully",
  "data": [ /* ... */ ],
  "metadata": {
    "count": 42,
    "page": 1,
    "hasNext": true,
    "hasPrev": false,
    "unfilteredCount": 42,
    "hasAny": true
  }
}
```

Single-resource `GET` responses (`GET /api/v1/projects/:id`, for example) use the same `{success, message, data}` shape without `metadata`.

## Rate limits

Requests are rate-limited per credential and per plan tier, with separate per-minute and per-day budgets:

| Tier | Requests / minute | Requests / day |
|---|---|---|
| Hobby | 30 | 1,000 |
| Pro | 300 | 50,000 |
| Enterprise | 3,000 | Unlimited |

An API key is bucketed by a hash of the key value; a bearer token is bucketed by the signed-in user. Exceeding your limit returns:

```json {{ title: '429 Too Many Requests' }}
{
  "error": "rate_limited",
  "message": "rate limit exceeded for minute window",
  "window": "minute",
  "limit": 30
}
```

The response also carries a `Retry-After` header (seconds) telling you when to retry. If the rate limiter's backing store is temporarily unavailable, requests are allowed through rather than blocked — a store outage never blocks your traffic.

## Next steps

* [Projects](/docs/api-reference/projects.md): list and inspect the projects your credential can access
* [Deployments](/docs/api-reference/deployments.md): the resource most endpoints scope their queries to
* [Secrets](/docs/api-reference/secrets.md): store the credentials your deployed workflows need
* [CLI authentication](/cli/auth.md): how **`agnt5 login`** obtains and stores a credential locally
