Convenções
As regras que valem para todos os endpoints, para você não precisar adivinhar nenhuma.
Todos os endpoints da API seguem as mesmas regras. Depois de ler esta página, você sabe o formato de qualquer requisição e de qualquer resposta.
Requisições
- A URL base é
https://api.cartaflow.com.br/v1. Só HTTPS. - O corpo é JSON em UTF-8, com o cabeçalho
Content-Type: application/json. - Há duas exceções, os endpoints que recebem um PDF:
POST /v1/lettersePOST /v1/letters/quote. Eles também aceitammultipart/form-data, para você enviar o arquivo junto com os dados, sem converter para base64. Veja Enviar um PDF com multipart. - Qualquer outro
Content-Typerecebe 400 comunsupported_content_type. - O corpo pode ter até 4,5 MB. Um PDF maior que 4 MB vai por URL ou pelo upload em duas etapas (
POST /v1/uploads), que aceitam até 10 MB. Veja Documento e área do endereço. - Campos desconhecidos são recusados com 422 e o código
unknown_parameter. Assim um erro de digitação, comopostal_cod, nunca passa em silêncio.
Enviar um PDF com multipart
Em POST /v1/letters e POST /v1/letters/quote, você pode enviar o corpo como multipart/form-data. O PDF vai na parte file, com até 4 MB. Os outros campos são os mesmos do JSON, um por parte, com os campos de dentro de objetos escritos entre colchetes e as listas repetindo o campo com []:
| No JSON | No multipart |
|---|---|
{ "to": { "name": "Destinatário Exemplo" } } | to[name]=Destinatário Exemplo |
{ "to": { "postal_code": "01001-000" } } | to[postal_code]=01001-000 |
{ "variables": { "nome": "Destinatário Exemplo" } } | variables[nome]=Destinatário Exemplo |
{ "metadata": { "customer_id": "1842" } } | metadata[customer_id]=1842 |
{ "color": true } | color=true |
{ "postal_services": ["registered", "return_receipt"] } | postal_services[]=registered e postal_services[]=return_receipt |
Booleanos vão como o texto true ou false. As regras de validação, as respostas e os erros são os mesmos do JSON.
curl -X POST https://api.cartaflow.com.br/v1/letters \
-H "Authorization: Bearer $CARTAFLOW_API_KEY" \
-F "file=@exemplo-a4.pdf" \
-F "to[name]=Destinatário Exemplo" \
-F "to[postal_code]=01001-000" \
-F "to[number]=128" \
-F "color=true"Com curl -F, o cabeçalho Content-Type: multipart/form-data é enviado automaticamente.
Nomes em inglês, mensagens no seu idioma
Os nomes de campos, valores, eventos e códigos de erro estão em inglês e em snake_case: postal_code, in_production, letter.posted, insufficient_balance. É o padrão da maioria das APIs, e o seu código fica igual para equipes no Brasil e fora dele.
O que é escrito para pessoas, como error.message, vem em português ou em inglês, conforme o cabeçalho Accept-Language. Sem esse cabeçalho, a API responde em português.
IDs
Todo objeto tem um id com um prefixo que diz o tipo. Guarde o id inteiro, como texto.
| Prefixo | Objeto | Exemplo |
|---|---|---|
ltr_ | Correspondência | ltr_8Kq2wX4mN7pR |
upl_ | Upload de um PDF (POST /v1/uploads) | upl_6Np3Vx8Rk2Tq |
ver_ | Verificação de endereço | ver_3Hd9Kp2qZ6tW |
evt_ | Evento | evt_6Tq1Yz8Wm3Rb |
btx_ | Movimentação do saldo | btx_4Lm8Qw2Zx9Kd |
acc_ | Conta | acc_7Jw3Nf8Xq2Lp |
req_ | Requisição (cabeçalho Request-Id e error.request_id) | req_9Vb4Xk2Lq8Mz |
Todo objeto também tem o campo object, com o tipo por extenso: letter, upload, verification, event, balance_transaction, account. Listas têm object: "list".
Datas
- Datas com hora seguem a ISO 8601, em UTC:
2026-10-13T14:20:00Z. Converta para o fuso do seu usuário na hora de mostrar. send_date, a data de agendamento de uma correspondência, é só a data:2026-10-20, no formatoAAAA-MM-DD, no horário de Brasília.- Os filtros
created_afterecreated_beforerecebem data e hora ISO 8601.
Valores em dinheiro
Valores são números inteiros em centavos, sempre com o campo currency, que vale BRL. Assim não há erro de arredondamento.
{ "currency": "BRL", "total": 1690 }1690 são R$ 16,90. Para mostrar, divida por 100 e formate em reais.
Todos os campos sempre aparecem
Uma resposta traz todos os campos documentados do objeto, sempre. Quando um campo não se aplica, ele vem com null, nunca some. Por exemplo, send_date é null em uma correspondência enviada na hora, e cancellation_reason é null enquanto ela não foi cancelada.
metadata sem dados vem como objeto vazio, {}. Booleanos são sempre true ou false, sem aspas.
Métodos HTTP
| Método | Para que serve | Exemplo |
|---|---|---|
GET | Consultar um objeto ou uma lista. Nunca muda nada. | GET /v1/letters/{letter_id} |
POST | Criar um objeto ou executar uma ação. | POST /v1/letters, POST /v1/letters/{letter_id}/cancel |
Status HTTP
| Status | Quando |
|---|---|
200 | Deu certo: consulta, listagem, alteração ou ação, como cancelar ou calcular o preço. |
201 | Um objeto foi criado: correspondência, upload ou verificação. |
400 | Não conseguimos ler a requisição: JSON malformado, Content-Type não aceito ou Idempotency-Key inválida. |
401 | A chave de API está ausente, inválida ou revogada. |
402 | O saldo não cobre a operação. |
403 | A conta não pode fazer a operação agora, por exemplo porque está suspensa. |
404 | O objeto não existe na sua conta, ou o endpoint não existe. |
409 | O estado atual do objeto não permite a operação, ou a Idempotency-Key conflita com outra requisição. |
413 | O corpo passa de 4,5 MB. Envie o PDF por URL ou com POST /v1/uploads. |
422 | Um ou mais campos têm valores inválidos. error.details lista todos. |
429 | Requisições demais por segundo. Espere o tempo de Retry-After. |
500 | Erro inesperado na CartaFlow. Repita com a mesma Idempotency-Key. |
503 | API temporariamente indisponível. Repita depois de Retry-After. |
Toda resposta de erro tem o mesmo formato. Veja Erros.
Cabeçalhos
O que você envia:
| Cabeçalho | Obrigatório | Para que serve |
|---|---|---|
Authorization | Sim | Bearer seguido da chave de API. Veja Autenticação. |
Content-Type | Quando há corpo | application/json. Em POST /v1/letters e POST /v1/letters/quote, também multipart/form-data. |
Idempotency-Key | Não, mas recomendado em todo POST | Evita criar o mesmo objeto duas vezes quando você repete uma requisição. Veja Idempotência. |
Accept-Language | Não | pt-BR (padrão) ou en. Define o idioma de error.message. |
O que a API devolve:
| Cabeçalho | O que traz |
|---|---|
Request-Id | O id da requisição, como req_9Vb4Xk2Lq8Mz. Envie ao suporte quando precisar de ajuda. |
RateLimit-Limit | Quantas requisições por segundo a conta pode fazer, somando todas as chaves. |
RateLimit-Remaining | Quantas ainda restam na janela atual. |
RateLimit-Reset | Em quantos segundos a janela recomeça. |
Retry-After | Em respostas 429 e 503: quantos segundos esperar antes de repetir. |
Idempotent-Replayed | true quando a resposta é a repetição de uma requisição anterior com a mesma Idempotency-Key. |
A API cresce sem quebrar a sua integração
A versão 1 recebe novos campos, novos valores de enums, novos eventos e novos códigos de erro sem aviso prévio, porque isso não quebra uma integração bem feita. Para isso, o seu código precisa:
- ignorar campos que ele não conhece nas respostas e nos webhooks;
- tratar um valor desconhecido de
statuscomo "em andamento", e umerror.codedesconhecido peloerror.type; - ignorar eventos de tipos que ele não usa.
Mudanças que quebram integrações só acontecem em uma nova versão. Veja Versões e compatibilidade.