EspañolReferencia de la APICobrosCrear cobro por boleto

Crear cobro por boleto

POST/v1/boleto
Emite un boleto bancario (Brasil) y devuelve el enlace del PDF y la línea digitable.
Habilitación por cuenta

El boleto se habilita por cuenta. Si la tuya aún no está habilitada, la llamada devuelve 400 con "Boleto não está habilitado para esta conta." — contacta al soporte para activarlo. El contrato PIX (POST /v1/pix) no cambia.

Request

Encabezados

EncabezadoObligatorioDescripción
AuthorizationsíBearer <TU_API_KEY>
Content-Typesíapplication/json

Cuerpo

{
  "amountCents": 5000,
  "externalRef": "pedido-123",
  "description": "Pedido 123",
  "dueDate": "2026-09-30",
  "customer": {
    "name": "Maria Silva",
    "email": "maria@ejemplo.com",
    "phone": "11999998888",
    "document": {
      "number": "12345678901",
      "type": "cpf"
    },
    "address": {
      "street": "Rua das Flores",
      "number": "100",
      "complement": "Apto 12",
      "district": "Centro",
      "city": "São Paulo",
      "zipCode": "01310100",
      "state": "SP"
    }
  }
}
CampoTipoOblig.Descripción
amountCentsenterosíMonto en centavos. Mínimo: 100 (R$ 1,00). Se aplican límites por cuenta.
customerobjetosíDatos del pagador (ver abajo).
customer.namestringsíNombre del pagador. Máx. 100 caracteres.
customer.emailstringsíE-mail válido del pagador.
customer.phonestringcondicionalTeléfono con código de área, solo números: 11 dígitos = celular, 10 = fijo. Obligatorio en cuentas que exigen los datos completos del pagador (ver abajo). En las demás es opcional y, si el proveedor rechaza el teléfono, el boleto se emite sin él (no bloquea el cobro).
customer.document.numberstringsíCPF (11 dígitos) o CNPJ (14 dígitos). Se elimina el formato.
customer.document.typestringsí"cpf" o "cnpj" (mayúsculas y espacios normalizados).
customer.addressobjetocondicionalDirección del pagador. Obligatoria en cuentas que exigen los datos completos del pagador; en las demás se ignora.
customer.address.streetstringcondicionalCalle. Máx. 120 caracteres.
customer.address.numberstringnoNúmero. Máx. 20 caracteres.
customer.address.complementstringnoComplemento. Máx. 60 caracteres.
customer.address.districtstringcondicionalBarrio. Máx. 60 caracteres.
customer.address.citystringcondicionalCiudad. Máx. 60 caracteres.
customer.address.zipCodestringcondicionalCEP (código postal brasileño) con 8 dígitos. Se elimina el formato.
customer.address.statestringcondicionalUF de 2 letras, ej.: "SP".
externalRefstringnoIdentificador en tu sistema. Se usa como clave de idempotencia.
descriptionstringnoTexto libre. Máx. 200 caracteres.
dueDatestringnoVencimiento en YYYY-MM-DD, hoy o futuro. Por defecto: hoy + 3 días.
Datos completos del pagador

Algunas cuentas exigen e-mail, teléfono y dirección del pagador para emitir boleto. En ellas, si falta o está mal uno de esos campos, la respuesta es 400 indicando cuál (ej.: "customer.address.zipCode inválido (CEP com 8 dígitos)."), sin crear el cobro, y el vencimiento debe ser hoy o una fecha futura. Recomendamos enviar siempre los datos completos: en las cuentas que no los exigen, la dirección se ignora.

Idempotencia vía externalRef

Reenviar la misma externalRef devuelve el boleto original con "idempotent": true, sin emitir otro. Úsala siempre que sea posible, sobre todo en reintentos por timeout. Si la emisión anterior quedó en verificación, el reenvío responde 400 con "Boleto em verificação..." hasta la confirmación: consulta la transacción indicada en el mensaje y no emitas otro boleto para el mismo pedido mientras tanto. Una vez confirmada la emisión, el mismo reenvío devuelve el boleto (200, "idempotent": true).

Respuesta 201 Created

{
  "success": true,
  "transactionId": "5glmNaIVteNioH6UBNxH",
  "status": "pending",
  "method": "boleto",
  "amountCents": 5000,
  "amount": 50,
  "dueDate": "2026-09-30",
  "bankSlipUrl": "https://southamerica-east1-coufai.cloudfunctions.net/payments-boletoPdf?tid=5glmNaIVteNioH6UBNxH&t=...",
  "identificationField": "46191110000000000000013187639011415640000005000",
  "nossoNumero": "13187639",
  "externalRef": "pedido-123",
  "idempotent": false
}
CampoDescripción
transactionIdID interno de la transacción. Úsalo en GET /v1/transactions/{id}.
statusSiempre "pending" al crear.
bankSlipUrlEnlace público al PDF del boleto, servido por Coufai. Puede enviarse al pagador; no requiere autenticación. Puede venir null justo después de la emisión mientras el banco concluye el registro (normalmente menos de un minuto); consulta la transacción enseguida. En algunas cuentas el boleto se entrega solo con la línea digitable, sin PDF: en ese caso bankSlipUrl queda null de forma definitiva y debes mostrar la identificationField al pagador.
identificationFieldLínea digitable (47 dígitos) para pago por internet banking. Puede venir null por el mismo motivo que bankSlipUrl.
nossoNumeroNúmero del boleto en el banco emisor. Puede venir null, según el banco emisor.
dueDateVencimiento aplicado.
amountCents / amountEco del monto, en centavos y en reales.

