EspañolRegistro de cambios

Registro de cambios

Cambios relevantes de la API pública de Coufai.

2026-10-20 — Boleto: entrega solo con la línea digitable

  • En algunas cuentas el boleto se entrega solo con la línea digitable, sin PDF: bankSlipUrl queda null de forma definitiva — muestra la identificationField al pagador.
  • Una vez confirmada una emisión en verificación, el reenvío con la misma externalRef devuelve el boleto (200, "idempotent": true).

Ver Crear cobro por boleto.

2026-10-15 — Boleto: datos completos del pagador y emisión en verificación

  • Nuevo objeto opcional customer.address (street, number, complement, district, city, zipCode, state) en POST /v1/boleto. Algunas cuentas pasan a exigir e-mail, teléfono y dirección del pagador: en ellas, si falta o está mal uno de esos campos, la respuesta es 400 indicando cuál, sin crear el cobro, y el vencimiento debe ser hoy o una fecha futura. En las demás cuentas la dirección se ignora — recomendamos enviarla siempre.
  • Justo después de la emisión, bankSlipUrl e identificationField pueden venir null mientras el banco concluye el registro (normalmente menos de un minuto); consulta la transacción enseguida.
  • Emisión en verificación: cuando la emisión no se confirma, la API responde 400 con "Boleto em verificação: a emissão não confirmou. NÃO reenvie — consulte a transação <id> em instantes.". Reenviar la misma externalRef repite esa respuesta hasta la confirmación, en lugar de emitir un segundo boleto.
  • Una falla pasajera en la que no se emitió nada pasa a responder 503 con "Boleto temporariamente indisponível. Tente novamente em instantes." — puedes reintentar. Un error inesperado sigue siendo 500 (la documentación decía 502 por error).

Vea Crear cobro por boleto.

2026-09-23 — Boleto: rechazo de la cuenta separado del rechazo de datos

  • Cuando el rechazo es de la cuenta (tope de emisión alcanzado o emisión bloqueada), la API deja de responder "Dados recusados pelo provedor: dados da cobrança inválidos. Corrija e reenvie. (invalid_action)" y pasa a responder 400 con "Limite de emissão de cobranças da conta atingido. Não reenvie; fale com o suporte. (issuance_limit)" o "Emissão não permitida para esta conta no momento. Não reenvie; fale com o suporte. (account_restricted)".
  • Con estos dos códigos, reintentar no lo resuelve: no reintente en bucle, contacte a soporte. Los rechazos de datos (invalid_cpfCnpj, invalid_dueDate…) siguen iguales.

2026-09-12 — Tarifa por boleto no pagado

  • Las cuentas con tarifa por boleto no pagado pasan a ser cobradas cuando un boleto vence sin pago. El transaction.expired de un boleto trae el nuevo campo opcional unpaidFeeCents (omitido cuando no hay tarifa), y el cobro aparece en GET /v1/transactions como una línea type: "fee" (feeKind: "boleto_unpaid", relatedTransactionId = el boleto).
  • Un boleto pagado después del vencimiento devuelve la tarifa automáticamente (línea type: "fee_reversal").
  • El saldo puede quedar negativo por la tarifa; los retiros se rechazan por saldo insuficiente hasta que el siguiente crédito lo cubra.
  • GET /v1/transactions acepta ?type=fee y ?type=fee_reversal en el filtro.

Vea Crear cobro por boleto.

2026-09-12 — Boleto: rechazo de datos ahora es un 400 claro; el teléfono nunca bloquea

  • Cuando el proveedor rechaza un dato del pagador o del cobro, la API responde 400 con "Dados recusados pelo provedor: <campo> inválido. Corrija e reenvie. (<código>)" (antes era un 500 genérico). <campo> indica qué corregir y <código> es estable (ej. invalid_cpfCnpj): use el código en su lógica, no el texto.
  • customer.phone es opcional: 11 dígitos = celular, 10 = fijo. Si el proveedor rechaza el teléfono, el boleto se emite sin él; un teléfono inválido ya no hace fallar el cobro.

