Tratamento de erros
O Cord usa códigos HTTP convencionais e uma estrutura de erro previsível para indicar sucesso ou falha.
Códigos de status HTTP
A API do Cord sempre devolve códigos de status HTTP padrão. Como regra geral:
2xxindica sucesso.4xxindica um erro originado nas informações enviadas (por exemplo, parâmetros ausentes, token inválido).5xxindica um erro nos servidores do Cord (são muito raros).
Resumo dos códigos
| Código | Descrição |
|---|---|
| 200 OK | Tudo funcionou como esperado. |
| 400 Bad Request | A requisição era inaceitável. Geralmente significa invalid_request (faltam campos) ou invalid_json; também invalid_cursor ou invalid_api_version. |
| 401 Unauthorized | A sua chave de API está ausente, é incorreta ou foi revogada. |
| 402 Payment Required | A operação exige um plano superior (subscription_required), excede um limite do seu plano atual (plan_limit_reached, por exemplo, número de clientes ou produtos) ou a chave excede o número de chaves que o seu plano inclui (subscription_key_limit). |
| 403 Forbidden | Você não tem permissões suficientes (por exemplo, tentou escrever com uma chave que só tem permissão de leitura, ou uma Publishable Key tentou uma operação fora do seu escopo). |
| 404 Not Found | O recurso (orçamento, cliente, fatura) não existe. |
| 409 Conflict | Uma mudança de status inválida para o recurso (invalid_state) — por exemplo, aprovar um orçamento que ainda é rascunho, ou apagar dados de teste (/api/test-mode/reset) sem estar em uma organização do tipo Sandbox. Também quando outra requisição com a mesma Idempotency-Key ainda está em andamento (idempotency_in_progress), ou quando uma fatura timbrada precisa de uma nota de crédito em vez de ser cancelada (credit_note_required). |
| 413 Payload Too Large | O corpo passa de 1 MB (payload_too_large); em POST /api/v1/elements/ai-draft, o arquivo passa de 4 MB, e em POST /api/v1/setup/plans, o arquivo passa de 3 MB. |
| 415 Unsupported Media Type | O corpo não foi enviado como application/json (unsupported_media_type). |
| 422 Unprocessable Entity | Você reutilizou uma Idempotency-Key com uma requisição diferente (idempotency_key_reused), ou a IA não encontrou produtos no texto (no_items). |
| 429 Too Many Requests | Você ultrapassou um limite de requisições por minuto (rate_limited, veja Limites de requisições), a cota mensal de API do seu plano (api_quota_exceeded) ou a de IA (ai_quota_exceeded). Respeite o header Retry-After. |
| 502 Bad Gateway | Um provedor externo rejeitou ou não concluiu a operação: o provedor fiscal ao emitir (provider_error) ou o envio de um e-mail (send_failed). |
| 503 Service Unavailable | Um serviço do qual a operação depende não respondeu — por exemplo, não foi possível obter uma taxa de câmbio (fx_unavailable) ou o catálogo de impostos (tax_catalog_unavailable), ou não foi possível verificar a sua assinatura (subscription_verification_unavailable). O Cord nunca inventa uma taxa: se não puder comprová-la, a operação falha de forma fechada. Tente de novo mais tarde. |
Estrutura do objeto de erro
Quando ocorre um erro 4xx ou 5xx, o Cord sempre responde com um objeto JSON plano (não aninhado) com dois campos: error (mensagem legível, para mostrar ou registrar) e code (string estável para a sua lógica de tratamento). Alguns erros acrescentam campos adicionais conforme o caso (veja os exemplos abaixo).
Exemplo de resposta de erro:
{
"error": "The company name is required",
"code": "invalid_request"
}
Exemplo com campos adicionais (limite de plano, 402):
{
"error": "Your plan allows 50 clients. Free up space or upgrade your plan to continue.",
"code": "plan_limit_reached",
"resource": "clients",
"limit": 50,
"plan": "starter"
}
Não conte com a forma aninhada error.code / error.message para fazer o parse da resposta: error é sempre a string da mensagem e code é um campo irmão no mesmo nível, não uma propriedade de error.
Toda resposta de /api/v1 traz o header Cord-Request-Id (req_…). Em um erro, o corpo o repete como request_id e acrescenta doc_url, o link para a explicação desse código nesta página. Inclua o request_id quando escrever para o suporte: com ele, encontramos a sua requisição exata.
{
"error": "API key inválida o revocada.",
"code": "invalid_key",
"request_id": "req_3f9c2a1b7d4e6f8a0b1c2d3e",
"doc_url": "https://docs.cordhq.app/docs/desarrolladores/esenciales/errores#invalid_key"
}
Códigos de erro comuns (code)
-
invalid_json: o corpo da sua requisiçãoPOSTnão é um JSON válido, ou não foi enviadoContent-Type: application/json. -
invalid_request: as validações de negócio falham (falta um campo, um desconto fora do intervalo, um orçamento sem cliente válido). O campoerrorexplica o motivo exato. -
invalid_fiscal_data(400): os dados fiscais decliente.fiscalnão passam na validação do país. A resposta trazissues, uma lista de{ field, code }. Veja Formulário fiscal. -
missing_key/invalid_key: falta o headerAuthorization: Bearer, ou a chave não existe / foi revogada. -
token_expired: a credencial de uma conexão autorizada (Zapier, Make) expirou. O app a renova sozinho com o seu refresh token. -
insufficient_scope: a sua chave não tem o escopo necessário (uma chave somente leitura tentou escrever, ou uma Publishable Key tentou uma rota fora da sua lista permitida). -
insufficient_permissions(403): uma chave restrita (rk_) não tem permissão para o recurso dessa rota, ou foi usada com MCP. O campoerrornomeia o recurso. -
ip_not_allowed(403): a chave tem IPs permitidos e a requisição chegou de outro. O campoerrorinclui o IP que o Cord viu. -
key_expired(401): a chave expirou, ou foi rotacionada e o período de carência terminou. Use a chave nova ou gere outra. -
not_found: o recurso solicitado não existe na sua organização. -
subscription_required: o recurso exige um plano superior. A resposta incluiplan(o atual) eplan_required. -
plan_limit_reached: você atingiu um limite rígido do seu plano (clientes, produtos ou orçamentos ativos). A resposta incluiresource,limiteplan. -
integration_limit_reached(403): as suas integrações já criaram as 100 assinaturas de webhook permitidas por organização. Exclua as que você não usa mais. -
rate_limited/api_quota_exceeded: limite de requisições por minuto ou de cota mensal da API. Tente de novo respeitandoRetry-After. -
fx_unavailable/tax_catalog_unavailable: um dado de terceiros (taxa de câmbio ou catálogo de impostos) não pôde ser obtido com certeza. A operação não foi concluída — não presuma que ela ocorreu. -
subscription_key_limit(402): a sua organização tem mais chaves ativas do que o plano inclui (por exemplo, depois de mudar para um plano inferior) e esta chave ficou fora da cota; as mais antigas continuam funcionando. Revogue as chaves que você não usa mais ou mude para um plano superior. A cota conta as chaves secretas, restritas e publicáveis ativas, sem as conexões autorizadas por OAuth: 2 no Grátis, 5 no Starter, 20 no Profissional, 50 no Scale e 200 no Developer. -
subscription_verification_unavailable(503): o Cord não conseguiu verificar a sua assinatura ou registrar o uso da chamada. A operação não foi executada; tente de novo. -
invalid_api_version(400): o headerCord-Versionpede uma versão que não existe. A mensagem lista as disponíveis. Veja Versões da API. -
invalid_cursor(400): ocursornão é válido. Use exatamente onext_cursorda resposta anterior. -
payload_too_large(413): o corpo JSON passa de 1 MB, ou o arquivo enviado paraPOST /api/v1/elements/ai-draftpassa de 4 MB. -
unsupported_media_type(415): o corpo não vem comoapplication/json(ou comomultipart/form-dataemPOST /api/v1/elements/ai-draft). -
test_mode_only(403): os simuladores de/api/v1/test_helperssó funcionam com uma chavesk_test_e sobre o ambiente de teste. -
credit_note_required(409): a fatura já timbrada não pode ser cancelada diretamente. Emita uma nota de crédito comaction: "credit_note". -
provider_error(502): o provedor fiscal rejeitou ou não concluiu a emissão. O campoerrorexplica o motivo. -
send_failed(502): não foi possível enviar o e-mail da fatura. Tente o envio de novo. -
ai_quota_exceeded(429) /no_items(422) /ai_unavailable(502 ou 503) /missing_text/text_too_long(400): erros das rotas com IA (POST /api/v1/cotizaciones/iaePOST /api/v1/elements/ai-draft): a cota de IA do seu plano acabou, o texto não trazia produtos, a IA não está disponível ou não conseguiu processar o pedido, ou o texto está ausente ou passa do máximo. Emai-draft, esses códigos chegam como eventoerrordentro do stream. -
invalid_state: o recurso não está em um status que permita a operação, ou mudou enquanto a processávamos. Nada foi aplicado. -
invalid_url: a URL de um webhook não éhttps://ou aponta para um host interno. -
invalid_idempotency_key/idempotency_key_reused/idempotency_in_progress/idempotency_unavailable: veja a próxima seção. -
secret_key_in_browser(403): uma Secret Key chegou a partir de um navegador. Ela já ficou exposta no código da página: revogue-a na aba Chaves do Cord Workbench e chame a API somente a partir do seu servidor. -
missing_origin/unauthorized_origin/invalid_origin(403): uma Publishable Key chegou semOrigin, ou de um domínio que não está nos domínios permitidos da sua organização (Configurações › Gerador de orçamentos incorporável). -
origin_allowlist_required(403): uma Publishable Key de produção só funciona com domínios permitidos configurados em Configurações › Gerador de orçamentos incorporável. As chavespk_test_funcionam a partir de qualquer origem.
Limites de requisições
O Cord aplica estes limites por janela de um minuto, salvo onde indicado. Ao ultrapassá-los, responde 429 com code: "rate_limited", em geral com o header Retry-After em segundos. Os marcados como rígidos não deixam passar requisições quando o Cord não consegue contá-las: em /api/v1/cotizaciones/{id} e /api/v1/elements/ai-draft, eles respondem então 503 com error: "access_temporarily_unavailable".
| Limite | Escopo | Aplica-se a |
|---|---|---|
| 120 por minuto | Por IP de origem | Toda requisição a /api/v1 e ao servidor MCP, com qualquer chave, antes de validar a chave. |
| 600 por minuto | Por chave secreta (sk_, rk_ ou conexão OAuth) |
Toda requisição a /api/v1 e ao servidor MCP. |
| 120 por minuto | Por chave publicável (pk_) |
As quatro rotas que uma pk_ aceita. |
| 120 por minuto (rígido) | Por organização | POST e DELETE em /api/v1/cotizaciones/{id} e as ferramentas MCP que mudam o status de um orçamento. |
| 20 por minuto | Por chave | POST /api/v1/webhooks. |
| 20 por minuto | Por organização | POST /api/v1/cotizaciones/ia. |
| 6 por minuto por IP, 60 por minuto e 500 por dia por organização (rígidos) | IP e organização | POST /api/v1/elements/ai-draft. |
| 6 por dia (rígido) | Por organização | POST /api/v1/setup/plans e proponer_configuracion no MCP. |
Como o limite por IP é menor que o de uma chave secreta, um servidor que envia tudo a partir de um único IP chega primeiro a 120 por minuto. Além desses limites, as chaves de produção consomem a cota mensal de API do seu plano (api_quota_exceeded).
Novas tentativas seguras com Idempotency-Key
Se uma requisição que cria ou altera algo (POST, PATCH, DELETE) for interrompida pela rede ou por timeout, você não sabe se o Cord a aplicou. Envie o header Idempotency-Key com um valor único por operação (por exemplo, um UUID gerado por você) e tente de novo com a mesma chave e o mesmo corpo:
curl -X POST "https://cordhq.app/api/v1/clientes" \
-H "Authorization: Bearer sk_live_tU..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5d9c2b7e-6f1a-4c3d-9e8b-2a1f0c7d6e5b" \
-d '{ "empresa": "ACME Corp" }'
- Nova tentativa com a mesma chave e o mesmo corpo: o Cord devolve a resposta original sem executar a operação de novo nem contá-la na sua cota. A resposta repetida traz o header
Idempotent-Replayed: true. - A mesma chave com outro corpo, método ou rota:
422 idempotency_key_reused. Use uma chave nova para cada operação diferente. - A primeira requisição ainda não terminou:
409 idempotency_in_progress. Aguarde e tente de novo. - Erros
5xxe429: não são armazenados, então você pode tentar de novo com a mesma chave. - A chave vale por chave de API, aceita de 1 a 255 caracteres visíveis (
invalid_idempotency_keyse não) e expira em 24 horas. - Se o Cord não conseguir reservar a chave, responde
503 idempotency_unavailablesem executar a operação.
O header é opcional: sem ele, cada requisição é executada como chega.