Quickstart
Create a key, price a letter at no cost and send your first letter.
In this guide you create an API key, check it, price a letter at no cost, send the letter and follow its status.
Create an API key
In the CartaFlow dashboard, which is in Portuguese, open Desenvolvedores > Chaves de API (Developers > API keys) and create a key. It starts with cf_live_ and is shown only once: copy it and store it somewhere safe.
Put the key in an environment variable on your server. Every example in these docs reads it from CARTAFLOW_API_KEY.
export CARTAFLOW_API_KEY="cf_live_..."The key stays on your server
Never put the key in your website, app or repository. See Authentication.
Check the key
Call GET /v1/account. The response shows the company that owns the key.
curl https://api.cartaflow.com.br/v1/account \
-H "Authorization: Bearer $CARTAFLOW_API_KEY"{
"id": "acc_7Jw3Nf8Xq2Lp",
"object": "account",
"legal_name": "Exemplo Serviços Ltda.",
"trade_name": "Exemplo",
"cnpj": "12.345.678/0001-90",
"plan": "essential",
"default_sender": {
"name": "Exemplo Serviços Ltda.",
"street": "Avenida Exemplo",
"number": "1000",
"complement": "Sala 301",
"neighborhood": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"postal_code": "01310-100"
},
"created_at": "2026-09-01T12:00:00Z"
}A 401 response means the key is wrong or was revoked. Copy it again from the dashboard.
Price the letter, at no cost
Call POST /v1/letters/quote with the recipient and the document. The dry run validates everything, completes the address from the postal code, counts the pages and returns the price, without creating or charging anything. Use the sample PDF from these docs, which already leaves the address area blank: https://docs.cartaflow.com.br/samples/exemplo-a4.pdf.
The address only needs a name, the postal code (CEP) and the building number. The street, neighborhood, city and state come from the postal code.
curl -X POST https://api.cartaflow.com.br/v1/letters/quote \
-H "Authorization: Bearer $CARTAFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": { "name": "Destinatário Exemplo", "postal_code": "01001-000", "number": "128" },
"document": { "type": "url", "url": "https://docs.cartaflow.com.br/samples/exemplo-a4.pdf" }
}'{
"object": "quote",
"plan": "essential",
"to": {
"name": "Destinatário Exemplo",
"street": "Praça da Sé",
"number": "128",
"complement": null,
"neighborhood": "Sé",
"city": "São Paulo",
"state": "SP",
"postal_code": "01001-000"
},
"pages": 1,
"sheets": 1,
"price": {
"currency": "BRL",
"total": 990,
"items": [{ "type": "base", "quantity": 1, "unit_amount": 990, "amount": 990 }]
}
}If something is wrong, the 422 response lists every problem at once, the same ones creating the letter would report. The prices in the examples are illustrative: the real price depends on your account's plan. Amounts are in centavos: 990 is R$ 9.90.
Send the letter
Call POST /v1/letters with the same body. Now the letter is real: it is printed, posted through Correios and charged to your balance. To see the result, send it to your company's address.
curl -X POST https://api.cartaflow.com.br/v1/letters \
-H "Authorization: Bearer $CARTAFLOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: first-0001" \
-d '{
"to": {
"name": "Destinatário Exemplo",
"postal_code": "01001-000",
"number": "128"
},
"document": {
"type": "url",
"url": "https://docs.cartaflow.com.br/samples/exemplo-a4.pdf"
}
}'If the PDF is on your machine or generated by your system, upload the file directly as multipart/form-data, up to 4 MB. The PDF goes in the file part and the address fields are written in brackets. Download the sample PDF to try it:
curl -X POST https://api.cartaflow.com.br/v1/letters \
-H "Authorization: Bearer $CARTAFLOW_API_KEY" \
-H "Idempotency-Key: first-0002" \
-F "file=@exemplo-a4.pdf" \
-F "to[name]=Destinatário Exemplo" \
-F "to[postal_code]=01001-000" \
-F "to[number]=128"The multipart rules are in Conventions. For larger PDFs, up to 10 MB, send them by URL or with the two-step upload. See Document and address area.
The response is a 201 with the new letter, received and already charged. Note the complete address in to and preview_url, which opens the PDF exactly as it will be printed.
{
"id": "ltr_8Kq2wX4mN7pR",
"object": "letter",
"status": "received",
"to": {
"name": "Destinatário Exemplo",
"street": "Praça da Sé",
"number": "128",
"complement": null,
"neighborhood": "Sé",
"city": "São Paulo",
"state": "SP",
"postal_code": "01001-000"
},
"from": {
"name": "Exemplo Serviços Ltda.",
"street": "Avenida Exemplo",
"number": "1000",
"complement": "Sala 301",
"neighborhood": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"postal_code": "01310-100"
},
"document": { "type": "url", "url": "https://docs.cartaflow.com.br/samples/exemplo-a4.pdf" },
"color": false,
"double_sided": false,
"address_placement": "first_page",
"postal_services": [],
"pages": 1,
"sheets": 1,
"price": {
"currency": "BRL",
"total": 990,
"items": [{ "type": "base", "quantity": 1, "unit_amount": 990, "amount": 990 }]
},
"send_date": null,
"cancellation_reason": null,
"failure_reason": null,
"tracking_code": null,
"preview_url": "https://files.cartaflow.com.br/previews/ltr_8Kq2wX4mN7pR.pdf?expires=1760369400&signature=9c1f2a7d",
"reference": null,
"metadata": {},
"status_transitions": {
"received_at": "2026-10-13T14:20:00Z",
"in_production_at": null,
"printed_at": null,
"posted_at": null,
"canceled_at": null,
"failed_at": null
},
"created_at": "2026-10-13T14:20:00Z"
}Changed your mind?
While the letter is received, cancel it with POST /v1/letters/{letter_id}/cancel. Nothing is printed and the amount goes back to your balance right away. See Letters and statuses.
Follow the status
Retrieve the letter by id with GET /v1/letters/{letter_id}.
curl https://api.cartaflow.com.br/v1/letters/ltr_8Kq2wX4mN7pR \
-H "Authorization: Bearer $CARTAFLOW_API_KEY"The letter moves through in_production, printed and posted. Each step depends on the production queue and on posting, and takes hours. The full lifecycle is in Letters and statuses.
Receive events by webhook
Instead of polling the API, let CartaFlow notify your server. In the dashboard, open Desenvolvedores > Webhooks (Developers > Webhooks), enter an HTTPS URL on your server and pick the events, such as letter.posted. On every status change, CartaFlow POSTs the full letter to that URL.
To check your endpoint, use the Enviar evento de teste (Send test event) button in the dashboard: it sends one delivery of type webhook.test. How to verify the signature and answer is in Webhooks.
Before sending at volume
Before you turn the integration on for all your letters, check that:
- The
cf_live_key is in an environment variable on your server, never in code. - Every
POSTcall sends anIdempotency-Keywith a value from your system, such as the invoice id. See Idempotency. - Your webhook verifies the signature of every event and ignores repeated events.
- Your balance covers the letters. Check
GET /v1/balancebefore large sends. See Pricing and balance. - The account's default sender is right in the dashboard, under Configurações > Empresa (Settings > Company). Correios returns letters it cannot deliver to this address.
- Your system handles errors by
error.code. See Errors.