2026-09-06 — Boleto bancario vía API

Nuevo endpoint POST /v1/boleto: emite un boleto bancario (Brasil) y devuelve el enlace público del PDF (bankSlipUrl, servido por Coufai) y la línea digitable (identificationField). Vencimiento opcional (dueDate, por defecto hoy + 3 días); misma idempotencia por externalRef que PIX.

  • Mismo ciclo que los cobros PIX: pending → paid (transaction.paid, con netCents ya descontada la tarifa de boleto de la cuenta) o expired (transaction.expired) tras el vencimiento. El campo method viene como "boleto".
  • Habilitado por cuenta: sin habilitación, la llamada devuelve 400 "Boleto não está habilitado para esta conta.". Contacta al soporte.
  • Los pagos cuyo aviso se retrasó se reconcilian con el banco en segundo plano y se acreditan de todos modos — reacciona al webhook en lugar de hacer polling agresivo.

Ver Crear cobro por boleto.

2026-08-27 — Motivo del rechazo en retiros

Los retiros rechazados ahora informan por qué fallaron. Los eventos transfer.failed, transfer.rejected y transfer.cancelled — y GET /v1/transactions/{id} — pasan a traer dos campos opcionales:

  • refusedCode — código estable en mayúsculas (ej.: INSUFFICIENT_BALANCE, DAILY_LIMIT_EXCEEDED, INVALID_PIX_KEY). Es el campo para tu lógica: no parsees el texto.
  • refusedReason — descripción legible, hoy devuelta en portugués y sujeta a cambios.

Antes, un retiro rechazado quedaba en failed sin ninguna indicación de causa, y la única salida era reintentar a ciegas. Con el código puedes distinguir lo que vale la pena reintentar (INSUFFICIENT_BALANCE, en minutos) de lo que no sirve sin corrección (INVALID_PIX_KEY) o solo al día siguiente (DAILY_LIMIT_EXCEEDED).

Compatible con lo existente: los campos se omiten cuando el motivo no está disponible, y ningún campo cambió de nombre o tipo. Trata la ausencia como “motivo no informado” — nunca como éxito — y un código desconocido como rechazo genérico, ya que la lista puede crecer.

Un retiro rechazado no se debita: si el importe llegó a salir de tu saldo, se devuelve antes de enviar el evento.

Consulta Motivo del rechazo.

2026-06-04 — Registro de destinatario tercero vía API

Nuevo endpoint POST /v1/recipients para enviar destinatarios terceros (CPF/CNPJ distinto del titular) directamente por la API — antes, el registro solo se hacía por soporte.

  • El registro crea al destinatario como pending_review: no aprueba y no libera retiros. La aprobación (KYC/AML) sigue haciéndola Coufai. Solo tras activarse, el destinatario aparece en GET /v1/recipients y puede usarse en POST /v1/withdraw vía recipientId.
  • Idempotente por documento: reenviar el mismo CPF/CNPJ devuelve el mismo recipientId (sin duplicar); un destinatario ya aprobado no se rebaja.
  • Name-match: la clave cpf/cnpj debe coincidir con el document. Límite por defecto de 10 destinatarios (activos + en análisis) por cuenta — ajustable por cuenta vía soporte.

Ver Registrar destinatario.

2026-06-02 — Reintento automático de webhook

Las entregas de webhook que fallan por error transiente (timeout, error de red, HTTP 5xx, 429, 408) ahora se reentregan automáticamente con backoff: 1min → 5min → 30min → 2h → 6h (hasta 6 intentos, ~9h). Antes había un único intento automático — la recuperación dependía de polling o reenvío manual.

  • 4xx no se reintenta (400/401/403/422…): significa que el endpoint rechazó el evento; martillar no ayudaría. Corrige la causa y usa el reenvío manual.
  • eventId se preserva entre intentos — deduplicar por X-Coufai-Event-Id es obligatorio. El timestamp y la X-Coufai-Signature se regeneran en cada intento; el eventId no.
  • Con esto, se recomienda un flujo event-driven: reacciona al webhook + mantén tu saldo desde netCents + concilia ligero, sin polling agresivo de GET /v1/balance.

