Boleto Issuance Guide

Temporarily unavailable

Boleto issuance is temporarily disabled. Expected return on 30 October 2026.

Issue a boleto on behalf of the account. The account is the beneficiary and a third party pays. The other direction, paying someone else’s boleto, is Boleto Pay.

The boleto_charge feature starts disabled for every tenant, including tenants created after this release. While it is off, every route under /v1/accounts/{accountId}/boleto/charge returns 403 feature_disabled. CorpX turns it on from the admin panel. Turning it off later does not cancel a boleto already issued.

Credential scope: boleto_charge.manage. This is money in, not cashout. Reads also accept read. Single issuance and batches are BaaS operations; Internet Banking credentials remain read-only for boleto charges.

Register the beneficiary

The holder must be a beneficiary at the settlement bank before the first issuance. Registration uses the account’s own data (name, tax id and, when the holder has more than one active account, branch and account). Do not send a body. Any JSON other than empty or {} returns 400 invalid_payload.

POST /v1/accounts/{accountId}/boleto/charge/beneficiary
SituationHTTPWhat happens
No beneficiary, or a closed relationship202 boleto_charge_beneficiary_pendingThe facade registers and stops. Header Retry-After: 300. Nothing was issued.
Already PENDING202, same codeNo second registration.
ELIGIBLE with relationship ACTIVE200Returns the beneficiary. Does not register again.
INELIGIBLE or UNDER_REVIEW422 boleto_charge_beneficiary_ineligibleThe message carries the status.
The settlement bank rejects synchronously502 boleto_charge_beneficiary_registration_failedThe message carries the bank’s description. Nothing was issued.
GET /v1/accounts/{accountId}/boleto/charge/beneficiary

Live read. 404 boleto_charge_beneficiary_not_found when the account was never registered. 422 boleto_charge_account_holder_not_found when the settlement bank does not recognize the holder in the boleto module: that is not a missing beneficiary.

FieldValues
statusELIGIBLE, PENDING, UNDER_REVIEW, INELIGIBLE
relationshipStatusACTIVE or CLOSED
relationshipActivetrue when the relationship is active
PATCH /v1/accounts/{accountId}/boleto/charge/beneficiary

Updates registration data. The facade resolves the beneficiary from the account; do not send the id. A person account accepts name and socialName. A company account accepts legalName and tradeName. Email, phone and representative apply to both. At least one field. 202 returns operationId.

PATCH /v1/accounts/{accountId}/boleto/charge/beneficiary/status

targetStatus is ELIGIBLE, UNDER_REVIEW or INELIGIBLE. Transitions: eligible goes to under review or ineligible; under review goes to eligible or ineligible; ineligible only returns to under review. Anything else is 409 boleto_charge_beneficiary_transition_invalid and is not sent to the bank. reason is required. documentId is required when the target is INELIGIBLE.

DELETE /v1/accounts/{accountId}/boleto/charge/beneficiary/relationship

Closes the relationship. After confirmation, the account cannot issue new boletos. Register again to come back. reason is required. terminationDate is optional, YYYY-MM-DD, today or later in Brasília time. 202 carries operationId.

GET /v1/boleto/charge/beneficiaries

Lists beneficiaries from the settlement bank and keeps only tax numbers of the tenant’s accounts. The page is the bank’s page, so it can come back shorter. hasNext means the bank has another page.

The first POST /boleto/charge also registers on its own, with the same 202, if you have not called registration first.

Monthly quotas

Two quotas are enforced together in the Brasília calendar month (America/Sao_Paulo):

QuotaDefaultConfiguration
Tenant100 issuances/monthboletoCharge.maxIssuesPerMonth in the tenant policy
PF account5 issuances/monthaccount policy
PJ account10 issuances/monthaccount policy

There is no unlimited mode. The effective balance is always the lower of tenant and account. Read both with:

GET /v1/accounts/{accountId}/boleto/charge/quota

