# Erros e limites

> Envelope de erro da API do Praeko, os 116 códigos do contrato com status e significado, e os limites de requisição, corpo, mídia e webhooks.

Fonte: https://praeko.app/docs/guides/errors-and-limits

O status HTTP é o da falha. O corpo segue sempre o mesmo envelope; `code` é estável (inglês, snake_case) e `message` é texto em português.

**422**

```json
{
  "error": {
    "code": "time_in_past",
    "message": "O horário já passou ou está perto demais.",
    "details": {}
  }
}
```

- `401` traz `WWW-Authenticate: Bearer`; `429` traz `Retry-After` (segundos).
- Em `422 invalid_field` e `invalid_request`, `details.errors` lista todos os problemas de uma vez.
- Campos em português são recusados: mande os nomes do contrato (em inglês) e o cabeçalho `X-Praeko-Contract: en`.

## Limites

| Limite | Valor |
|---|---|
| Requisições | 300 por minuto por chave (aproximado, por instância). Passou: `429 rate_limited` com `Retry-After`. |
| Corpo JSON | 1 MB (`413 body_too_large`) |
| Arquivo de mídia | Até 50 MiB de uma vez (`simple: true`); acima, em partes de 5 a 95 MiB. Vídeo até 5 GiB, capa e foto até 20 MiB. |
| Webhooks | 10 por workspace; entrega com 10 s de limite |
| Log de uso | 30 dias, só metadados (rota, status, duração; nunca o corpo) |

## Os códigos (116)

O status e o texto vêm das rotas documentadas e, quando a rota não os traz, do comportamento do servidor. Ficam de fora só os códigos das rotas internas de operação da plataforma. Um traço (—) no status quer dizer que o servidor atual não devolve o código.

