Erros
O formato dos erros, o que cada código significa e quando repetir uma requisição.
Toda resposta de erro tem o mesmo formato, em qualquer endpoint. O status HTTP diz a categoria, e error.code diz o erro exato. Quando o problema está nos campos, a lista details traz todos os campos com problema de uma vez, para você corrigir tudo em uma tentativa.
O formato
{
"error": {
"type": "validation_error",
"code": "invalid_parameters",
"message": "2 campos têm problemas. Veja details.",
"request_id": "req_9Vb4Xk2Lq8Mz",
"doc_url": "https://docs.cartaflow.com.br/errors#invalid_parameters",
"details": [
{
"param": "to.postal_code",
"code": "postal_code_not_found",
"message": "Não encontramos o CEP 01001-999. Confira os 8 dígitos."
},
{
"param": "document.url",
"code": "address_area_not_blank",
"message": "Há conteúdo na área do endereço da primeira página. Deixe a área em branco ou use address_placement: extra_page."
}
]
}
}| Campo | O que é |
|---|---|
type | A categoria do erro. Cada tipo corresponde a um status HTTP (tabela abaixo). |
code | O erro exato. Use este campo no seu código: um código nunca muda de significado. Novos códigos podem ser criados; trate um código desconhecido pelo type. |
message | Explicação para pessoas. O texto pode mudar, então não compare mensagens no seu código. |
request_id | O id da requisição, o mesmo do cabeçalho Request-Id. Envie ao suporte quando precisar de ajuda. |
doc_url | Link para a explicação do código nesta página. |
details | Os campos com problema, cada um com param (o campo, em notação de pontos), code e message. Só existe em validation_error; nos outros tipos vale null. |
Quando há um erro, nada é criado nem cobrado.
Idioma das mensagens
message vem em português por padrão. Para receber as mensagens em inglês, envie o cabeçalho Accept-Language: en. Os códigos são sempre os mesmos, em qualquer idioma.
curl "https://api.cartaflow.com.br/v1/letters/ltr_inexistente" \
-H "Authorization: Bearer $CARTAFLOW_API_KEY" \
-H "Accept-Language: en"Tipos de erro
| HTTP | error.type | O que significa |
|---|---|---|
| 400, 413 | invalid_request_error | Não conseguimos ler a requisição: o JSON está malformado, o cabeçalho está errado ou o corpo é grande demais. |
| 401 | authentication_error | A chave de API está ausente, inválida ou revogada. |
| 402 | billing_error | O saldo da conta não cobre a operação. |
| 403 | permission_error | A conta não pode fazer esta operação agora. |
| 404 | not_found_error | O objeto ou o endpoint não existe. |
| 409 | conflict_error | A requisição é válida, mas o estado atual do objeto não permite a operação. |
| 422 | validation_error | Lemos a requisição, mas um ou mais campos têm valores inválidos. A lista details diz quais e por quê. |
| 429 | rate_limit_error | A conta passou do limite de requisições por segundo. |
| 500, 503 | api_error | Algo falhou do lado da CartaFlow. Não é um problema da sua requisição. |
Códigos de erro
O valor de error.code. O link doc_url de cada erro leva para a linha dele nesta tabela.
| error.code | HTTP | O que aconteceu | Como resolver |
|---|---|---|---|
invalid_json | 400 | O corpo da requisição não é um JSON válido. | Confira aspas, vírgulas e chaves. Gere o corpo com a função de JSON da sua linguagem em vez de montar o texto à mão. |
unsupported_content_type | 400 | O cabeçalho Content-Type não é aceito neste endpoint. | Envie application/json. Só POST /v1/letters e POST /v1/letters/quote aceitam também multipart/form-data, para enviar o PDF junto. |
invalid_idempotency_key | 400 | O cabeçalho Idempotency-Key está vazio ou tem mais de 255 caracteres. | Use um identificador único e curto, como o id da fatura no seu sistema ou um UUID. |
request_too_large | 413 | O corpo da requisição passa de 4,5 MB, o máximo que a API recebe numa requisição. | Envie o PDF por URL (document.type = url) ou com POST /v1/uploads (document.type = upload). As duas formas aceitam PDFs de até 10 MB. |
api_key_missing | 401 | A requisição não tem o cabeçalho Authorization. | Envie Authorization: Bearer seguido da sua chave de API. |
api_key_invalid | 401 | A chave não existe ou está escrita de forma errada. | Copie a chave de novo no painel, em Desenvolvedores > Chaves de API. Ela começa com cf_live_. |
api_key_revoked | 401 | A chave foi revogada no painel. | Crie uma chave nova no painel e atualize a variável de ambiente do seu sistema. |
insufficient_balance | 402 | O saldo disponível é menor que o preço do envio. | Recarregue o saldo no painel. Consulte GET /v1/balance antes de envios grandes. |
account_suspended | 403 | A conta está suspensa e não pode criar envios. | Fale com o suporte da CartaFlow pelo painel. |
resource_not_found | 404 | Não existe objeto com esse id na sua conta. | Confira o id, letra por letra. Cada conta só vê os próprios objetos: o id de um objeto de outra conta também recebe 404. |
endpoint_not_found | 404 | O caminho ou o método HTTP não existe na API. | Confira a URL, o prefixo /v1 e o método HTTP indicado na referência. |
postal_code_not_found | 404 | O CEP consultado não existe na base de CEPs dos Correios. | Confira os 8 dígitos com a pessoa ou com o cadastro de origem. |
letter_not_cancelable | 409 | O status da correspondência não é mais scheduled nem received: ela já entrou em produção, foi postada ou falhou. | Consulte a correspondência para ver o status atual. Depois da produção não é possível cancelar. |
verification_not_cancelable | 409 | A verificação terminou sem confirmação (failed ou expired) e não pode ser cancelada nem revogada. | Consulte a verificação para ver o status atual. Para tentar de novo, crie uma verificação nova. |
verification_not_awaiting_code | 409 | A verificação não está esperando código: a correspondência ainda não foi postada, ou a verificação já terminou, foi cancelada ou foi revogada. | Só confira códigos quando o status for awaiting_code. A mensagem de erro informa o status atual. |
idempotency_key_reused | 409 | Essa Idempotency-Key já foi usada com outro corpo ou em outro endpoint nas últimas 24 horas. | Use uma chave nova para cada operação diferente. Repita a mesma chave só para repetir exatamente a mesma requisição. |
idempotency_key_in_use | 409 | Uma requisição com a mesma Idempotency-Key ainda está sendo processada. | Espere um segundo e repita. Você vai receber a resposta da primeira requisição. |
invalid_parameters | 422 | Um ou mais campos têm valores inválidos. | Leia a lista details: cada item traz o campo (param), um código e uma mensagem. |
rate_limit_exceeded | 429 | A conta fez mais requisições por segundo do que o limite. | Espere os segundos indicados no cabeçalho Retry-After e repita. Para envios grandes, distribua as chamadas. |
internal_error | 500 | Erro inesperado na CartaFlow. | Repita com a mesma Idempotency-Key. Se continuar, envie o request_id ao suporte. |
service_unavailable | 503 | A API está temporariamente indisponível, por manutenção ou sobrecarga. | Repita depois do tempo do cabeçalho Retry-After, com a mesma Idempotency-Key. |
Códigos de detalhe
Em um validation_error, cada item de details tem um destes códigos, junto com o campo em param.
| details[].code | O que há de errado no campo | Como resolver |
|---|---|---|
required | Campo obrigatório ausente. | Envie o campo indicado em param. |
unknown_parameter | O campo não existe neste endpoint. | Confira a grafia. A API rejeita campos desconhecidos para que um erro de digitação não passe em silêncio. |
invalid_type | O tipo do valor está errado, como um texto onde se espera um número. | Envie o tipo indicado na referência. Booleanos são true ou false, sem aspas. |
invalid_value | O valor não é aceito: não está na lista de valores permitidos ou não combina com outro campo da requisição, como letter e document juntos numa verificação. | Use um dos valores listados na referência do campo. A mensagem explica o motivo. |
invalid_format | O formato está errado: URL, data, base64, CEP ou código. | Siga o formato e o exemplo da referência do campo. |
too_short | O texto ou a lista está abaixo do tamanho mínimo. | Confira o mínimo na referência do campo. |
too_long | O texto ou a lista passa do tamanho máximo. | Confira o máximo na referência do campo e abrevie, se for um endereço. |
out_of_range | O número ou a data está fora do intervalo permitido, como um send_date que não seja de amanhã até 180 dias à frente. | Confira o mínimo e o máximo na referência do campo. |
requires_registered | return_receipt e restricted_delivery só funcionam com registered: o aviso de recebimento e a mão própria são serviços da carta registrada. | Inclua registered em postal_services, ou tire os serviços que dependem dele. |
postal_code_not_found | O CEP não existe na base dos Correios. | Confira os 8 dígitos. Você pode testar o CEP antes com GET /v1/postal-codes/{postal_code}. |
address_incomplete | Não foi possível completar o endereço: o campo não veio na requisição e o CEP não o informa. Acontece com CEPs que valem para uma cidade inteira, que não têm logradouro nem bairro. | Envie o campo indicado em param, como street e neighborhood. |
document_unreachable | Não conseguimos baixar o PDF da URL em 10 segundos, ou o servidor respondeu com erro. | Use uma URL HTTPS pública ou assinada (S3, GCS, Azure) válida por pelo menos 10 minutos. |
upload_not_found | Não existe upload com esse id na sua conta. | Confira o upload_id. Use o id devolvido por POST /v1/uploads, com a mesma conta da chave de API. |
upload_expired | O upload foi criado há mais de 24 horas e não pode mais ser usado. | Crie um upload novo com POST /v1/uploads e envie o arquivo de novo. |
upload_already_used | O upload já foi usado por outra correspondência. Cada upload serve para uma correspondência só. | Crie um upload novo para cada correspondência. Para repetir a mesma criação com segurança, use a mesma Idempotency-Key. |
upload_file_missing | O arquivo nunca foi enviado para o upload_url do upload. | Envie o PDF com PUT para o upload_url, com o cabeçalho Content-Type: application/pdf, e espere a resposta 2xx antes de criar a correspondência. Se o link expirou (ele vale 2 horas), crie um upload novo. |
document_invalid | O arquivo não é um PDF válido: está corrompido ou é de outro formato. | Abra o arquivo em um leitor de PDF para conferir. Em base64, envie só o conteúdo, sem o prefixo data:application/pdf;base64,. |
document_encrypted | O PDF está protegido por senha. | Gere o PDF sem senha e sem restrições de impressão. |
document_too_large | O PDF passa do tamanho máximo da forma de envio: 10 MB por URL ou com POST /v1/uploads, 4 MB em multipart/form-data e 3 MB em base64 (depois de decodificado). | Reduza a resolução das imagens para 300 dpi, o máximo que a impressão aproveita. Para um PDF grande, use URL ou POST /v1/uploads, que aceitam até 10 MB. |
document_page_size | Alguma página não é A4 em retrato (210 x 297 mm). | Exporte o PDF em A4, orientação retrato, em todas as páginas. |
too_many_sheets | A correspondência passaria de 6 folhas, o máximo que cabe em um envelope. | Use double_sided: true para imprimir frente e verso, ou divida o conteúdo em mais de uma correspondência. |
address_area_not_blank | Há texto ou imagem na área do endereço da primeira página, onde a CartaFlow imprime o destinatário. | Deixe a área em branco no seu layout ou use address_placement: extra_page. |
variable_missing | O PDF tem uma marcação, como {{nome}}, que não veio em variables. | Envie um valor para cada marcação do PDF. Para deixar uma em branco, envie uma string vazia. |
variable_not_found | Você enviou uma variável que o PDF não tem. | Confira o nome, letra por letra. Num PDF feito no Word, abra o PDF e confira se a marcação aparece inteira, com as chaves, como texto selecionável. |
document_text_unreadable | O PDF não tem texto selecionável: é uma imagem ou uma digitalização. Sem texto, não conseguimos encontrar as marcações, como as variáveis ou o {{OTP_CODE}} de uma verificação. | Exporte o PDF direto do editor (no Word: Arquivo > Salvar como > PDF), em vez de imprimir em PDF a partir de uma imagem ou digitalizar. |
otp_code_missing | O PDF de uma verificação não tem a marcação {{OTP_CODE}}, o lugar onde a CartaFlow imprime o código. | Escreva {{OTP_CODE}}, em maiúsculas e com as chaves, onde o código deve aparecer, e exporte o PDF de novo com texto selecionável. Ou crie a verificação sem document, com o modelo da CartaFlow. |
Quando repetir uma requisição
Alguns erros são passageiros e vale repetir. Outros só passam quando você muda a requisição.
| Situação | O que fazer |
|---|---|
429 (rate_limit_exceeded) | Espere os segundos do cabeçalho Retry-After e repita. |
500 (internal_error) e 503 (service_unavailable) | Repita com espera crescente: 1, 2, 4, 8 segundos, até umas 5 vezes. No 503, respeite Retry-After. |
| Falha de rede ou tempo esgotado, sem resposta | Repita como no 500. Você não sabe se a primeira chegou, e a Idempotency-Key garante que nada seja criado em dobro. |
409 com idempotency_key_in_use | A primeira requisição com a mesma chave ainda está em andamento. Espere um segundo e repita. |
Qualquer outro 4xx | Não repita igual. Corrija a requisição conforme o código. |
Sempre repita com a mesma Idempotency-Key. Assim, se a primeira tentativa chegou a criar a correspondência, a repetição devolve a mesma correspondência em vez de criar outra. Veja Idempotência.
Um exemplo de tratamento
import { randomUUID } from 'node:crypto';
const RETRYABLE = new Set([429, 500, 503]);
async function cartaflow(path, { method = 'GET', body, idempotencyKey } = {}) {
// Uma chave por operação, criada uma vez e reaproveitada em todas as tentativas.
const key = idempotencyKey ?? (method === 'POST' ? randomUUID() : undefined);
for (let attempt = 0; attempt < 5; attempt++) {
let response;
try {
response = await fetch(`https://api.cartaflow.com.br/v1${path}`, {
method,
headers: {
Authorization: `Bearer ${process.env.CARTAFLOW_API_KEY}`,
...(body ? { 'Content-Type': 'application/json' } : {}),
...(key ? { 'Idempotency-Key': key } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
} catch {
await sleep(2 ** attempt * 1000); // falha de rede: repita com a mesma chave
continue;
}
if (response.ok) return response.json();
const { error } = await response.json();
const inUse = response.status === 409 && error.code === 'idempotency_key_in_use';
if (!RETRYABLE.has(response.status) && !inUse) {
// Erro que não passa sozinho: registre e mostre a quem precisa corrigir.
throw Object.assign(new Error(error.message), { code: error.code, details: error.details, requestId: error.request_id });
}
const retryAfter = Number(response.headers.get('Retry-After'));
await sleep(retryAfter > 0 ? retryAfter * 1000 : 2 ** attempt * 1000);
}
throw new Error('A API da CartaFlow não respondeu depois de 5 tentativas.');
}
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));Pedir ajuda ao suporte
Toda resposta, de sucesso ou de erro, traz o cabeçalho Request-Id, e os erros também trazem error.request_id. Registre esse valor nos seus logs. Ao falar com o suporte, envie o request_id: com ele encontramos a requisição exata.