The response carries periodStart, resetsAt, effectiveRemaining, and tenant / account blocks (limit, used, remaining, source). account.personType is PF or PJ; source says whether the limit came from default, tenant, or account.

An issuance consumes quota only after the beneficiary has been validated and the operation admitted. An idempotent repeat of the same boleto, a pending/ineligible beneficiary, and a quota denial do not count. Once admitted, the slot is permanent: a later settlement-bank failure is not refunded.

Issue

POST /v1/accounts/{accountId}/boleto/charge

identifier is required in the JSON body and is this operation’s only idempotency key. The Idempotency-Key header does not replace it. Repeating the same boleto returns 200 without issuing another or consuming quota again.

The 202 carries chargeId (the settlement bank’s id), barcode, digitableLine, ourNumber and registrationCondition: PENDING. Registration at the central base arrives later as boleto.charge.registered or boleto.charge.registration_failed.

Request fields

Money is a number in BRL with two decimal places. Never cents, never a string.

FieldRequiredMeaning
identifierYesIdempotency for this account. Unique per account; there is no header fallback.
dueDateYesDue date, YYYY-MM-DD. Not before issueDate.
issueDateNoIssue date, YYYY-MM-DD.
amount.nominalYesFace amount, greater than zero.
amount.currencyNoBRL.
payer.nameYesPayer name.
payer.personTypeYesperson or company.
payer.taxNumberYesCPF when person, CNPJ when company. Digits only.
payer.emailNoPayer email.
payer.addressStreet, addressNumber, addressComplement, addressDistrict, addressCity, addressState, addressZipCodeNoPayer address. ZIP is 8 digits.
interestNoInterest after the due date. startDate must be after dueDate.
fineNoFine after the due date. Same startDate rule.
discountNoEarly-payment discount. Each groups[].limitDate must be before dueDate.
abatement.amountNoFixed abatement, not negative. Together with discounts it must not reach amount.nominal.
allowPartialPaymentNoAccepts a payment below the amount.
allowDivergentPaymentNoAccepts an amount other than face value, within minPaymentAmount and maxPaymentAmount.
divergentPaymentModeNoanyValue, minMax or minOnly.
expirationPolicy.allowPaymentAfterDueDateNoWhether the boleto can be paid after the due date.
expirationPolicy.paymentLimitDateNoLast day payment is accepted, YYYY-MM-DD, not before dueDate. This is how you bound validity: there is no write-off of an open boleto.
expirationPolicy.blockPaymentAfterLimitNoRejects payment after paymentLimitDate.
expirationPolicy.decursoGraceDaysNoGrace days after the limit.

interest and fine share the charge shape: calculationModel, typeId, typeKey, value and startDate. discount.groups[] carries sequence, typeId, typeKey, value and limitDate. A typeKey containing percentage treats value as a percent of face value; otherwise value is BRL.

A charge that disagrees with its model returns 422 boleto_charge_rules_inconsistent. A bad discount date returns 422 boleto_charge_discount_invalid. Abatement plus discounts that zero the boleto return 422 boleto_charge_reductions_exceed_amount. An identifier already active returns 409 boleto_charge_identifier_in_use. A barcode already active returns 409 boleto_charge_barcode_active.

Response fields

FieldMeaning
chargeIdSettlement-bank id. This is the path id and the id in webhooks.
identifierYours.
barcode44-digit barcode, once the bank has returned it.
digitableLineDigitable line.
ourNumberOur number.
statusOPEN, SETTLED, OVERDUE, WRITTEN_OFF, CANCELLED, EXPIRED. OVERDUE does not close the boleto.
statusReasonWhen the bank sends one: termExpiration, settlement, manualWriteOff, cancellation, other.
registrationConditionPENDING (central base), REGISTERED, REJECTED.
dueDate, paymentLimitDateYYYY-MM-DD.
amount.nominalFace amount.
amount.updatedAmountUpdated amount, once the bank has applied interest, fine and discount.
amount.currencyBRL.
payer.name, payer.taxNumber, payer.personTypeWho must pay.
payment.amount, payment.settledAtSet when the boleto was settled.

