Saltar al contenido principal
POST
/
api
/
v1
/
webhooks
/
aggregators
/
order-status
Estado de orden de agregador
curl --request POST \
  --url https://app.fire.rest/api/v1/webhooks/aggregators/order-status \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <x-api-key>' \
  --data '
{
  "channelCode": "<string>",
  "status": "<string>",
  "providerEventId": "<string>",
  "occurredAt": "<string>",
  "orderId": "<string>",
  "externalOrderId": "<string>",
  "metadata": {}
}
'
{
  "received": true,
  "duplicate": false,
  "eventId": "b1f2c3d4-5e6f-5a7b-8c9d-0e1f2a3b4c5d",
  "webhookEventId": "9e6c8af8-af80-4967-9422-c096ab43c0e7",
  "status": "queued",
  "firstReceivedAt": "2026-06-14T18:46:00.512Z",
  "message": "Event accepted and queued for processing."
}
Este endpoint es entrante — tu agregador de delivery (Rappi, Uber, Didi, iFood, PedidosYa, Glovo…) le hace POST cada vez que avanza la orden de su lado: se asignó un repartidor, la orden fue retirada, está en ruta, fue entregada, y así. Fire autentica la solicitud, correlaciona la orden, refleja el último estado en orders.aggregator, y registra el evento en su log de eventos de agregador para observabilidad.
Este endpoint es asíncrono. Fire autentica, correlaciona la orden (guards de tenant + canal), deduplica y encola el evento — luego responde 202 Accepted con un webhookEventId (típicamente en menos de 100 ms). El espejo de la orden se actualiza un instante después en un worker en segundo plano (normalmente en ~2 segundos). Para verificar el resultado, consultá GET /v1/webhooks/events/{webhookEventId}. Los problemas corregibles por el cliente (payload inválido, orden no encontrada, tenant o canal equivocado, ids en conflicto) se rechazan síncronamente con 4xx antes del 202.
El estado es passthrough. Los agregadores no comparten un vocabulario de estados, así que Fire no impone un enum: status se guarda tal cual como el tipo de evento (courier_assigned, on_route, entregue, lo que use tu canal). El estado actual de la orden es el del occurredAt más reciente — no un orden de ciclo de vida fijo. No hay compuerta anti-regresión: un timestamp posterior gana, punto. Las etiquetas amigables y traducidas son un asunto de presentación que se resuelve desde el catálogo del canal, nunca se imponen aquí.
No hay un eventId emitido por Fire para ecoar. A diferencia del callback fiscal y los estados KDS — que ecoan un event.id que Fire emitió — un estado de agregador es un evento externo espontáneo. Fire no es la fuente de verdad acá, así que no hay chequeo de fuente de verdad sobre un eventId. En cambio, vos le decís a Fire a qué orden pertenece el estado, vía resolución de la orden abajo. La idempotencia se llavea por tu providerEventId (ver Idempotencia).

Resolución de la orden

Debés decirle a Fire a qué orden pertenece este estado. Hay dos caminos, y podés enviar uno o ambos — al menos uno es requerido:
CampoCorrelaciona porCuándo usarlo
orderIdNuestro orders.id (UUID)Guardaste el id de orden de Fire (ej. ecoado de un evento saliente o de la respuesta de inyección)
externalOrderIdEl id de orden externo (XMART/agregador), comparado contra el metadata.order_id de la ordenSolo conocés tu propia referencia de orden
Ambos son vendor-scoped: la orden correlacionada debe pertenecer a la cuenta + vendor ligados a tu API key, y su canal debe coincidir con channelCode.
Si enviás ambos ids, deben apuntar a la misma orden. Fire correlaciona cada uno de forma independiente; si orderId y externalOrderId correlacionan a órdenes distintas, la solicitud se rechaza con 409 Conflict — Fire no va a adivinar a cuál te referías. Enviá uno, o enviá ambos apuntando a la misma orden.

Autenticación