Cambio de comportamiento, no de contrato: ningún campo nuevo, ninguna acción necesaria para quien ya deduplica por eventId. Ver Reintento e idempotencia.

2026-05-27 — Normalización automática de document.type y pixKeyType + errores más informativos

Cambio aditivo: las solicitudes que ya envían valores correctos siguen funcionando sin ajustes. Lo que cambia es cómo manejamos variaciones y cómo reportamos errores.

Normalización automática (ley de Postel) en campos enum:

  • customer.document.type en POST /v1/pix: acepta "CNPJ", "Cpf", " cnpj " etc. — las variaciones de case y whitespace se normalizan internamente a minúsculas. Antes, cualquier no-minúscula se convertía silenciosamente en "cpf" en la sanitización, haciendo que el proveedor rechazara números con tamaño incompatible y generando un 502 Erro ao comunicar com gateway confuso.
  • pixKeyType en POST /v1/withdraw: misma normalización — "CPF", "Email", " evp " aceptados.

Validación de tamaño del documento: customer.document.number debe tener 11 dígitos cuando type=cpf o 14 dígitos cuando type=cnpj (tras quitar puntuación). Antes, números con tamaño incompatible eran aceptados por Coufai y rechazados por el PSP.

Errores más informativos — todos los mensajes de 400 por validación de payload incluyen ahora el valor recibido (truncado) y lo esperado:

// Antes
{ "error": "Erro ao comunicar com gateway. Tente novamente." }
// status: 502
 
// Ahora
{ "error": "customer.document.type deve ser 'cpf' ou 'cnpj'. Recebido: 'CNPJ '." }
// status: 400

Recomendación: enviar valores en minúsculas sin whitespace es el estándar limpio, pero la API ya no lo exige.

Ver Crear cobro, Iniciar retiro y Errores.

2026-05-09 — Retiro a terceros pre-aprobados vía API

POST /v1/withdraw ganó soporte al campo opcional recipientId para retirar a destinatarios terceros (CPF/CNPJ que no es el titular de la cuenta).

  • Sin recipientId (por defecto): comportamiento anterior — solo CPF/CNPJ del titular registrado, otros tipos rechazados.
  • Con recipientId: libera los 5 tipos de clave PIX (cpf, cnpj, email, phone, evp) para un destinatario pre-aprobado por el equipo Coufai (compliance/AML).

La aprobación del recipientId es manual — contacta a soporte con el nombre legal y CPF/CNPJ del destinatario. No hay endpoint de auto-registro.

Nuevo endpoint GET /v1/recipients devuelve la lista de destinatarios activos pre-aprobados para tu cuenta con los respectivos recipientId — úsalos en POST /v1/withdraw. Ver Listar destinatarios.

Defensas actuales inalteradas: API key, IP allowlist, rate limit, anti-burst, caps diario/mensual, validación de saldo, idempotencia vía externalRef. Cada retiro a tercero dispara alerta interna en Coufai (auditoría continua).

Ver Iniciar retiro para schema completo, restricciones por tipo de clave y ejemplos.

2026-05-09 — Datos del PIX en webhooks y consulta REST

Nuevos campos opcionales entregados cuando la transacción fue efectivamente liquidada en la red SPI. Cambio aditivo — los clientes que no los usan los ignoran, sin adaptación.

  • Webhooks transaction.paid, transaction.refunded, transaction.partially_refunded ahora traen:

    • endToEndId — E2E ID estándar BACEN del PIX (formato E<ISPB><YYYYMMDDhhmm><id>).
    • payer — quién pagó: { name, document, documentType, bankAccount: { ispb, branch, account } }.
  • Webhooks transfer.paid, transfer.refunded, transfer.partially_refunded ahora traen:

    • endToEndId — E2E ID del PIX OUT.
    • beneficiary — quién recibió el retiro: misma estructura de payer.
    • refundEndToEndId — solo en devoluciones (transfer.refunded / transfer.partially_refunded): E2E ID del PIX de devolución, distinto del PIX OUT original.
  • REST GET /v1/transactions/:id y GET /v1/transactions devuelven los mismos campos cuando aplica. El polling pasa a ser fallback equivalente al webhook.

