Conecta tu negocio.
Coordina cada entrega.
Pedidos de tu plataforma, entregas de tu equipo. Conecta tu backend con TRAQQI, de la recolección a la confirmación.
Una conexión y permisos por negocio.
02 / CONECTARecibe cada cambio ↗Webhooks firmados o consulta de eventos.
Una organización. Una conexión.
Un propietario o administrador gestiona la conexión desde el dashboard, incluso en el navegador del celular. La API limita cada clave a su organización y a los pedidos que creó esa integración. No necesitas credenciales de Supabase de TRAQQI.
Tu app → tu backend → API TRAQQI → app del repartidor.
TRAQQI → webhook de tu backend → notificación al comprador.
https://traqqi-worker.iqqiadmin.workers.dev
| Método | Endpoint | Uso | Permiso |
|---|---|---|---|
| POST | /v1/deliveries | Crear un pedido | deliveries:write |
| GET | /v1/deliveries?after={uuid} | Listar hasta 50 pedidos | deliveries:read |
| GET | /v1/deliveries?external_id={id} | Buscar una referencia externa | deliveries:read |
| GET | /v1/deliveries/{uuid} | Consultar un pedido | deliveries:read |
| GET | /v1/events?after={cursor} | Consultar hasta 50 eventos | events:read |
| GET | /v1/deliveries/{uuid}/location | Consultar ubicación habilitada | tracking:read |
No están implementados cancelación o transferencia por API, checkout de Stripe, claves de sandbox, tokens públicos para compradores ni eventos independientes de inicio/cierre de ruta. Los pedidos de prueba usan la base actual: acuerda una organización de pruebas antes de enviar datos.
Tu clave se queda en tu servidor.
Inicia sesión con la misma cuenta de la app, selecciona tu negocio y entra a Conexión API. Solo owner/admin puede crear, pausar, reactivar, configurar, rotar o revocar la integración; el servidor vuelve a comprobar la sesión y la membresía en cada comando.
Authorization: Bearer <CLAVE_PRIVADA_DE_LA_INTEGRACION>
- La clave tiene prefijo
tq_live_y solo se revela al crearla o rotarla. Guarda el valor en un gestor de secretos; nunca en URLs, JavaScript público, apps móviles o repositorios. - Rotar invalida la clave anterior inmediatamente, sin período de convivencia. Coordina el reemplazo en tu backend. Si perdiste la respuesta de creación, consulta la conexión y rota la clave; no repitas el alta.
- Permisos:
deliveries:read,deliveries:write,events:read,codes:readytracking:read. Para obtener códigos por consulta de eventos necesitas tantoevents:readcomocodes:read. - Configura hasta diez orígenes HTTPS exactos, sin rutas ni comodines. Si envías
Origin, debe estar permitido. Sin esa cabecera se admite un servidor con clave válida, salvo que activesrequire_origin.
Un cliente externo puede falsificar Origin. La lista de dominios complementa la clave; no permite exponerla en el navegador. Tu backend debe autenticar al comprador y comprobar que el pedido le pertenece antes de compartir códigos o ubicación.
Crea una vez. Reintenta con seguridad.
curl -X POST 'https://traqqi-worker.iqqiadmin.workers.dev/v1/deliveries' \
-H 'Authorization: Bearer <CLAVE_PRIVADA>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: pedido-123-intento-01' \
--data '{
"external_id": "PEDIDO-123",
"recipient_name": "Cliente de ejemplo",
"recipient_phone": "+12025550101",
"delivery_address": "Dirección ilustrativa de entrega, Monterrey",
"latitude": 25.68,
"longitude": -100.31,
"delivery_notes": "Pedido sintético; no entregar ni contactar."
}'
| Campo | Validación |
|---|---|
external_id | Texto de 1 a 80 caracteres; referencia estable y única dentro de tu integración. |
recipient_name | Texto de 2 a 120 caracteres. |
recipient_phone | E.164: + seguido de 8 a 15 dígitos, sin espacios. |
delivery_address | Texto de 10 a 500 caracteres. |
latitude / longitude | Números finitos; latitud −90 a 90, longitud −180 a 180. |
delivery_notes | Opcional; hasta 1000 caracteres. |
El cuerpo es plano. No envíes campos adicionales como organización, repartidor o estado: se rechazan con 400 unknown_field. La clave determina el negocio. La referencia que muestra la app se genera como API-<uuid>; conserva también el ID retornado.
{
"id": "6d12b2ba-a0ca-49ef-9bfc-8bc9ca0b5bfa",
"external_id": "PEDIDO-123",
"replayed": false
}
Idempotency-Key es obligatoria: 8–100 caracteres de [A-Za-z0-9_-]. Repetir la misma clave y cuerpo devuelve 201 con el mismo ID y replayed: true. Cambiar el cuerpo produce 409 idempotency_conflict; otro intento de crear una referencia existente produce 409 external_id_exists. El recibo dura siete días; la referencia externa sigue siendo única después.
Consultar y reconciliar
Los tres GET de pedidos devuelven {data: [...]}. La consulta por UUID contiene un elemento o devuelve 404; la búsqueda por referencia retorna cero o uno. El listado contiene como máximo 50, ordenados por UUID, no por fecha: usa el último id como after y continúa hasta recibir una página vacía. Codifica los parámetros de búsqueda.
{
"data": [
{
"id": "6d12b2ba-a0ca-49ef-9bfc-8bc9ca0b5bfa",
"external_id": "PEDIDO-123",
"reference": "API-6d12b2ba-a0ca-49ef-9bfc-8bc9ca0b5bfa",
"status": "awaiting_pickup",
"version": 1,
"created_at": "2026-11-04T17:00:00Z",
"assigned_at": null,
"delivered_at": null,
"failed_at": null,
"failure_reason": null
}
]
}
La lectura no devuelve teléfono, dirección, identidad del repartidor ni código. Tu sistema conserva los datos originales y relaciona cada pedido con su comprador.
Cada entrega tiene su momento.
awaiting_pickup → assigned → in_transit → delivered
↘ failed
failed → awaiting_pickup (reprogramación autorizada)
assigned significa que el repartidor tomó el pedido. in_transit significa que inició la ruta. La app y sus comandos autorizados gestionan estas transiciones; la API externa no permite forzar estados ni confirmar una entrega. Un intento fallido conserva su motivo y requiere el flujo operativo de devolución/reprogramación antes de un nuevo intento.
Usa version en lecturas y aggregate_version en eventos para reconocer estados anteriores. No supongas orden de llegada de los webhooks. Un evento de código puede compartir versión con el estado del intento: deduplica por event_id, no solo por versión.
Recibe cambios sin perder el contexto.
| Evento | Significado |
|---|---|
delivery.created | El pedido fue aceptado. |
delivery.collected | El repartidor tomó el pedido; todavía puede no haber iniciado ruta. |
delivery.out_for_delivery | La ruta comenzó y el pedido está en camino. |
delivery.confirmation_code.issued | Se emitió el código del intento. Evento privado para el backend del comercio. |
delivery.attempt_failed | El intento falló; incluye el motivo registrado. |
delivery.delivered | La entrega se confirmó y el código quedó consumido. |
delivery.rescheduled | El pedido volvió a estar disponible para un nuevo intento. |
Consulta con cursor
GET https://traqqi-worker.iqqiadmin.workers.dev/v1/events?after=0
Authorization: Bearer <CLAVE_PRIVADA>
{ "data": [], "retention_days": 7 }
Se devuelven hasta 50 eventos en orden ascendente. El cursor es un decimal de hasta 18 dígitos representado como texto: no lo conviertas a Number. Procesa y guarda de forma durable el cursor del último evento; úsalo como after en la siguiente consulta. Tolera huecos. Tras una interrupción mayor a siete días, reconcilia los estados con el listado de pedidos. No es un historial permanente.
Receptor HTTPS y firma
Solicita a soporte la autorización del host público de tu receptor. El operador lo incluye en WEBHOOK_HOSTS; escribir una URL en el dashboard no autoriza su dominio automáticamente. Después configura una URL HTTPS sin credenciales, puerto, parámetros ni fragmento y guarda el secreto de firma mostrado una vez. Solo se admite un receptor por negocio. Se envían los eventos de esa integración; actualmente no hay selección de tipos de webhook. Los permisos de consulta de códigos no sustituyen la protección del receptor, que también recibe códigos.
Content-Type: application/json
Traqqi-Event-Id: 7e97735d-0f3b-4ac1-a737-7872b9179281
Traqqi-Signature: t=<unix_seconds>,v1=<hex_hmac_sha256>
{
"cursor": "125",
"event_id": "7e97735d-0f3b-4ac1-a737-7872b9179281",
"event_type": "delivery.confirmation_code.issued",
"schema_version": 1,
"organization_id": "db875790-dd98-47a7-9307-647ea1201711",
"delivery_id": "6d12b2ba-a0ca-49ef-9bfc-8bc9ca0b5bfa",
"external_id": "PEDIDO-123",
"aggregate_version": 2,
"attempt_id": "425079ec-ec86-4892-b0fa-c3407851289e",
"occurred_at": "2026-11-04T18:00:00Z",
"expires_at": "2026-11-05T18:00:00Z",
"payload": {
"code": "123456"
}
}
Verifica HMAC-SHA256 sobre timestamp + "." + cuerpo_original, con el secreto de firma del webhook (distinto a la clave API), comparación en tiempo constante y tolerancia de cinco minutos. El cuerpo debe ser los bytes originales, antes de parsear JSON; no lo serialices de nuevo para verificarlo. Comprueba que la cabecera de ID coincida con event_id.
Los eventos de estado incluyen payload.status y, cuando corresponde, payload.failure_reason. El sobre contiene schema_version: 1, aggregate_version, fechas UTC, IDs y cursor. Tolera campos nuevos y eventos desconocidos. Algunas respuestas de polling pueden incluir ciphertext: null; no es un campo que deba consumir tu aplicación.
Tras validar la firma, guarda el evento en una bandeja durable con unicidad por event_id y responde 2xx, también ante duplicados ya guardados. Procesa notificaciones en segundo plano. El receptor tiene cinco segundos para responder y los eventos pueden repetirse o llegar fuera de orden.
El código llega al comprador.
Al recoger un pedido integrado se solicita un código de seis dígitos. El Worker lo emite de forma asíncrona: procesa hasta diez pendientes cada minuto. Puede tardar un minuto o más si hay cola. Tu backend espera delivery.confirmation_code.issued y comunica payload.code únicamente al comprador autorizado.
Conserva el código como texto para preservar ceros iniciales. attempt_id y expires_at están en el sobre del evento, no dentro de payload. Caduca como máximo a las 24 horas y deja de ser válido al consumirse o invalidarse el intento. La app del repartidor solicita el código al recibir el paquete; no puede consultar el código privado de la integración.
Los códigos caducados o de intentos terminados no se recuperan mediante polling ni deben reutilizarse desde tu copia. Si un webhook de código llega después de un cambio de estado, consulta el estado actual y descártalo cuando ya no corresponda. No registres códigos en logs, analítica o herramientas de soporte.
Solo cuando corresponde compartir.
El endpoint está implementado y se verificó técnicamente con ubicaciones sintéticas. Para responder con coordenadas requiere: pedido propio en in_transit, ruta activa, última ubicación de menos de dos minutos y autorización temporal de seguimiento de ese pedido. Ese foco caduca a los cinco minutos y su conexión con la selección de destino en la app todavía está pendiente.
{ "available": false, "location": null }
{
"available": true,
"location": {
"latitude": 25.68,
"longitude": -100.31,
"accuracy": 15,
"observed_at": "2026-11-04T18:00:00Z"
}
}
Trata available: false como un estado normal, sin mostrar una ubicación vieja como actual. No hay historial de recorrido, identidad del repartidor ni acceso al resto de su ruta. La entrega finalizada corta la disponibilidad. No anuncies rastreo externo completo hasta validar el recorrido con dispositivos reales.
Tu backend autoriza al comprador; el navegador nunca consulta TRAQQI con la clave privada. Como recomendación de consumo, consulta cada uno o dos minutos mientras la vista esté abierta y detén las consultas cuando termine la entrega. Ese intervalo es una recomendación, no una cuota adicional implementada.
Diseña también para los reintentos.
{ "error": "idempotency_conflict", "request_id": "4e5486cb-62e9-44b2-af1b-56743e4c0447" }
| HTTP | Acción del cliente |
|---|---|
400 / 413 | Corrige campos, JSON, cursor o tamaño; no repitas el mismo cuerpo inválido. |
401 | Revisa clave o sesión vencida/revocada; no reintentes sin corregirla. |
403 | Revisa permisos y Origin; la clave no autoriza otros negocios. |
404 | El recurso solicitado no está disponible para la integración. |
409 | Reconcilia idempotency_conflict o external_id_exists; no cambies referencias al azar. |
429 | Espera Retry-After (actualmente 60 segundos), reduce concurrencia y añade jitter. |
503 | Espera Retry-After (actualmente 15 segundos); reintenta de forma acotada con la misma clave idempotente. |
Los errores llevan un identificador de solicitud y la cabecera X-Request-Id. Guarda este identificador para soporte; no registres Authorization, teléfonos, direcciones ni cuerpos con códigos.
Reintentos de webhooks
Hasta diez intentos durante un máximo de 24 horas, con espera exponencial y jitter. Fallos de red, timeout, 408, 429 y 5xx se reintentan. Retry-After del receptor se respeta dentro del límite de una hora. No se siguen redirecciones: 3xx y los demás 4xx terminan el envío. No existe todavía un panel de reenvío ni alertas automáticas al administrador; usa polling como recuperación y monitoriza tu receptor.
Menos datos. Más claridad.
- 120 solicitudes por minuto calendario por integración, compartidas entre lecturas y escrituras. Existe además un limitador aproximado de 60 solicitudes por IP por minuto en el borde de Cloudflare; no es una cuota global exacta.
- Cuerpo JSON máximo: 16 KiB. UUID para IDs, UTC ISO 8601 para fechas, enteros para versiones y cadenas para códigos/cursors.
- Eventos y recibos de idempotencia: siete días. Código recuperable: máximo 24 horas o fin del intento. Auditoría de integración: 90 días. Solo última ubicación, sin historial continuo de GPS.
- Respuestas privadas con Cache-Control: no-store. No expongas respuestas privadas en cachés compartidas.
- El procesamiento inicial usa lotes de diez webhooks y dos receptores simultáneos. No se garantiza entrega inmediata ni se anuncia SLA.
Pedidos, cuentas y otros datos tienen sus propias políticas: estos plazos no significan que todos los datos se borren a los siete días. Consulta el aviso de privacidad. Tolera cambios aditivos en respuestas y eventos.
Comprueba el recorrido completo.
Acordar una organización de pruebas; todavía no existe un sandbox independiente por clave.
Crear un pedido sintético desde tu backend y verificar que aparece en la app correcta.
Repetir la creación con la misma clave y comprobar que no se duplica.
Recoger el pedido, iniciar ruta y recibir eventos y código mediante polling o webhook.
Confirmar con el código y comprobar el estado entregado; probar también incidencia y reprogramación.
Verificar firma inválida, eventos repetidos/desordenados, caída del receptor y recuperación con cursor.
Comprobar revocación, aislamiento entre negocios, permisos y límites con carga controlada.
Validar el GPS móvil y el foco temporal antes de ofrecer ubicación al comprador.
Prepara tu conexión.
Genera tus credenciales desde el dashboard y comparte esta referencia con el equipo que mantiene tu backend.
Gestionar conexión →Consultar con soporte ↗