Letters and statuses
The path of a letter from your request to its handover to Correios, and how to follow each step.
A letter is a document that CartaFlow prints, puts in an envelope and posts through Correios, the Brazilian national postal service, to an address in Brazil. You create a letter with POST /v1/letters, and it moves through a fixed sequence of statuses until it is posted.
The life of a letter
A letter only moves forward. It starts as scheduled when you schedule it with send_date, or straight as received when you send it right away. It then moves to in_production, printed and posted. While it is scheduled or received, it can still be canceled. If something goes wrong in production, it moves to failed and the amount goes back to your balance.
- Scheduled
scheduled- Canceled
canceled
- Canceled
- Received
received- Canceled
canceled - Failed
failed
- Canceled
- In production
in_production- Failed
failed
- Failed
- Printed
printed- Failed
failed
- Failed
- Posted
posted
| Status | What it means | Final | Cancelable |
|---|---|---|---|
scheduledScheduled | The letter was validated and is waiting for its send_date. It has not been charged yet. On that date, at 06:00 Brasília time, CartaFlow debits the balance and the letter moves to received. If the balance does not cover the price, it moves to canceled. It can be canceled at no cost. | No | Yes |
receivedReceived | CartaFlow validated the document and the address, charged the letter to your balance and put it in the production queue. It can still be canceled, and the amount goes back to the balance. | No | Yes |
in_productionIn production | The letter is part of a print batch. From this point on it can no longer be canceled. | No | No |
printedPrinted | The letter was printed, folded and put in its envelope. Posting is the next step. | No | No |
postedPosted | The letter was handed to Correios, the Brazilian postal service. From here on delivery follows the postal service, and CartaFlow receives no further updates about it. A registered letter carries its Correios tracking code in tracking_code. | Yes | No |
canceledCanceled | The letter was canceled before production and nothing was printed. cancellation_reason says why: you canceled it, or the balance did not cover a scheduled letter on its send date. If it had been charged, the amount went back to the balance. | Yes | No |
failedFailed | Something went wrong in production and the letter will not be posted. failure_reason says why. The amount went back to the balance automatically. | Yes | No |
A status never moves backwards. If a letter has to be printed again in production, for example because the paper jammed in the printer, it joins another print batch and keeps its status.
New statuses may appear
Treat a status you do not recognize as a step in progress. CartaFlow may add statuses without changing the API version, but it never changes the meaning of an existing one. See Versioning and compatibility.
When each step happened
The status_transitions field records when the letter reached each status, in UTC. Statuses the letter has not reached are null.
{
"received_at": "2026-10-13T14:20:00Z",
"in_production_at": "2026-10-13T21:00:00Z",
"printed_at": "2026-10-14T10:42:00Z",
"posted_at": "2026-10-14T16:05:00Z",
"canceled_at": null,
"failed_at": null
}received_atis when the letter was charged. For a letter sent right away it equalscreated_at. For a scheduled letter it is the send date.posted_atis when the letter was handed to Correios. It is the last record of a letter that was sent.canceled_atis only set when the letter was canceled, andfailed_atwhen it failed in production.
How to follow a letter
Use webhooks. CartaFlow POSTs to your URL on every status change, and when the letter is changed in the dashboard before production (letter.updated), with the full letter in the body, so you do not need to call the API afterwards.
| Event | When it is sent |
|---|---|
letter.scheduled | A letter was scheduled with send_date. It has not been charged yet. |
letter.received | A letter was charged and joined the production queue: right after creation or, if it was scheduled, on its send date. Covers letters created through the API and the dashboard. |
letter.in_production | The letter joined a print batch and can no longer be canceled. |
letter.printed | The letter was printed and enveloped. |
letter.posted | The letter was handed to Correios. It is the last event of a letter that was sent. |
letter.canceled | The letter was canceled. Check cancellation_reason: requested when you canceled it, insufficient_balance when a scheduled letter could not be charged on its date. |
letter.failed | The letter failed in production and will not be posted. The amount went back to the balance automatically. Check failure_reason. |
letter.updated | The letter was changed in the dashboard before production, without changing status: send date, address, file, variables, printing, Correios services or reference. data carries the letter as it is now. When the change altered the price of a scheduled letter, price has the new amount. |
If you cannot receive webhooks, retrieve the letter with GET /v1/letters/{letter_id}. Production and posting take hours, so polling more than once an hour tells you nothing new. For many letters, use GET /v1/letters with the status filter instead of retrieving them one by one.
curl "https://api.cartaflow.com.br/v1/letters/ltr_8Kq2wX4mN7pR" \
-H "Authorization: Bearer $CARTAFLOW_API_KEY"Canceling
You can cancel a letter with POST /v1/letters/{letter_id}/cancel while it is scheduled or received. Nothing is printed.
- A
receivedletter has already been charged. The amount goes back to your balance right away, as arefundtransaction. - A
scheduledletter has not been charged yet, so there is nothing to refund. - From
in_productionon, the letter is part of a print batch. The API answers 409 with the codeletter_not_cancelable, as it also does for apostedorfailedletter. - Canceling a letter that is already canceled returns the same letter, with no error, so retrying the call is safe.
curl -X POST "https://api.cartaflow.com.br/v1/letters/ltr_8Kq2wX4mN7pR/cancel" \
-H "Authorization: Bearer $CARTAFLOW_API_KEY"Why a letter was canceled
Every canceled letter carries the reason in cancellation_reason. On other letters the field is null.
| Value | What happened |
|---|---|
requested | You canceled it, through the API or the dashboard. |
insufficient_balance | It was a scheduled letter and, on the send date, the balance did not cover the price. Nothing was charged. See Scheduling. |
Changing before production
Through the API, a letter does not change after it is created: to change something, cancel it and create another. In the dashboard, under Envios (Letters), people who can send letters can change one before production:
| Status | What can change |
|---|---|
scheduled | Everything: the send date (or sending it right away, which charges it and moves it to received), address, file, variables, printing, Correios services and reference. No cost; the price is calculated again. |
received | Only the address and the reference, which do not change the price. To change anything else, cancel it (the amount goes back to the balance) and create another. |
in_production and later | Nothing. |
Every change sends the letter.updated event, with the letter as it is now and the same status. Sending a scheduled letter right away sends letter.received.
When production fails
If a letter cannot go out, because the file does not print, because it was damaged and could not be produced again, or because Correios refused it at posting, it moves to failed, a final status, and the amount goes back to your balance automatically, as a refund transaction. You receive the letter.failed event.
The reason is in failure_reason. On other letters the field is null.
| Value | What happened |
|---|---|
unprintable_document | The file passed validation but could not be printed as it is. |
damaged_in_production | The letter was damaged in printing, folding or enveloping and could not be produced again. |
refused_by_carrier | Correios refused the letter at posting. |
other | Another reason. Contact support from the dashboard for the details. |
To send it again, create a new letter. Problems found before the charge, such as a PDF that breaks the rules or a postal code that does not exist, never reach failed: the API answers 422 at creation and nothing is charged.
Correios services
With no choice made, a letter goes as a simple letter ("carta simples"). With the postal_services field you add optional Correios services. Each one costs extra, with its own line in price. See Pricing and balance.
| Value | Service | What changes |
|---|---|---|
registered | Registered letter ("carta registrada") | The letter is posted with registration and gets a Correios tracking code, shown in tracking_code once it is posted. |
return_receipt | Return receipt ("aviso de recebimento", AR) | Correios returns a card signed by whoever received the letter to the sender printed on the envelope. Requires registered. |
restricted_delivery | Restricted delivery ("mão própria", MP) | Correios delivers only to the person named on the envelope. Requires registered. |
{
"to": { "name": "Destinatário Exemplo", "postal_code": "01001-000", "number": "128" },
"document": { "type": "url", "url": "https://arquivos.exemplo.com.br/notificacoes/contrato-5531.pdf" },
"postal_services": ["registered", "return_receipt"],
"reference": "contrato-5531"
}return_receiptandrestricted_deliveryonly work together withregistered. Without it, the API answers 422 withrequires_registeredand nothing is created.- The return receipt goes back to the sender: the address you sent in
fromor, if you did not send one, the account’s default sender. Check that address before using AR. CartaFlow does not receive or scan the receipt. - In
multipart/form-data, repeat the field once per service:postal_services[]=registeredandpostal_services[]=return_receipt. - The letter returns your choice in
postal_services, always as a list:[]for a simple letter.
After posting
Once posted, the letter is with Correios and delivery follows the postal service. CartaFlow receives no further information about it, so posted is the last status of a letter that was sent. Delivery time depends on the postal service level and the distance to the destination, and CartaFlow does not guarantee it.
- Registered letter:
tracking_codecarries the Correios tracking code, such asRA123456785BR, from posting on. Use it to follow the delivery with Correios. - Simple letter: there is no tracking after posting, and
tracking_codeisnull.
When Correios cannot deliver a letter, it goes back to the sender address printed above the recipient. That is the address you sent in from or, if you did not send one, the account’s default sender. See Addresses in Brazil.
CartaFlow is not affiliated with Correios
CartaFlow prints, envelopes and posts letters through Correios, as any Correios customer does. It is not part of Correios.
The Letter object
The main fields of a letter. The full list, with the type and description of every field, is in Retrieve a letter.
| Field | What it holds |
|---|---|
id | Unique identifier, starts with ltr_. |
status | The current step. See the status table above. |
to and from | Recipient and sender, complete, as they are printed. |
document | Where the content came from: url, upload (multipart or POST /v1/uploads) or base64. |
color, double_sided, address_placement | How the letter is printed. See Document and address area. |
postal_services | The Correios services chosen, or [] for a simple letter. |
pages and sheets | Pages in your document and sheets of paper printed. |
price | The price, line by line. See Pricing and balance. |
send_date | The scheduled date, or null when the letter was sent right away. |
cancellation_reason | Why the letter was canceled, or null. |
failure_reason | Why the letter failed in production, or null. |
tracking_code | The Correios tracking code of a registered letter once posted, or null. |
preview_url | Link to the final PDF, exactly as it will be printed. |
reference and metadata | Your own identifiers and data. See Metadata and reference. |
status_transitions | When the letter reached each status. |
created_at | When the letter was created. |