# Authentication, errors and limits

> How to authenticate with the Praeko API (Bearer key), what each scope allows, the error format, the limits and versioning.

Source: https://praeko.app/docs/en/auth

Everything that applies to every route: how to authenticate, what each scope allows, the error format and how many requests fit in a minute.

## Authentication

Send your API key in the `Authorization` header with the `Bearer` prefix. The base URL is `https://praeko.app`; everything is JSON in UTF-8.

**Headers**

```text
Authorization: Bearer praeko_…
X-Praeko-Contract: en
```

| Item | Detail |
|---|---|
| Key format | Starts with `praeko_`. Older keys start with `arauto_` and still work. |
| `X-Praeko-Contract: en` | Selects the English contract on the paths that exist in both contracts (`/v1/posts`, `/v1/insights`, `/v1/youtube/videos`). Always send it. |
| Secret | Shown once, when you create the key. Praeko stores only the hash. |
| Panel cookie | For the panel only. Programs should use an API key. |
| AI agents (MCP) | The MCP server at `https://praeko.app/mcp` accepts the API key or an OAuth sign-in (the agent opens a consent screen and you choose the access). See [MCP server](https://praeko.app/docs/en/mcp). |

> **Keep the key out of your code** Use an environment variable or a secrets vault. If it leaks, revoke it under API keys and create another one.

## Scopes

Each key has one scope and each level includes the one before. The minimum scope of each route shows as a badge in the [reference](https://praeko.app/docs/en/api).

| Scope | Allows | Routes |
|---|---|---|
| (read) | Read channels, posts, calendar, metrics, comments, usage. | GET on most resources |
| (write) | Create, edit and cancel posts; upload media; reply to comments; slots; ideas. | POST, PUT, PATCH, DELETE on content |
| (admin) | Keys, webhooks, Instagram token, metrics import. | api-keys, webhooks, insights/import |

## Errors

The HTTP status is the status of the failure. The body always has the same envelope; `code` is stable, and `message` is human-readable text, currently in Portuguese.

**422**

```json
{
  "error": {
    "code": "time_in_past",
    "message": "tiktok: 2026-11-01T10:00:00Z já passou ou é daqui a menos de 30 s",
    "details": {}
  }
}
```

| Status | code | Means |
|---|---|---|
| 400 | `empty_body`, `invalid_json` | Empty body, or a body that is not a JSON object. |
| 401 | `unauthorized`, `invalid_api_key`, `revoked_api_key`, `expired_api_key` | No key, or a key that does not exist, was revoked or has expired. Includes `WWW-Authenticate: Bearer`. |
| 403 | `forbidden` | The key's scope is not enough. |
| 404 | `post_not_found`, `media_not_found`, `webhook_not_found`… | The resource does not exist in this workspace. |
| 409 | `channel_not_connected`, `no_slots`, `media_not_ready`… | The current state blocks the action. |
| 413 | `body_too_large` | The body is over 1 MB. |
| 422 | `invalid_field`, `invalid_request`, `time_in_past`… | Invalid field; `details.errors` lists all of them. Portuguese field names are rejected. |
| 429 | `rate_limited` | Per-minute limit. Includes `Retry-After`. |
| 502 | `youtube_error`, `instagram_error`, `tiktok_unavailable`, `buffer_error` | The network refused the request; its message comes in the body. |

The full list of contract codes, with status and meaning, is in [Errors and limits](https://praeko.app/docs/en/guides/errors-and-limits).

## Limits

| Limit | Value |
|---|---|
| Requests | 300 per minute per key (approximate, per instance). Over the limit: `429` with `Retry-After`. |
| JSON body | 1 MB |
| Media file | Up to 50 MiB in one request (`simple: true`); above that, in parts of at least 5 MiB |
| Webhooks | 10 per workspace; each delivery has a 10 s timeout |
| Usage log | 30 days, metadata only |

## Idempotency

Send the `Idempotency-Key` header (a UUID) on `POST`, `PATCH` and `DELETE`, and a retry after a network failure returns the first response, with `Idempotent-Replayed: true`, without creating anything new. The key lasts 24 hours, per workspace and per route. A key reused with a different request returns `422 idempotency_key_reuse`; a first request that is still running returns `409 idempotency_in_progress`. Without the header nothing changes. Details, curl, SDK and CLI in [Retry a request safely](https://praeko.app/docs/en/guides/idempotency).

**Retry without duplicating**

```bash
curl -X POST https://praeko.app/v1/posts \
  -H "Authorization: Bearer $PRAEKO_KEY" -H "X-Praeko-Contract: en" \
  -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" -d @post.json
```

## Versions and changelog

The version is in the path: `/v1`. The Portuguese paths still answer, marked with `Deprecation: true` and `Link: …; rel="successor-version"`. The date to switch them off has not been set. The [public changelog](https://praeko.app/docs/en/changelog) lists the changes, with the date of each one.

## Deprecated paths

These Portuguese paths still work and answer with `Deprecation: true` and `Link: <new>; rel="successor-version"`: the Portuguese `/v1/...` aliases (for example `/v1/canais`, instead of `/v1/channels`), `/saude` (use `/health`) and `/conexoes/:network/retorno` (use `/connections/:network/callback`). In the panel, the old addresses (`#/publicar`, `#/canais`, `#/conectar-agente`…) are rewritten to the new ones (`#/publish`, `#/channels`, `#/connect-agent`…) and `/entrar` redirects (301) to `/sign-in`. Move your integrations to the new paths. Before any of them is switched off, the notice appears here in the [changelog](https://praeko.app/docs/en/changelog), at least 90 days ahead; the date has not been set.