Respuesta 200 OK (idempotente)

Cuando externalRef ya fue usada, la respuesta es el cobro original con "idempotent": true.

Ciclo de vida y webhooks

MomentoEstado de la transacciónEvento de webhook
Emisiónpending—
Pago compensado por el banco (normalmente el siguiente día hábil)paidtransaction.paid, con netCents ya descontada la tarifa
Vencido sin pagoexpiredtransaction.expired — trae unpaidFeeCents si su cuenta tiene tarifa por boleto no pagado

El boleto puede pagarse después del vencimiento mientras el banco lo acepte; en ese caso el transaction.paid llega normalmente. El monto acreditado en tu saldo es netCents (monto menos la tarifa de boleto de tu cuenta). El method de la transacción es "boleto" en todos los eventos.

No reconcilies con polling agresivo

Reacciona al transaction.paid y mantén tu saldo a partir de netCents. Coufai reconcilia los pagos con el banco en segundo plano; un pago cuyo aviso se retrasó se acredita de todos modos.

Tarifa por boleto no pagado

Las cuentas con tarifa por boleto no pagado configurada son cobradas cuando el boleto vence sin pago: en el momento del expired, la tarifa se debita del saldo y el evento transaction.expired trae unpaidFeeCents (omitido cuando no hay tarifa). Reglas:

  • Aparece en el extracto y en la API como una línea propia en GET /v1/transactions, con type: "fee", feeKind: "boleto_unpaid" y relatedTransactionId apuntando al boleto.
  • Reversión automática: si el boleto se paga después del vencimiento, la tarifa se devuelve junto con el crédito del pago — una línea type: "fee_reversal" con el mismo relatedTransactionId.
  • El saldo puede quedar negativo por la tarifa. Mientras esté negativo, los retiros se rechazan por saldo insuficiente; el siguiente pago recibido cubre la diferencia.
  • El valor es el contratado para su cuenta — consulte con soporte. Sin tarifa configurada nada cambia: el expired sigue sin efecto financiero.

Errores

HTTPerror
400"amountCents deve ser inteiro ≥ 100 (R$1,00 mínimo)."
400"customer.name obrigatório."
400"customer.document.number e customer.document.type obrigatórios."
400"customer.document.type deve ser 'cpf' ou 'cnpj'. Recebido: '<valor>'."
400"customer.email é obrigatório e deve ser válido."
400"dueDate inválido (use YYYY-MM-DD)."
400"customer.address é obrigatório para emitir boleto nesta conta." — y las variantes por campo (customer.address.zipCode, customer.address.state, customer.phone…): la cuenta exige los datos completos del pagador; corrige el campo indicado.
400"dueDate deve ser hoje ou uma data futura (YYYY-MM-DD)."
400"Boleto em verificação: a emissão não confirmou. NÃO reenvie — consulte a transação <id> em instantes." — la emisión anterior no se confirmó y el boleto puede existir; no emitas otro para el mismo pedido.
400"Dados recusados pelo provedor: <campo> inválido. Corrija e reenvie. (<código>)" — el pagador o el cobro fue rechazado en la validación del proveedor. <campo> indica qué corregir (documento, e-mail, nome = nombre, endereço = dirección, vencimento = vencimiento, valor); <código> es estable, ej. invalid_cpfCnpj.
400"Limite de emissão de cobranças da conta atingido. Não reenvie; fale com o suporte. (issuance_limit)" — la cuenta alcanzó su tope de emisión; reintentar no lo resuelve.
400"Emissão não permitida para esta conta no momento. Não reenvie; fale com o suporte. (account_restricted)" — la cuenta está impedida de emitir por ahora; reintentar no lo resuelve.
400"Boleto não está habilitado para esta conta."
400"Boleto não disponível para esta conta no momento." — habilitación en curso; reintenta más tarde o contacta al soporte.
400"Valor acima do máximo permitido (R$ X.YY)."
401"API key inválida ou revogada."
403"Conta não está aprovada para uso da API."
429"Limite de requisições atingido. Tente em 1 hora."
500"Erro ao gerar boleto. Tente novamente."
503"Boleto temporariamente indisponível. Tente novamente em instantes." — falla pasajera: no se emitió nada, puedes reintentar.

Los mensajes de error se devuelven en portugués.

Ejemplos

curl -X POST https://api.coufai.com.br/v1/boleto \
  -H "Authorization: Bearer $COUFAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amountCents": 5000,
    "externalRef": "pedido-123",
    "description": "Pedido 123",
    "dueDate": "2026-09-30",
    "customer": {
      "name": "Maria Silva",
      "email": "maria@ejemplo.com",
      "phone": "11999998888",
      "document": { "number": "12345678901", "type": "cpf" },
      "address": {
        "street": "Rua das Flores", "number": "100", "district": "Centro",
        "city": "São Paulo", "zipCode": "01310100", "state": "SP"
      }
    }
  }'