Este endpoint requiere una API key vendor-scoped con el scope webhooks:aggregator (binding de cuenta + vendor). Fire valida que la orden correlacionada pertenezca a ese vendor. Las keys sin el scope, o sin binding de vendor, se rechazan con 403 Forbidden.
x-api-key
string
requerido
Tu API key vendor-scoped de Fire con scope webhooks:aggregator. Generá una desde Developers → Gestión de API para la cuenta/vendor cuyas órdenes reportará esta key.
Authorization
string
Bearer <token> opcional — aceptado como alternativa legacy a x-api-key. Enviá uno u otro.

Cuerpo de la solicitud

channelCode
string
requerido
El código de agregador/canal — debe ser igual al metadata.channel.code de la orden (channels.code, ej. RAPPI, UBER, o el id numérico de canal como 99). Si no coincide con el canal de la orden correlacionada, Fire responde 403.
status
string
requerido
El estado de entrega crudo, passthrough — guardado tal cual como el tipo de evento. Se acepta cualquier string no vacío (accepted, courier_assigned, picked_up, on_route, delivered, cancelled, o las etiquetas propias de tu canal). No se impone ningún enum.
providerEventId
string
requerido
El id propio de tu agregador para esta entrega — la llave de idempotencia (junto con channelCode). Fire mapea (channelCode, providerEventId) a un id de evento interno estable, así que reenviar el mismo par con el mismo status es un replay seguro. Usá el id de evento nativo si lo tenés; si no, un UUID.
occurredAt
string
requerido
Timestamp ISO 8601 UTC de cuándo cambió el estado del lado del agregador — no cuándo se envió. Esto es lo que ordena el recorrido: el estado con el occurredAt más reciente es el estado actual de la orden.
orderId
string
UUID de la orden en Fire. Requerido si externalOrderId está ausente. Ver Resolución de la orden.
externalOrderId
string
El id de orden externo (XMART/agregador), comparado contra el metadata.order_id de la orden. Requerido si orderId está ausente. Ver Resolución de la orden.
metadata
object
Bolsa libre opcional de campos extra (nombre del repartidor, url de tracking, etc.). Se guarda tal cual, sin validar.

Ejemplos

Los reportes de la misma entrega comparten un channelCode y correlacionan a la misma orden; cada uno lleva su propio status, providerEventId y occurredAt:
courier_assigned (por orderId de Fire)
{
  "channelCode": "RAPPI",
  "status": "courier_assigned",
  "providerEventId": "evt-7af3-0001",
  "occurredAt": "2026-06-14T18:46:00.000Z",
  "orderId": "7a3a7d6b-1234-4abc-9def-0123456789ab"
}
on_route (por id de orden externo)
{
  "channelCode": "RAPPI",
  "status": "on_route",
  "providerEventId": "evt-7af3-0002",
  "occurredAt": "2026-06-14T18:52:00.000Z",
  "externalOrderId": "RP-2026-558831"
}
delivered (ambos ids — deben apuntar a la misma orden)
{
  "channelCode": "RAPPI",
  "status": "delivered",
  "providerEventId": "evt-7af3-0003",
  "occurredAt": "2026-06-14T19:07:00.000Z",
  "orderId": "7a3a7d6b-1234-4abc-9def-0123456789ab",
  "externalOrderId": "RP-2026-558831",
  "metadata": { "courier": "Ana P.", "trackingUrl": "https://rappi.example/t/abc" }
}

Respuesta

En éxito el endpoint responde 202 Accepted — el evento fue autenticado, correlacionado, deduplicado y encolado. Un 202 no significa que el espejo de la orden ya se actualizó; eso ocurre de forma asíncrona. Usá el endpoint de estado para confirmar. El body trae dos ids distintos: eventId es el id interno estable de Fire para este par (channelCode, providerEventId), webhookEventId es el id de Fire para el registro encolado. Misma forma que el callback fiscal.
received
boolean
Siempre true cuando la solicitud fue aceptada y encolada.
duplicate
boolean
true cuando este reporte de estado exacto ya se ingresó (misma orden, mismo (channelCode, providerEventId), mismo status) — se devuelve el registro existente y no se re-encola nada. false para un reporte nuevo (incluido un status distinto de la misma entrega — eso es un paso nuevo, no un duplicado).
eventId
string
El id interno estable de Fire derivado de (channelCode, providerEventId).
webhookEventId
string
El id de Fire para el registro encolado. Pasalo a GET /v1/webhooks/events/{webhookEventId} para consultar el resultado. En un duplicado es el mismo id que se devolvió la primera vez.
status
string
Estado actual en la cola — queuedprocessingprocessed (y retry / failed / dead / ignored).
firstReceivedAt
string
Timestamp ISO 8601 UTC de cuándo Fire recibió por primera vez este reporte. Estable entre reintentos.
message
string
Resumen legible.
{
  "received": true,
  "duplicate": false,
  "eventId": "b1f2c3d4-5e6f-5a7b-8c9d-0e1f2a3b4c5d",
  "webhookEventId": "9e6c8af8-af80-4967-9422-c096ab43c0e7",
  "status": "queued",
  "firstReceivedAt": "2026-06-14T18:46:00.512Z",
  "message": "Event accepted and queued for processing."
}