Eventos sin liquidación (transaction.failed, cancelled, expired, transfer.created, processing, pending_approval, approved, rejected, failed, cancelled) no traen esos campos — se omiten del JSON, no vienen como null.

Nota LGPD: payer.* y beneficiary.* contienen datos personales (nombre, CPF/CNPJ, cuenta bancaria). Tú eres el controlador del tratamiento posterior. Ver Eventos.

2026-05-08 — Multi-webhook + historial de entregas

  • Múltiples URLs de webhook por cuenta (hasta 6). Cada URL filtra eventos por nombre canónico o por categoría semántica (cashin, cashout, refund, dispute, chargeback). Ver Configurando webhook.
  • Catálogo de eventos canónicos estandarizado: 21 eventos transaction.* y transfer.*, con nombres estables en X-Coufai-Event y en el campo event del cuerpo. Ver Eventos.
  • Eventos nuevos entregados por primera vez: transaction.partially_refunded, transaction.infraction, transaction.dispute, transaction.protest, transaction.blocked, transaction.chargeback, transfer.created, transfer.processing, transfer.partially_refunded.
  • Header nuevo: X-Coufai-Event-Id con un identificador único por evento. Igual entre URLs del fanout y en reenvíos manuales — úsalo para dedup.
  • Historial de entregas en el panel (60 días de retención): timeline de cada intento con payload exacto, HTTP status y duración. Disponible en la transacción o en la lista global.
  • Reenvío manual de webhook: botón para disparar un nuevo intento de una entrega histórica. eventId se preserva (idempotencia), timestamp y signature se regeneran. Límite 10/h por cuenta. Ver Reenvío manual.

Rupturas de naming (notar si integraste antes de mayo/2026)

Los siguientes nombres fueron reemplazados por los canónicos:

Nombre antiguoNombre canónico actual
TRANSFER_PAIDtransfer.paid
transaction.refunded.partialtransaction.partially_refunded
transfer.refunded.partialtransfer.partially_refunded
chargeback.appliedtransaction.chargeback
med.openedtransaction.infraction
dispute.openedtransaction.dispute
funds.blockedtransaction.blocked
protest.openedtransaction.protest

Si comparabas por el nombre antiguo, actualízalo. El status en el cuerpo (campo estable) sigue igual.

2026-05-08 — 2FA en el panel (sin impacto en la API)

  • Los comerciantes ahora pueden activar autenticación en dos pasos (TOTP) en el panel, en Configuración → Seguridad.
  • Sin cambios en el contrato de la API REST. Las API keys siguen funcionando exactamente como antes — sin código adicional, sin header nuevo, sin cambios en solicitudes o respuestas.
  • Los retiros iniciados desde el panel pasan a exigir código TOTP en el momento de la operación. Los retiros vía POST /v1/withdraw quedan inalterados (autenticación por API key + IP allowlist + rate limit).

2026-05 — Lanzamiento de la documentación pública

  • Versión inicial cubriendo:
    • POST /v1/pix — creación de cobro PIX.
    • POST /v1/withdraw — retiro PIX.
    • GET /v1/balance — saldo.
    • GET /v1/transactions y GET /v1/transactions/:id — listado y consulta.
    • Webhook único entregado al endpoint configurado por el comerciante, firmado por HMAC.

[POR CONFIRMAR CON EL EQUIPO COUFAI]: alinear la política de versionado de la API (semver, breaking changes, plazos de deprecación) y el formato definitivo de este changelog.