Desarrolladores / Referencia de la APIv1 · Integración inicial
DOCUMENTACIÓN / API REST

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.

01 / EMPEZAR

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.

Arquitectura recomendada

Tu app → tu backend → API TRAQQI → app del repartidor.
TRAQQI → webhook de tu backend → notificación al comprador.

URL base del servicio
https://traqqi-worker.iqqiadmin.workers.dev
MétodoEndpointUsoPermiso
POST/v1/deliveriesCrear un pedidodeliveries:write
GET/v1/deliveries?after={uuid}Listar hasta 50 pedidosdeliveries:read
GET/v1/deliveries?external_id={id}Buscar una referencia externadeliveries:read
GET/v1/deliveries/{uuid}Consultar un pedidodeliveries:read
GET/v1/events?after={cursor}Consultar hasta 50 eventosevents:read
GET/v1/deliveries/{uuid}/locationConsultar ubicación habilitadatracking: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.

02 / SEGURIDAD

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.

Cabecera requerida
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:read y tracking:read. Para obtener códigos por consulta de eventos necesitas tanto events:read como codes: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 actives require_origin.
CORS no acredita la identidad de un servidor.

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.

03 / PEDIDOS

Crea una vez. Reintenta con seguridad.

Crear un pedido · ejemplo sintético
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."
}'
CampoValidación
external_idTexto de 1 a 80 caracteres; referencia estable y única dentro de tu integración.
recipient_nameTexto de 2 a 120 caracteres.
recipient_phoneE.164: + seguido de 8 a 15 dígitos, sin espacios.
delivery_addressTexto de 10 a 500 caracteres.
latitude / longitudeNúmeros finitos; latitud −90 a 90, longitud −180 a 180.
delivery_notesOpcional; 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.

201 · nuevo pedido
{
  "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.

Proyección de un pedido
{
  "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.

04 / ESTADOS

Cada entrega tiene su momento.

Recorrido principal
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.

05 / EVENTOS

Recibe cambios sin perder el contexto.

EventoSignificado
delivery.createdEl pedido fue aceptado.
delivery.collectedEl repartidor tomó el pedido; todavía puede no haber iniciado ruta.
delivery.out_for_deliveryLa ruta comenzó y el pedido está en camino.
delivery.confirmation_code.issuedSe emitió el código del intento. Evento privado para el backend del comercio.
delivery.attempt_failedEl intento falló; incluye el motivo registrado.
delivery.deliveredLa entrega se confirmó y el código quedó consumido.
delivery.rescheduledEl pedido volvió a estar disponible para un nuevo intento.

Consulta con cursor

Recuperar eventos
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.

POST enviado a tu receptor
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.

Responde rápido; procesa después.

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.

06 / CONFIRMACIÓN

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.

07 / UBICACIÓN

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.

Ubicación no disponible · respuesta normal
{ "available": false, "location": null }
Ubicación disponible · ejemplo sintético
{
  "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.

08 / RECUPERACIÓN

Diseña también para los reintentos.

Formato de error
{ "error": "idempotency_conflict", "request_id": "4e5486cb-62e9-44b2-af1b-56743e4c0447" }
HTTPAcción del cliente
400 / 413Corrige campos, JSON, cursor o tamaño; no repitas el mismo cuerpo inválido.
401Revisa clave o sesión vencida/revocada; no reintentes sin corregirla.
403Revisa permisos y Origin; la clave no autoriza otros negocios.
404El recurso solicitado no está disponible para la integración.
409Reconcilia idempotency_conflict o external_id_exists; no cambies referencias al azar.
429Espera Retry-After (actualmente 60 segundos), reduce concurrencia y añade jitter.
503Espera 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.

09 / OPERACIÓN RESPONSABLE

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.

10 / ANTES DE CONECTAR

Comprueba el recorrido completo.

01

Acordar una organización de pruebas; todavía no existe un sandbox independiente por clave.

02

Crear un pedido sintético desde tu backend y verificar que aparece en la app correcta.

03

Repetir la creación con la misma clave y comprobar que no se duplica.

04

Recoger el pedido, iniciar ruta y recibir eventos y código mediante polling o webhook.

05

Confirmar con el código y comprobar el estado entregado; probar también incidencia y reprogramación.

06

Verificar firma inválida, eventos repetidos/desordenados, caída del receptor y recuperación con cursor.

07

Comprobar revocación, aislamiento entre negocios, permisos y límites con carga controlada.

08

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 ↗