| Status | `code` | Significa |
|---|---|---|
| 404 | `route_not_found` | Não existe rota com esse método e caminho. |
| 405 | `method_not_allowed` | O caminho existe, mas não aceita esse método. |
| 500 | `internal_error` | Erro inesperado no servidor. A `message` é genérica; tente de novo. |
| 401 | `unauthorized` | Faltou o cabeçalho `Authorization: Bearer …`. |
| 401 | `invalid_api_key` | A chave não existe. |
| 401 | `revoked_api_key` | A chave foi revogada. |
| 401 | `expired_api_key` | A chave expirou. |
| 401 | `mfa_required` | Confirme o segundo fator para continuar. |
| 401 | `invalid_code` | Código incorreto. |
| 409 | `mfa_already_enabled` | O app autenticador já está ativo: desative antes de configurar de novo. |
| 409 | `mfa_not_started` | Comece pela configuração do app autenticador. |
| 409 | `mfa_not_enabled` | Ative o app autenticador antes. |
| 409 | `current_session` | Esta é a sessão que você está usando: use Sair. |
| 404 | `session_not_found` | Sessão não encontrada. |
| 422 | `invalid_confirmation` | Faltou a confirmação que a ação destrutiva pede no corpo. |
| 401 | `invalid_session` | A sessão do painel venceu ou não vale mais: entre de novo. |
| 403 | `invalid_origin` | Pedido com sessão do painel vindo de outra origem: recusado. |
| 403 | `forbidden` | O escopo da chave não basta para esta rota (veja o selo de escopo). |
| 429 | `rate_limited` | Passou do limite de requisições por minuto. |
| 400 | `empty_body` | O corpo veio vazio. |
| 400 | `invalid_json` | O corpo não é um objeto JSON. |
| 413 | `body_too_large` | O corpo passa de 1 MB. |
| 422 | `invalid_field` | Um campo do corpo, da query ou de um cabeçalho está fora do formato ou do limite. A `message` diz qual. |
| 422 | `invalid_request` | Faltou algo; a lista completa vem em `details.errors`. |
| 422 | `missing_schedule` | Faltou `scheduledFor`, `queue` ou `draft`. |
| 422 | `missing_time` | Faltou `scheduledFor`. |
| 422 | `time_in_past` | O horário já passou ou está perto demais. |
| 409 | `no_slots` | `queue` num canal sem horários. |
| 409 | `no_free_slot` | Não há horário livre no canal nos próximos 120 dias. |
| 404 | `post_not_found` | O post não existe. |
| 404 | `item_not_found` | O item não existe neste post. |
| 404 | `action_not_found` | Este item não tem comentário guardado. |
| 404 | `channel_not_found` | O canal não existe neste workspace ou a rede não está conectada. |
| 409 | `channel_not_connected` | A rede não está conectada. |
| 404 | `api_key_not_found` | A chave não existe. |
| 403 | `oauth_requires_panel` | Conectar uma conta é feito no painel (Canais › Conectar), não com chave de API. |
| 409 | `video_not_private` | YouTube já enviado: o vídeo já está público no YouTube, então o horário e os dados só mudam pelo Studio (o `details.studio` traz o link). |
| 409 | `channel_in_use` | Há itens pendentes nesta conta: cancele ou mova antes de removê-la. |
| 409 | `channel_connected` | Desconecte a conta antes de removê-la da lista. |
| 409 | `channel_is_buffer` | É o canal do Instagram que publica pelo Buffer: ele não sai da lista. |
| 401 | `invalid_credentials` | E-mail ou senha incorretos. |
| 422 | `weak_password` | A senha não passa nas regras: de 12 a 128 caracteres e nada previsível (como o e-mail ou caracteres repetidos). |
| 400 | `invalid_link` | Link inválido ou vencido: peça um link novo. |
| 429 | `account_locked` | Muitas tentativas erradas: a conta fica travada por alguns minutos. |
| 401 | `invalid_passkey` | A chave de acesso não passou na verificação. |
| 404 | `passkey_not_found` | Chave de acesso não encontrada. |
| 409 | `passkey_exists` | Esta chave de acesso já está cadastrada. |
| 400 | `invalid_challenge` | Desafio inválido ou vencido: comece de novo. |
| 409 | `passkey_limit` | O limite é de 20 chaves de acesso por conta. |
| 401 | `code_required` | Informe o código do app autenticador (`code`). |
| 409 | `api_key_in_use` | É a chave que está fazendo esta chamada. |
| 401 | `invalid_token` | Token OAuth inválido, vencido ou emitido para outro servidor (no `/mcp`). |
| 404 | `request_not_found` | O pedido de autorização do agente não existe ou venceu: comece de novo pelo app. |
| 403 | `request_from_another_browser` | O pedido de autorização começou em outro navegador: comece a conexão de novo pelo app. |
| 404 | `connection_not_found` | Conexão de agente não encontrada. |
| 409 | `video_not_found` | YouTube já enviado: o vídeo foi apagado no YouTube. |
| 404 | `no_video` | O post não tem vídeo no YouTube ainda. |
| 404 | `no_avatar` | O canal não tem foto de perfil disponível. |
| 404 | `media_not_found` | A mídia não existe neste workspace. |
| 409 | `media_in_use` | Um post pendente usa a mídia. |
| 409 | `media_not_ready` | A mídia ainda não foi concluída. |
| 422 | `media_type_mismatch` | A mídia é de outro tipo (vídeo no lugar de capa, ou o contrário), ou a capa não é JPEG nem PNG. |
| 409 | `media_locked` | O vídeo só troca em post adotado ou em rascunho; a imagem de um post de imagem não troca por PATCH. |
| 404 | `unknown_network` | Rede desconhecida. |
| 422 | `network_not_supported` | Esta ação ainda não vale para essa rede. |
| 409 | `invalid_state` | A mídia não está esperando esse envio (não foi criada assim, ou já foi enviada). |
| 409 | `id_exists` | Já existe um post com esse `id`. |
| 409 | `already_adopted` | Esse post do Buffer já está no Praeko. |
| 409 | `nothing_to_publish` | Não há item pendente neste post (ou na rede pedida). |
| 409 | `not_editable` | O item já saiu ou está em andamento (YouTube já enviado: só muda enquanto o vídeo segue privado e agendado; se o horário agendado já passou, não muda mais). |
| 409 | `post_adopted` | Post adotado do Buffer: só texto, horário e rascunho mudam. |
| 409 | `post_prepared` | Este Reel já está sendo preparado na Meta: a legenda não muda mais; só dá para cancelar. |
| 409 | `oauth_not_configured` | A conexão com essa rede não está configurada neste servidor. |
| 409 | `followers_from_api` | A rede já traz os seguidores pela API (só `instagram` aceita valor manual). |
| 409 | `youtube_not_connected` | O YouTube não está conectado. |
| 409 | `instagram_meta_not_connected` | A API da Meta não está conectada. |
| 422 | `download_failed` | Não deu para baixar o arquivo da URL (redirecionamento sem destino, status de erro ou corpo vazio). |
| 422 | `unsupported_mime` | O tipo do arquivo não é aceito para esse `type` (vídeo: MP4 ou MOV; capa: PNG ou JPEG). |
| 422 | `file_too_large` | O arquivo passa do limite desse caminho de envio (por exemplo, `simple: true` acima de 50 MiB). |
| 413 | `part_too_large` | A parte passa de 95 MiB. |
| 422 | `part_too_small` | Uma parte (que não a última) tem menos de 5 MiB. |
| 422 | `part_empty` | A parte está vazia. |
| 422 | `parts_unequal` | As partes têm tamanhos diferentes: todas iguais, só a última pode ser menor. |
| 422 | `parts_missing` | Falta uma parte da sequência: as partes vão de 1 a N, sem buraco. |
| 422 | `no_parts` | Nenhuma parte foi enviada. |
| 422 | `size_mismatch` | O total enviado não bate com o `size` combinado. |
| 422 | `storage_rejected` | O armazenamento recusou juntar as partes; reenvie as partes. |
| — | `identity_not_linked` | Reservado no contrato; o servidor atual não devolve. |
| — | `last_login_method` | Reservado no contrato; o servidor atual não devolve. |
| 409 | `missing_oauth_credential` | A conexão com a Meta não está configurada neste servidor. |
| 409 | `already_connected` | O Instagram já está conectado; nada foi alterado. |
| 400 | `oauth_state_invalid` | Página de volta da Meta: pedido incompleto, vencido ou de outra sessão. Gere a conexão de novo no painel. |
| 400 | `oauth_denied` | Página de volta da Meta: a autorização foi negada. Nada foi alterado. |
| 422 | `no_instagram_account` | Página de volta da Meta: o login não deu acesso a nenhuma conta profissional do Instagram. |
| 409 | `multiple_instagram_accounts` | Página de volta da Meta: o login dá acesso a mais de uma conta; escolha uma. |
| 502 | `token_exchange_failed` | Página de volta da Meta: a Meta recusou trocar o código por um token. Nada foi alterado. |
| 422 | `publish_permission_missing` | Página de volta da Meta: o token não tem a permissão de publicar. |
| 422 | `token_cannot_read_account` | Página de volta da Meta: o token não consegue ler a conta do Instagram. |
| 404 | `webhook_not_found` | O webhook não existe. |
| 404 | `delivery_not_found` | A entrega não existe. |
| 422 | `unsafe_url` | A url não é https, aponta para rede privada ou localhost. |
| 409 | `webhook_limit_reached` | Já são 10 webhooks neste workspace. |
| 404 | `idea_not_found` | A ideia não existe. |
| 409 | `idea_limit_reached` | Já são 2000 ideias neste workspace. |
| 422 | `idempotency_key_reuse` | A `Idempotency-Key` já foi usada com outro pedido (método, caminho, query ou corpo diferentes). |
| 409 | `idempotency_in_progress` | Um pedido com a mesma `Idempotency-Key` ainda está em andamento (repita em instantes). |
| 409 | `idempotency_response_too_large` | A resposta original passou de 256 KB e não foi guardada; consulte o recurso por GET. |
| 422 | `invalid_image` | Mídia `image` cujo arquivo não é um JPEG (a conferência acontece no envio; no envio em partes, ao completar). |
| 422 | `image_aspect_ratio` | A proporção da imagem fica fora de 4:5 a 1,91:1 (o Instagram recusaria; use 1080x1350). |
| 422 | `image_too_large` | A imagem passa de 8 MB (limite do Instagram). |
| 422 | `kind_not_supported_by_network` | `kind: image` num item de YouTube ou TikTok. |
| 422 | `carousel_not_supported` | Mais de uma imagem em `media.images` (carrossel chega depois). |
| 502 | `buffer_error` | O Buffer recusou editar ou cancelar o post. |
| 502 | `instagram_error` | A Meta (Instagram) recusou o pedido ou não respondeu. |
| 502 | `tiktok_unavailable` | Não deu para consultar a conta do TikTok. |
| 502 | `youtube_error` | O YouTube não respondeu ou recusou a conferência/escrita; nada foi alterado (ou o `itemResults` diz o que ficou). |

Os caminhos antigos em português respondem `{ "erro": { "codigo", "mensagem", "detalhes" } }` com o código antigo; o mapa é um para um.
