Cobros (pay-ins)
Un cobro es dinero que entra. Hay cuatro métodos, en dos familias:
debit_otp: el banco del pagador le manda un código a su usuario; su usuario lo teclea en la pantalla de usted; usted lo confirma. Tres pasos con una persona en el medio.direct_debit: débito domiciliado, con unmandate(contrato) que el pagador firmó con su banco. Un solo paso.incoming_mobile_paymenteincoming_transfer: el pagador manda el dinero por su cuenta (un Pago Móvil o una transferencia desde su banco) a la cuenta receptora; usted lo espera con un cobro creado antes.
Cobro recibido, paso a paso
Sección titulada «Cobro recibido, paso a paso»POST /v2/payinsconmethod: incoming_mobile_payment(oincoming_transfer), elamountexacto y enpayerquién va a pagar: documento, banco y, en Pago Móvil, teléfono.201constatus: pendingypending_reason: awaiting_payment;expires_atdice hasta cuándo se espera (48 h por omisión;expires_in_hoursla acorta, nunca más de 72).- Muéstrele a su pagador la cuenta receptora:
GET /v2/capabilitiesla trae enreceiving_accountde cada métodoincoming_*(banco, teléfono o cuenta, documento y titular). - Su pagador paga desde su banco. Cuando llega un pago que coincide en
todo (monto exacto, banco emisor y teléfono o documento del pagador,
posterior a la creación del cobro y dentro de la ventana), el cobro pasa
a
confirmedconbank_referenceyconfirmed_at, su saldo sube por el neto y le llegapayin.confirmed. - Si la ventana vence sin pago:
failed/expiredypayin.failed. Mientras espera, puede cancelarlo conPOST /v2/payins/{id}/cancel.
Un pago que llega sin un cobro que lo espere no se le atribuye a nadie: cree el cobro antes de pedirle a su pagador que pague. La comisión por un Pago Móvil recibido se descuenta del monto (el saldo sube por el neto); la transferencia recibida no tiene comisión.
Cobro con código, paso a paso
Sección titulada «Cobro con código, paso a paso»POST /v2/payinsconmethod: debit_otp→201constatus: pendingypending_reason: awaiting_code. El banco le manda el código al pagador.code_expires_atdice hasta cuándo vale.- Su usuario teclea el código en su pantalla.
POST /v2/payins/{id}/confirmcon{"code": "…"}→ el débito se ejecuta.confirmedtraebank_reference;failedtraefailure.code.
El código tiene un solo intento. Uno equivocado falla la operación con
failure.code: code_rejected: cree otro cobro, o pida otro código con
POST /v2/payins/{id}/resend-code antes de confirmar (el anterior deja de
servir; tope de cinco por operación). El código no se guarda nunca.
Si el resultado no llegó en el momento, la operación queda pending /
processing: la API la resuelve sola y le avisa por webhook.
El concepto
Sección titulada «El concepto»Lo que el pagador ve en su banco es concept, por ejemplo «Cobro TCP7K2M9Q»:
lo pone el sistema con el prefijo de su empresa y seis caracteres del id de
la operación. Su reference no viaja al banco.
Crear un cobro
Sección titulada «Crear un cobro»/v2/payinsAlcancepayins:crear
Crea un cobro. Con method: debit_otp, el banco de su usuario le manda un código y la operación queda pending / awaiting_code hasta que usted la confirme.
Con method: incoming_mobile_payment o incoming_transfer usted declara el monto y quién va a pagar, y la operación queda pending / awaiting_payment hasta que ese pago llegue a la cuenta receptora (la que GET /v2/capabilities muestra en receiving_account) o venza la ventana (expires_at; expires_in_hours la acorta, hasta 72 h). Cuando llega un pago que coincide en monto, banco y pagador, la operación pasa a confirmed con bank_reference y le llega payin.confirmed; si vence, failed / expired. Mientras espera, puede cancelarla con POST /v2/payins/{id}/cancel.
Requiere el alcance payins:crear.
Los campos de payer son los que el método declara en GET /v2/capabilities; se validan contra esa misma declaración y un campo que el método no declara se rechaza (details[].code = unknown).
Reintentar este POST es seguro con la misma Idempotency-Key: devuelve el mismo cobro y no pide otro código.
| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
Idempotency-Key |
header | sí | Una clave única por pedido, de 8 a 128 caracteres. La misma clave devuelve la misma operación. |
| Código | Significa |
|---|---|
201 |
El cobro existe. status dice qué pasó: con awaiting_code su usuario tiene que confirmarlo; con awaiting_payment se espera el pago hasta expires_at. |
400 |
El pedido no pasó la validación. details nombra cada campo. Códigos: amount_out_of_range, bank_unsupported, body_invalid, country_unsupported, currency_unsupported, field_invalid, field_required, idempotency_key_required, method_unavailable. |
401 |
La llave falta o no es válida. Códigos: unauthorized. |
403 |
La llave es válida pero no tiene el alcance. Códigos: scope_missing. |
409 |
La misma Idempotency-Key con otro cuerpo. Códigos: idempotency_key_reused. |
503 |
No se pudo procesar ahora. Reintente más tarde con la misma Idempotency-Key. Códigos: temporarily_unavailable. |
Consultar un cobro
Sección titulada «Consultar un cobro»/v2/payins/{id}Alcanceoperaciones:leer
El estado actual. Requiere el alcance operaciones:leer.
| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
id |
path | sí | El id de la operación. |
| Código | Significa |
|---|---|
200 |
El cobro. |
401 |
La llave falta o no es válida. Códigos: unauthorized. |
403 |
La llave es válida pero no tiene el alcance. Códigos: scope_missing. |
404 |
No hay una operación con ese identificador en su empresa. Códigos: not_found. |
Cancelar un cobro que espera el pago
Sección titulada «Cancelar un cobro que espera el pago»/v2/payins/{id}/cancelAlcancepayins:crear
Sólo para un cobro recibido (incoming_mobile_payment / incoming_transfer) que todavía está pending / awaiting_payment. Queda failed con failure.code = cancelled y le llega payin.failed. Un pago que llegue después ya no se le atribuye.
Cualquier otro cobro, o uno que ya terminó, 409 not_cancellable.
Requiere el alcance payins:crear.
| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
id |
path | sí | El id de la operación. |
| Código | Significa |
|---|---|
200 |
El cobro, cancelado. |
401 |
La llave falta o no es válida. Códigos: unauthorized. |
403 |
La llave es válida pero no tiene el alcance. Códigos: scope_missing. |
404 |
No hay una operación con ese identificador en su empresa. Códigos: not_found. |
409 |
El cobro no está esperando un pago. Códigos: not_cancellable. |
Confirmar con el código
Sección titulada «Confirmar con el código»/v2/payins/{id}/confirmAlcancepayins:crear
Ejecuta el débito con el código que tecleó su usuario. El código no se guarda.
confirmed trae bank_reference. failed con code_rejected es un código equivocado o vencido: pida otro con POST /v2/payins/{id}/resend-code sobre una operación nueva, porque un cobro fallido es final.
Requiere el alcance payins:crear.
| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
id |
path | sí | El id de la operación. |
| Código | Significa |
|---|---|
200 |
El resultado del débito. |
400 |
Falta el código o el cuerpo no se pudo leer. Códigos: body_invalid, field_required. |
401 |
La llave falta o no es válida. Códigos: unauthorized. |
403 |
La llave es válida pero no tiene el alcance. Códigos: scope_missing. |
404 |
No hay una operación con ese identificador en su empresa. Códigos: not_found. |
409 |
La operación no está esperando un código. Códigos: code_not_expected. |
503 |
No se pudo procesar ahora. Reintente más tarde con la misma Idempotency-Key. Códigos: temporarily_unavailable. |
Pedir otro código
Sección titulada «Pedir otro código»/v2/payins/{id}/resend-codeAlcancepayins:crear
Para cuando el mensaje no llegó. Tiene un tope por operación. Requiere el alcance payins:crear.
| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
id |
path | sí | El id de la operación. |
| Código | Significa |
|---|---|
200 |
La operación, con el nuevo vencimiento del código. |
401 |
La llave falta o no es válida. Códigos: unauthorized. |
403 |
La llave es válida pero no tiene el alcance. Códigos: scope_missing. |
404 |
No hay una operación con ese identificador en su empresa. Códigos: not_found. |
409 |
La operación no está esperando un código, o ya se pidieron demasiados. Códigos: code_not_expected, too_many_codes. |
503 |
No se pudo procesar ahora. Reintente más tarde con la misma Idempotency-Key. Códigos: temporarily_unavailable. |
Los campos de un cobro
Sección titulada «Los campos de un cobro»| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
amount |
string^[0-9]+(\.[0-9]+)?$ |
sí | Cadena decimal con punto y los decimales de la moneda. Nunca un número JSON. |
country |
string^[A-Za-z]{2}$ |
sí | ISO 3166-1 alfa-2. |
currency |
string^[A-Za-z]{3}$ |
sí | ISO 4217. |
expires_in_hours |
integer |
no | Sólo cobros recibidos: cuántas horas se espera el pago. Sin él, la ventana por omisión (48 h). Nunca más de 72. |
mandate |
Mandato |
no | |
metadata |
object |
no | Lo que usted quiera guardar, hasta 4 KB. Vuelve tal cual. |
method |
CodigoDeMetodo |
sí | |
payer |
Pagador |
sí | |
reference |
string |
no | Su referencia. Vuelve tal cual en la operación. |
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account_number |
string |
no | |
account_type |
mobile | account |
no | |
bank_code |
string |
no | |
document |
object |
no | |
email |
string |
no | |
name |
string |
no | |
phone |
string |
no |
Qué campos exige cada método y cada banco lo dice GET /v2/capabilities
(fields): mándelos tal cual, y sólo esos. En un cobro recibido no hay
name: el pago se reconoce por documento, banco y teléfono.
mandate (sólo direct_debit)
Sección titulada «mandate (sólo direct_debit)»| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
contract_date |
string |
no | |
contract_id |
string^[A-Za-z0-9]{1,30}$ |
no | Alfanumérico, hasta 30: es lo que acepta el banco. |
Ejemplos
Sección titulada «Ejemplos»curl -X POST https://api.tucapi.app/v2/payins \ -H "Authorization: Bearer $LLAVE" \ -H "Idempotency-Key: orden-4821" \ -H "Content-Type: application/json" \ -d '{ "amount": "1500.50", "country": "VE", "currency": "VES", "metadata": { "orden": "A-1" }, "method": "debit_otp", "payer": { "account_number": "04121234567", "account_type": "mobile", "bank_code": "0102", "document": { "number": "12345678", "type": "V" }, "name": "Nombre Apellido" }, "reference": "factura 123" }'{ "amount": "1500.50", "bank_reference": null, "code_expires_at": "2026-09-23T15:04:05Z", "country": "VE", "created_at": "2026-09-23T14:59:05Z", "created_by": "company", "currency": "VES", "failure": null, "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f", "metadata": { "orden": "A-1" }, "method": "debit_otp", "pending_reason": "awaiting_code", "reference": "factura 123", "status": "pending", "type": "payin", "updated_at": "2026-09-23T14:59:05Z"}curl -X POST https://api.tucapi.app/v2/payins/$ID/confirm \ -H "Authorization: Bearer $LLAVE" \ -H "Content-Type: application/json" \ -d '{ "code": "123456" }'{ "amount": "1500.50", "bank_reference": "000123456789", "confirmed_at": "2026-09-23T15:04:05Z", "country": "VE", "created_at": "2026-09-23T14:59:05Z", "created_by": "company", "currency": "VES", "failure": null, "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f", "metadata": { "orden": "A-1" }, "method": "debit_otp", "reference": "factura 123", "status": "confirmed", "type": "payin", "updated_at": "2026-09-23T14:59:05Z"}curl -X POST https://api.tucapi.app/v2/payins/$ID/cancel \ -H "Authorization: Bearer $LLAVE"{ "amount": "1500.50", "bank_reference": null, "country": "VE", "created_at": "2026-09-23T14:59:05Z", "created_by": "company", "currency": "VES", "expires_at": null, "failure": { "code": "cancelled", "message": "La operación fue cancelada." }, "id": "6f1c2a9e-3b4d-4c5e-8f70-1a2b3c4d5e6f", "metadata": { "orden": "A-1" }, "method": "incoming_mobile_payment", "pending_reason": null, "reference": "factura 123", "status": "failed", "type": "payin", "updated_at": "2026-09-23T14:59:05Z"}