POST https://api.fire.rest/api/v1/fire/external/orders/ORD-77/confirm-payment
x-api-key: <tu_api_key>
Content-Type: application/json
{
"payments": [
{
"uid": "9b1c...",
"total": "20",
"method": "CASH",
"currency_code": "BRL",
"details": {
"total_bill": 20,
"currency_code": "BRL",
"transaction_id": "BR-K000-POS-38-1785258648480",
"transaction_status": "APPROVED",
"transaction_date": { "date": "2026-07-28T17:10:48.000Z" }
}
},
{
"uid": "7d3a...",
"total": "15.90",
"method": "CREDIT",
"currency_code": "BRL",
"details": {
"total_bill": 15.90,
"currency_code": "BRL",
"transaction_id": "BR-K000-POS-38-1785258648999",
"transaction_status": "APPROVED",
"transaction_date": { "date": "2026-07-28T17:11:02.000Z" }
}
}
]
}
{
"success": true,
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"outcome": "settled",
"settled": true,
"status": "COMPLETED",
"paymentStatus": "SUCCEEDED",
"completedAt": "2026-07-28T17:11:02.000Z",
"tenderCount": 2,
"declinedCount": 0,
"amountMismatch": false,
"declines": [],
"flowsTriggered": 1
}
}
{
"success": true,
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"outcome": "declined",
"settled": false,
"status": "OPEN",
"paymentStatus": "PENDING",
"completedAt": null,
"tenderCount": 0,
"declinedCount": 2,
"amountMismatch": false,
"declines": [
{ "sent": "INSUFFICIENT_FUNDS", "resolvedTo": "INSUFFICIENT_FUNDS", "group": "funds", "exceedsOrderTotal": false },
{ "sent": "51", "resolvedTo": null, "group": null, "exceedsOrderTotal": true }
],
"flowsTriggered": 0
}
}
{
"success": true,
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"outcome": "duplicate",
"settled": false,
"status": "COMPLETED",
"paymentStatus": "SUCCEEDED",
"completedAt": "2026-07-28T17:11:02.000Z",
"tenderCount": 0,
"declinedCount": 0,
"amountMismatch": false,
"flowsTriggered": 0
}
}
{
"success": false,
"error": "BUSINESS_ERROR",
"code": "INCOMPLETE_CHARGE",
"message": "Approved tenders total 200000 does not reach the order total 359000 — send the complete charge in one call"
}
{
"success": false,
"error": "BUSINESS_ERROR",
"code": "AMOUNT_EXCEEDS_TOTAL",
"message": "Approved tenders total 500000 exceeds the order total 359000"
}
{
"success": false,
"error": "CONFLICT",
"code": "ORDER_ALREADY_SETTLED",
"message": "Order is already settled and does not accept new payment reports",
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"completedAt": "2026-07-30T16:24:11.802Z"
}
}
{
"success": false,
"error": "CONFLICT",
"code": "ORDER_NOT_OPEN",
"message": "Order is closed and does not accept payment reports",
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"orderStatus": "CANCELLED",
"completedAt": null
}
}
{
"success": false,
"error": "VALIDATION_ERROR",
"message": "Datos de entrada inválidos",
"details": [
{
"code": "custom",
"path": ["payments", 1],
"message": "Duplicate transaction id \"BR-K000-POS-38-1785258648480\" within the same payment batch"
}
]
}
{
"success": false,
"error": "FORBIDDEN",
"message": "API key must be vendor-scoped (account + vendor binding) to access this endpoint"
}
{
"success": false,
"error": "NOT_FOUND",
"message": "InjectedOrder not found with ID ORD-77"
}
{
"success": false,
"error": "CONFLICT",
"message": "External order id ORD-77 matches 2 orders across vendors; cannot disambiguate"
}
API
Confirmar pago
Registra el cobro de una orden abierta y la salda. Acepta varios medios de pago en un mismo cobro y también los intentos rechazados.
POST
/
api
/
v1
/
fire
/
external
/
orders
/
{orderId}
/
confirm-payment
POST https://api.fire.rest/api/v1/fire/external/orders/ORD-77/confirm-payment
x-api-key: <tu_api_key>
Content-Type: application/json
{
"payments": [
{
"uid": "9b1c...",
"total": "20",
"method": "CASH",
"currency_code": "BRL",
"details": {
"total_bill": 20,
"currency_code": "BRL",
"transaction_id": "BR-K000-POS-38-1785258648480",
"transaction_status": "APPROVED",
"transaction_date": { "date": "2026-07-28T17:10:48.000Z" }
}
},
{
"uid": "7d3a...",
"total": "15.90",
"method": "CREDIT",
"currency_code": "BRL",
"details": {
"total_bill": 15.90,
"currency_code": "BRL",
"transaction_id": "BR-K000-POS-38-1785258648999",
"transaction_status": "APPROVED",
"transaction_date": { "date": "2026-07-28T17:11:02.000Z" }
}
}
]
}
{
"success": true,
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"outcome": "settled",
"settled": true,
"status": "COMPLETED",
"paymentStatus": "SUCCEEDED",
"completedAt": "2026-07-28T17:11:02.000Z",
"tenderCount": 2,
"declinedCount": 0,
"amountMismatch": false,
"declines": [],
"flowsTriggered": 1
}
}
{
"success": true,
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"outcome": "declined",
"settled": false,
"status": "OPEN",
"paymentStatus": "PENDING",
"completedAt": null,
"tenderCount": 0,
"declinedCount": 2,
"amountMismatch": false,
"declines": [
{ "sent": "INSUFFICIENT_FUNDS", "resolvedTo": "INSUFFICIENT_FUNDS", "group": "funds", "exceedsOrderTotal": false },
{ "sent": "51", "resolvedTo": null, "group": null, "exceedsOrderTotal": true }
],
"flowsTriggered": 0
}
}
{
"success": true,
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"outcome": "duplicate",
"settled": false,
"status": "COMPLETED",
"paymentStatus": "SUCCEEDED",
"completedAt": "2026-07-28T17:11:02.000Z",
"tenderCount": 0,
"declinedCount": 0,
"amountMismatch": false,
"flowsTriggered": 0
}
}
{
"success": false,
"error": "BUSINESS_ERROR",
"code": "INCOMPLETE_CHARGE",
"message": "Approved tenders total 200000 does not reach the order total 359000 — send the complete charge in one call"
}
{
"success": false,
"error": "BUSINESS_ERROR",
"code": "AMOUNT_EXCEEDS_TOTAL",
"message": "Approved tenders total 500000 exceeds the order total 359000"
}
{
"success": false,
"error": "CONFLICT",
"code": "ORDER_ALREADY_SETTLED",
"message": "Order is already settled and does not accept new payment reports",
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"completedAt": "2026-07-30T16:24:11.802Z"
}
}
{
"success": false,
"error": "CONFLICT",
"code": "ORDER_NOT_OPEN",
"message": "Order is closed and does not accept payment reports",
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"orderStatus": "CANCELLED",
"completedAt": null
}
}
{
"success": false,
"error": "VALIDATION_ERROR",
"message": "Datos de entrada inválidos",
"details": [
{
"code": "custom",
"path": ["payments", 1],
"message": "Duplicate transaction id \"BR-K000-POS-38-1785258648480\" within the same payment batch"
}
]
}
{
"success": false,
"error": "FORBIDDEN",
"message": "API key must be vendor-scoped (account + vendor binding) to access this endpoint"
}
{
"success": false,
"error": "NOT_FOUND",
"message": "InjectedOrder not found with ID ORD-77"
}
{
"success": false,
"error": "CONFLICT",
"message": "External order id ORD-77 matches 2 orders across vendors; cannot disambiguate"
}
Cierra el ciclo del pago diferido: la orden nació abierta, la cocina ya trabajó, y acá llega el
cobro. La orden pasa a
Lo que separa un rechazo de un reintento es el
Esa última fila es deliberada. Si Fire respondiera con error porque no pudo encolar un
evento, reintentarías un cobro que ya entró. El dinero manda sobre el aviso.
Los campos que Fire no conoce se aceptan y se guardan: podés enviar tu objeto de pago completo
sin recortarlo. La moneda de una pieza rechazada no invalida el envío, porque no suma.
COMPLETED y arranca la facturación cuando lo aprobado llega exacto al
total de la orden.
Un cobro con varias piezas, en una sola llamada. Si repartís la cuenta entre efectivo y
tarjeta, mandá las dos piezas en la misma llamada: las piezas aprobadas tienen que sumar
exacto el total de la orden. Un cobro que no cuadra se rechaza entero y no se registra nada —
consolidá tus parciales antes de mandarlos.Reintentar es seguro y esperado: reenviá el envío completo y la idempotencia por
transaction_id se encarga del resto.¿Tenés que corregir la orden antes de cobrarla —datos de factura del cliente, localizador—? Eso
va por Corregir orden, no por este endpoint. Este solo registra
la plata; la diferencia entre los dos está explicada ahí.
Autenticación
string
requerido
Tu API key de Fire con scope
orders:write. La key debe ser vendor-scoped — las keys sin
vendorId se rechazan con 403.Path params
string
requerido
Cualquiera de las tres referencias públicas de la orden:
Es el mismo conjunto que aceptan Obtener orden y
Cancelar orden.
| referencia | qué es |
|---|---|
orders.id | el UUID interno de Fire |
order_external | el id que asignaste al crear la orden |
metadata.order_id | copia del id externo dentro de la orden |
Cuerpo
object[]
requerido
Los medios con los que cobraste la orden. Entre 1 y 20 piezas. Cada objeto se guarda tal cual lo
enviaste; abajo solo están los campos que Fire lee.
string
requerido
Estado del intento. Cuentan como cobrado:
APPROVED, AUTHORIZED, CAPTURED, PAID,
SUCCESS, SUCCEEDED (sin distinguir mayúsculas).Cualquier otro valor se trata como rechazo, incluso si no lo reconocemos. Es deliberado:
facturar un cobro que no ocurrió no tiene vuelta atrás, mientras que no saldar algo que sí se
cobró se resuelve con otro envío.string
requerido
Identificador de la transacción. Es la clave de idempotencia: reenviar el mismo no vuelve a
cobrar ni a facturar. Si tu medio no lo genera, Fire usa
payments[].uid.number
requerido
Monto de esta pieza, decimal. Debe ser mayor que cero. Si no viene, Fire cae al
payments[].total
del nivel superior.string
requerido
Moneda de la pieza. Todas las piezas aprobadas deben compartir la misma — incluidas las de
llamadas anteriores de la misma orden. Si no viene, Fire cae al
payments[].currency_code del
nivel superior.string
Motivo del rechazo, con un código de Motivos de rechazo.
Solo aplica cuando
transaction_status no es aprobado.El código de tu adquirente (51, do_not_honor) hay que traducirlo de tu lado al del catálogo:
Fire no guarda los códigos de cada proveedor. Si mandás uno que no está, se acepta y se guarda,
pero vuelve con resolvedTo: null y queda fuera de tus métricas agrupadas.string
Medio de pago (
CASH, CREDIT, DEBIT, PIX…). Se usa para la facturación y las métricas.Petición
POST https://api.fire.rest/api/v1/fire/external/orders/ORD-77/confirm-payment
x-api-key: <tu_api_key>
Content-Type: application/json
{
"payments": [
{
"uid": "9b1c...",
"total": "20",
"method": "CASH",
"currency_code": "BRL",
"details": {
"total_bill": 20,
"currency_code": "BRL",
"transaction_id": "BR-K000-POS-38-1785258648480",
"transaction_status": "APPROVED",
"transaction_date": { "date": "2026-07-28T17:10:48.000Z" }
}
},
{
"uid": "7d3a...",
"total": "15.90",
"method": "CREDIT",
"currency_code": "BRL",
"details": {
"total_bill": 15.90,
"currency_code": "BRL",
"transaction_id": "BR-K000-POS-38-1785258648999",
"transaction_status": "APPROVED",
"transaction_date": { "date": "2026-07-28T17:11:02.000Z" }
}
}
]
}
Qué hace Fire con lo que enviás
Solo las piezas aprobadas suman y saldan. Las rechazadas se registran para tus métricas, no suman y nunca bloquean el cobro.| situación | outcome | ¿salda? |
|---|---|---|
| la suma de las aprobadas es igual al total | settled | sí — pasa a COMPLETED y arranca la facturación |
| ninguna pieza aprobada, solo rechazos | declined | no — se registra y la orden sigue abierta |
| ninguna pieza nueva (ya las habías enviado) | duplicate | no — reintento idempotente |
| la suma de las aprobadas no es igual al total | — | 400 — no se registra nada |
| una pieza nueva sobre una orden cerrada (saldada, cancelada o forzada) | — | 409 — no se registra nada |
Todo o nada. Un cobro cuyas piezas aprobadas no suman el total de la orden se rechaza entero —
ni siquiera se guarda un rechazo que viniera en el mismo envío.El motivo no es contable, es operativo: aceptar plata en una orden que no salda la deja con dinero
adentro y sin salida. Fire no procesa devoluciones y no hay forma de que nos avises que
devolviste. Consolidar los cobros parciales te toca a vos — y sos el único que puede devolver, así
que ese estado ya lo cargás igual.Cobrar más que el total se rechaza por otra razón: facturaría un monto que no es el que se
cobró, y una factura no se puede desemitir. Los dos mensajes te dan los dos montos para que
corrijas y reenvíes.Los rechazos son la excepción: un envío sin piezas aprobadas se registra y la orden sigue
abierta. Sin plata no hay estado que resolver, y ahí viven tus métricas de rechazo.
Qué pasó con cada rechazo
Cada pieza rechazada vuelve endeclines[], con lo que enviaste y si lo encontramos en el catálogo.
"declines": [
{ "sent": "INSUFFICIENT_FUNDS", "resolvedTo": "INSUFFICIENT_FUNDS", "group": "funds", "exceedsOrderTotal": false },
{ "sent": "51", "resolvedTo": null, "group": null, "exceedsOrderTotal": true }
]
exceedsOrderTotal: true quiere decir que el monto que intentaste cobrar supera el total de la
orden. No exigimos que cada pieza sea igual al total —en un cobro repartido, $20 sobre $35,90 es
legítimo— pero superarlo nunca lo es: casi siempre significa que se está cobrando otra orden. El
rechazo es tu aviso gratis, porque si el siguiente intento aprueba se saldaría un monto que no
corresponde.
resolvedTo: null quiere decir que ese código no existe en el catálogo — en el ejemplo, se mandó
el código crudo del adquirente en vez del de Fire. El rechazo se registró igual y el valor quedó
guardado, pero no aparece en nada agrupado por motivo.
Revisá este bloque en tu primera integración. Un código mal traducido no rompe nada: el cobro
funciona, la respuesta es
200, y tus rechazos entran sin clasificar. Te enterarías meses después
con el tablero de motivos vacío.Una orden cerrada no acepta nada más
Una orden recibe cobros mientras está abierta. Una vez cerrada está cerrada, sin importar cómo llegó a estarlo. Una pieza que nunca vio se rechaza y no se registra nada — ni aprobada ni rechazada. El código de error te dice por qué está cerrada, y las dos cosas piden acciones distintas:| código | la orden | qué significa para vos |
|---|---|---|
ORDER_ALREADY_SETTLED | ya se cobró por completo | la plata entró. Si cobraste de nuevo, hay que devolver |
ORDER_NOT_OPEN | se cerró sin cobrarse (cancelada, forzada) | esa plata no corresponde a esta orden |
transaction_id, no el estado de la orden:
| lo que enviás | qué es | respuesta |
|---|---|---|
un transaction_id que Fire ya tiene | un reintento después de un timeout de red | 200, outcome: duplicate |
un transaction_id que Fire nunca vio | un reporte sobre una orden cerrada | 409 |
Un reintento nunca se rechaza, a propósito. Cobraste una vez y nuestra respuesta nunca te llegó;
contestar con un error ahí empujaría al operador a pasar la tarjeta de nuevo — el doble cobro que
estamos tratando de evitar. Mandá el mismo
transaction_id y recibís de vuelta el resultado
original.Cuando el cobro salda
Saldar es lo que completa la orden, así que ahí es cuando Fire emiteorder.completed — la orden nació OPEN y recién ahora
terminó. La respuesta lo informa como flowsTriggered.
flowsTriggered: 0 no es un error de cobro. Significa una de tres cosas:
| Caso | Por qué |
|---|---|
| El cobro no saldó | La orden sigue abierta; no se completó nada. |
outcome: "duplicate" | El evento ya salió con la llamada original. |
| Falló el encolado | El pago quedó registrado igual. El incidente queda del lado de Fire para que un operador lo re-dispare. |
Una orden con pago diferido ya emitió su documento fiscal al abrirse, y vuelve a pasar
por el paso fiscal en
order.completed. Fire detecta el documento existente y saltea
la segunda emisión — no te llegan dos facturas.Idempotencia
Cada pieza se identifica por sutransaction_id dentro de la orden. Podés reintentar sin miedo:
- Reenviar el cobro completo →
duplicate. No se vuelve a saldar ni a facturar. - Reenviar después de un timeout → reenviá el envío entero. Un cobro incompleto nunca se
registró, y cualquier pieza que sí haya entrado se ignora por su
transaction_id.
Validaciones
Todas devuelven400 salvo donde se indique. Cada rechazo trae un code además del mensaje:
ramificá por el código, no por el texto — el mensaje está escrito para leerse y se puede reescribir, el
código es contrato.
| regla | código |
|---|---|
payments con al menos una pieza | 400 |
| máximo 20 piezas | 400 |
details.transaction_status presente | 400 |
transaction_id o uid presente | 400 |
transaction_id sin repetir dentro del mismo envío | 400 |
| monto mayor que cero en cada pieza | 400 |
| una sola moneda entre las piezas aprobadas | 400 MIXED_CURRENCIES |
| la suma de las aprobadas es igual al total de la orden, exacto (todo o nada) | 400 INCOMPLETE_CHARGE |
| la suma de las aprobadas no supera el total de la orden | 400 AMOUNT_EXCEEDS_TOTAL |
| el total de la orden se puede resolver en la moneda de las piezas | 400 TOTAL_NOT_RESOLVABLE |
| API key válida | 401 |
scope orders:write y key vendor-scoped | 403 |
| la orden existe dentro de tu vendor | 404 |
| la referencia matchea más de una orden | 409 AMBIGUOUS_ORDER_REFERENCE |
| una pieza nueva sobre una orden ya saldada | 409 ORDER_ALREADY_SETTLED |
| una pieza nueva sobre una orden cerrada sin cobrarse | 409 ORDER_NOT_OPEN |
Respuestas
{
"success": true,
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"outcome": "settled",
"settled": true,
"status": "COMPLETED",
"paymentStatus": "SUCCEEDED",
"completedAt": "2026-07-28T17:11:02.000Z",
"tenderCount": 2,
"declinedCount": 0,
"amountMismatch": false,
"declines": [],
"flowsTriggered": 1
}
}
{
"success": true,
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"outcome": "declined",
"settled": false,
"status": "OPEN",
"paymentStatus": "PENDING",
"completedAt": null,
"tenderCount": 0,
"declinedCount": 2,
"amountMismatch": false,
"declines": [
{ "sent": "INSUFFICIENT_FUNDS", "resolvedTo": "INSUFFICIENT_FUNDS", "group": "funds", "exceedsOrderTotal": false },
{ "sent": "51", "resolvedTo": null, "group": null, "exceedsOrderTotal": true }
],
"flowsTriggered": 0
}
}
{
"success": true,
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"outcome": "duplicate",
"settled": false,
"status": "COMPLETED",
"paymentStatus": "SUCCEEDED",
"completedAt": "2026-07-28T17:11:02.000Z",
"tenderCount": 0,
"declinedCount": 0,
"amountMismatch": false,
"flowsTriggered": 0
}
}
{
"success": false,
"error": "BUSINESS_ERROR",
"code": "INCOMPLETE_CHARGE",
"message": "Approved tenders total 200000 does not reach the order total 359000 — send the complete charge in one call"
}
{
"success": false,
"error": "BUSINESS_ERROR",
"code": "AMOUNT_EXCEEDS_TOTAL",
"message": "Approved tenders total 500000 exceeds the order total 359000"
}
{
"success": false,
"error": "CONFLICT",
"code": "ORDER_ALREADY_SETTLED",
"message": "Order is already settled and does not accept new payment reports",
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"completedAt": "2026-07-30T16:24:11.802Z"
}
}
{
"success": false,
"error": "CONFLICT",
"code": "ORDER_NOT_OPEN",
"message": "Order is closed and does not accept payment reports",
"data": {
"orderId": "7e2b8c10-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
"orderCode": "OC-1024",
"orderStatus": "CANCELLED",
"completedAt": null
}
}
{
"success": false,
"error": "VALIDATION_ERROR",
"message": "Datos de entrada inválidos",
"details": [
{
"code": "custom",
"path": ["payments", 1],
"message": "Duplicate transaction id \"BR-K000-POS-38-1785258648480\" within the same payment batch"
}
]
}
{
"success": false,
"error": "FORBIDDEN",
"message": "API key must be vendor-scoped (account + vendor binding) to access this endpoint"
}
{
"success": false,
"error": "NOT_FOUND",
"message": "InjectedOrder not found with ID ORD-77"
}
{
"success": false,
"error": "CONFLICT",
"message": "External order id ORD-77 matches 2 orders across vendors; cannot disambiguate"
}
Relacionado
Motivos de rechazo
Catálogo agrupado para clasificar los intentos rechazados.
Obtener orden
Consultá el estado y el total antes de cobrar.