Consult

GET /v1/accounts/{accountId}/boleto/charge
GET /v1/accounts/{accountId}/boleto/charge/{chargeId}

The two reads have different guarantees:

  • Local list. The first GET pages through the Postgres mirror and does not call MT. Every item has updatedAt, the instant of the last successful synchronization. Filters: barcode (44 digits), payer (name or tax id), dueDate, status, page (minimum 1), and pageSize (1 to 200, default 50). The response carries items, page, pageSize, totalItems, totalPages, and hasNext.
  • Always-live detail. The GET with {chargeId} calls MT every time, updates the mirror, and returns the current result with the new updatedAt. If MT fails, the API returns an error; it never serves an old cached result.

There is no historical backfill. The list starts with new v2.85.0 issuances. An older boleto enters the mirror after a successful detail lookup.

Issue in batches

Batch routes are BaaS-only and require boleto_charge.manage.

  1. POST /v1/accounts/{accountId}/boleto/charge/batches creates an empty DRAFT. It requires Idempotency-Key; the optional JSON body accepts name (up to 120 characters).
  2. PUT .../batches/{batchId}/items adds 1–50 JSON requests per call.
  3. DELETE .../batches/{batchId}/items/{itemId} removes an item while the batch is still DRAFT.
  4. POST .../batches/{batchId}/dispatch reserves quota and starts asynchronous processing.
  5. GET .../batches/{batchId}?page=1&pageSize=50 follows statistics and per-item results. GET .../batches lists the account’s batches.

Every item uses exactly the single-issuance body, including the required identifier. A chunk upload is atomic: if one item is invalid or conflicts, none of the 1–50 is stored. A batch accepts at most 5,000 items. boleto_charge_batch_chunk_invalid means the chunk is outside the 1–50 range; boleto_charge_batch_too_large means the batch would exceed 5,000; and boleto_charge_batch_item_conflict means the same identifier has another payload in the batch.

Only DRAFT is editable. States are:

DRAFT → QUEUED → PROCESSING → COMPLETED | PARTIAL_FAILED | FAILED

Dispatch is atomic too: every item must fit both the tenant and account remaining balances at once. If either balance is too small, the API returns 429 boleto_charge_monthly_limit_exceeded, reserves nothing, starts no issuance, and leaves the batch in DRAFT. Once the batch is admitted, the full reservation is consumed without refund, including items that later fail at the settlement bank.

Dispatch creates billable issuances

Each boleto sent for issuance may incur charges under the commercial terms agreed with CorpX. Review the items and quota before dispatch. The Portal asks for explicit confirmation on screen; that is a UI safeguard, not an API field.

Batch statistics

statistics.generation tracks total, pending, processing, issued, failed, and progressPercentage. A batch finishes when all issuance attempts finish; registration, payment, and expiration continue on each individual boleto.

statistics.payment tracks issued, paid, unpaid, overdue, paidAmount, and conversionRate. Conversion is:

paid / issued × 100

Failed issuance items are excluded from the denominator. Payment statistics therefore keep changing after the batch reaches a terminal state.

When all attempts finish, the API sends boleto.charge.batch.completed with the final state and a generation summary (total, issued, failed). Read the batch for current payment conversion. With an HMAC subscription, validate X-Signature over the raw bytes and deduplicate on the envelope id.

Lifecycle

  1. Beneficiary registration, if there is none yet (202, a few minutes).
  2. Issuance (202). The boleto starts OPEN with registration PENDING.
  3. boleto.charge.registered or boleto.charge.registration_failed.
  4. Then boleto.charge.settled (also a BOLETO line on the statement), overdue, written_off, cancelled or expired.

boleto.charge.failed is in the catalog and is not sent in this version.

There is no way to write off an open boleto. The settlement bank does not offer that yet; DELETE exists in the code, returns 501, and is outside this contract. When validity must be predictable, send expirationPolicy.paymentLimitDate at issuance.