Verificar el resultado

Como el procesamiento es asíncrono, el 202 solo confirma que el evento fue encolado. Para ver si el espejo de la orden se actualizó, consultá el endpoint de estado con el webhookEventId que devolvió el 202:
GET https://app.fire.rest/api/v1/webhooks/events/{webhookEventId}
x-api-key: <tu key webhooks:aggregator>
status
string
Ciclo de vida de la cola: queuedprocessingprocessed (listo) · failed / dead (se rindió tras reintentos) · retry (esperando el próximo intento) · ignored (manejado, sin acción — ej. un duplicado).
attempts
number
Intentos de procesamiento hasta ahora.
result
object | null
En éxito, el resultado del worker — ej. { "kind": "merged", "current": "delivered" }.
error
object | null
{ "message": "…" } cuando el último intento falló; null si no.
Se devuelve 404 para ids desconocidos — o ids de otro tenant — sin filtrar existencia. Autenticá con la misma key webhooks:aggregator que usaste para el evento.

Idempotencia y el recorrido

Fire deduplica por la orden más (channelCode, providerEventId) y el statusno por un eventId emitido por Fire. Esto es lo que hace funcionar el recorrido manteniendo limpios los reintentos:
EscenarioResultado
courier_assigned, luego on_route, luego deliveredstatus distinto (y providerEventId), misma ordencada uno es un paso nuevo202 duplicate:false
El mismo reporte reenviado — misma orden, mismo (channelCode, providerEventId), mismo status202 duplicate:true — registro existente devuelto, no reprocesado
Ni orderId ni externalOrderId enviados400
Orden no encontrada para este vendor404
orderId y externalOrderId correlacionan a órdenes distintas409
channelCode ≠ el canal de la orden, o key no es de este vendor403
Auth store momentáneamente inalcanzable503 — transitorio, reintentá
Un status distinto nunca es un duplicado. Como los agregadores reportan legítimamente muchos estados para una entrega, dos reportes con el mismo (channelCode, providerEventId) pero un status distinto son dos pasos distintos — Fire guarda ambos. Mantené providerEventId único por reporte de estado para no reproducir un paso por accidente.

El espejo de la orden

Una vez procesado, el último estado se refleja en el bloque aggregator de la orden, con el recorrido completo guardado en history (ordenado por occurredAt). El status actual es la entrada con el occurredAt más reciente:
orders.aggregator
{
  "channelCode": "RAPPI",
  "status": "delivered",
  "occurredAt": "2026-06-14T19:07:00.000Z",
  "history": [
    { "status": "courier_assigned", "occurredAt": "2026-06-14T18:46:00.000Z" },
    { "status": "on_route",         "occurredAt": "2026-06-14T18:52:00.000Z" },
    { "status": "delivered",        "occurredAt": "2026-06-14T19:07:00.000Z" }
  ]
}
El estado crudo se guarda tal cual; cualquier etiqueta amigable y traducida se resuelve al momento de mostrar desde el catálogo de estados del canal — el valor guardado nunca cambia.

Relacionado

Inyectar orden

El endpoint de inyección que crea la orden que este estado referencia.

Estado de orden KDS

El webhook entrante hermano para el estado de cocina — mismo modelo async + cola, pero con eventId emitido por Fire y anti-regresión.

Callback fiscal

El webhook entrante fiscal — mismo envelope async + idempotencia.

Autenticación

Cómo funcionan las API keys, scopes y el binding de vendor.