> For the complete documentation index, see [llms.txt](https://docs.shredpay.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.shredpay.xyz/developers/getting-started/authentication.md).

# Authentication

ShredPay uses two auth schemes depending on who is calling.

| Caller                  | Scheme                      | Header                    |
| ----------------------- | --------------------------- | ------------------------- |
| AI agent / your backend | **API Key**                 | `X-Api-Key: sk_live_…`    |
| Agent Console (browser) | **Privy JWT** (via Gateway) | `Authorization: Bearer …` |

This page covers API keys — the only scheme you need for programmatic access. JWT-authenticated routes are reserved for the Agent Console UI.

## API keys

API keys are issued from the [Agent Console](https://console.shredpay.xyz). Each key is bound to exactly one **sub-wallet** and carries:

* **Permissions** — `read` or `trade`.
* **Spend limits** — daily and monthly USD caps.
* **Allowed chains** — a whitelist of chain IDs the key can transact on.

```http
GET /api/wallet/address HTTP/1.1
Host: agent-api.shredpay.xyz
X-Api-Key: sk_live_e9f3...c104
```

### Key format

```
sk_live_<32-character-secret>     production
sk_test_<32-character-secret>     test environment
```

The full secret is shown **once** at creation time. Store it securely — ShredPay only keeps a hash on disk, so we cannot recover a lost key. Use the rotate endpoint if a key is compromised; the previous key keeps working for 24 hours to give you a grace window.

### Permissions

| Permission | What it allows                                                                                         |
| ---------- | ------------------------------------------------------------------------------------------------------ |
| `read`     | All `GET /api/*` endpoints; MCP read-only tools (`get_balance`, `get_positions`, …).                   |
| `trade`    | Everything in `read` **plus** write endpoints (`send_transaction`, `execute_swap`, `defi_deposit`, …). |

Issue separate keys for read-only dashboards and trading bots. It limits blast radius if one is leaked.

### Spend limits

Limits are enforced **per key**, not per sub-wallet. A request that would exceed either the daily or monthly USD cap is rejected with `403 LIMIT_EXCEEDED` before any signing happens.

Check current usage anytime:

```http
GET /api/limits
```

```json
{
  "daily_limit_usd": "1000",
  "daily_used_usd": "247.50",
  "monthly_limit_usd": "10000",
  "monthly_used_usd": "1820.00",
  "resets_at": "2026-04-26T00:00:00Z"
}
```

### Rotating a key

```http
POST /api/v1/agent/keys/{key_id}/rotate
Authorization: Bearer <console JWT>
```

The response contains a brand-new `sk_live_…` value. The previous key continues to authenticate for 24 hours, then is permanently revoked.

## Storing keys safely

* Treat API keys like passwords — never commit them, never log them, never paste them into chat tools.
* Inject via environment variable or a secret manager (AWS Secrets Manager, Doppler, 1Password).
* Use **separate keys per environment** so a leaked test key cannot move production funds.
* Rotate on a schedule (quarterly is a good default) and immediately on suspected compromise.

## Errors

| Code                    | Meaning                                                                |
| ----------------------- | ---------------------------------------------------------------------- |
| `401 UNAUTHENTICATED`   | Missing or malformed `X-Api-Key` header.                               |
| `401 INVALID_KEY`       | Key does not exist or has been revoked.                                |
| `403 PERMISSION_DENIED` | Key lacks the required permission (e.g. `read` key calling `tx/send`). |
| `403 CHAIN_NOT_ALLOWED` | Target `chain_id` is not in the key's `allowed_chains`.                |
| `403 LIMIT_EXCEEDED`    | Daily or monthly USD cap hit.                                          |

See [reference/error-codes.md](/developers/reference/error-codes.md) for the full list.
