Guía de integración — API pública de TRUCKDOCK360 (v1)
API de solo lectura para conectar el ERP, TMS, Power BI o Excel del generador a sus propios datos. Base: https://api.truckdock.co/v1. Documentación interactiva: https://api.truckdock.co/v1/docs.
1. Autenticación
- Un administrador crea la llave en Configuración → API (nombre, alcances, vencimiento opcional). La llave completa (
td_live_…) se muestra una sola vez: guárdala en el gestor de secretos de tu sistema. - Envíala en cada llamada en la cabecera
X-Api-Key(también se aceptaAuthorization: Bearer td_live_…). - Pruébala:
curl -H "X-Api-Key: td_live_TU_LLAVE" https://api.truckdock.co/v1/me
Alcances: despachos:read (despachos, entregas, transportadoras), citas:read (citas, muelles, eventos), liquidaciones:read (liquidaciones, PDF, el flete/piso SICETAC del despacho y los costos de las operaciones portuarias y aéreas), metricas:read (métricas del tablero) y metricas_dinero:read (métricas de dinero; requiere el anterior). Sin el alcance, la respuesta es 403 insufficient_scope. Revocar una llave la apaga de inmediato; las integraciones que la usen empiezan a recibir 401 api_key_revoked.
2. Formato
- Listas:
{ "data": [ … ], "next_cursor": "…", "next": "https://…" }. Siguenexthasta que seanull. - Un recurso:
{ "data": { … } }. La excepción es/metricas/{key}, que devuelve la tabla directamente:{ metrica, kind, unidad, columnas, data, data_anterior, meta }(ver §6). - Errores:
application/problem+json(RFC 9457) concodeestable,detailen español einstance(urn:truckdock:request:<id>) para citar en soporte. Códigos:invalid_api_key,api_key_revoked,api_key_expired,tenant_inactive,jwt_not_accepted,insufficient_scope,validation_error(conerrors[].campo/mensaje),invalid_cursor,not_found,range_too_wide,rate_limited,method_not_allowed,internal_error. - Instantes en ISO 8601 UTC con
Z(2026-09-22T14:05:00Z); conviértelos a hora de Bogotá en tu herramienta. Fechas (fecha_operacion,fecha_entrega) y los parámetrosdesde/hastason días calendario de Bogotá, ambos extremos inclusive, sobre la fecha de negocio de cada recurso. - Números: dinero (COP), kilos y porcentajes llegan como números JSON, nunca como texto. Los ids son UUID (texto).
- Nombres de campo en español (
numero,fecha_operacion,placa,estado); sufijos técnicos en inglés (_id,created_at,updated_at,*_at). Los valores de estado son los códigos del sistema (en_operacion,flota_propia,M1_temprano). Tu integración debe tolerar campos y valores nuevos: en v1 solo se añade.
3. Paginación
Cada lista paginada ({data, next_cursor, next}) acepta limit (por defecto 100, máximo 500) y cursor. /muelles, /transportadoras y /citas/{id}/eventos devuelven el conjunto completo en { "data": [ … ] }, sin limit ni cursor. La primera llamada va sin cursor; las siguientes usan el next_cursor de la respuesta anterior (o directamente la URL next). El cursor es opaco y está atado a los filtros: si cambias los filtros, vuelve a empezar sin cursor (si no, recibes 400 invalid_cursor). El orden es estable por (updated_at, id) (eventos: (created_at, id)), así que las filas nuevas nunca desplazan páginas ya leídas.
4. Sincronización incremental (ERP / TMS)
- Primera carga: recorre la lista completa con
desde/hastapor tramos (por ejemplo, un mes) siguiendonext. - Guarda el mayor
updated_atrecibido como marca de agua. - Cada ciclo: pide
?updated_since=<marca − 5 minutos>(ISO con zona horaria, por ejemplo2026-09-22T14:00:00-05:00) y haz upsert porid. Los 5 minutos cubren transacciones largas; el upsert absorbe los repetidos. - Eventos logísticos: el registro original nunca cambia, pero el generador puede corregir su fecha y hora o su ubicación (v1.4.0). Usa
updated_sinceen/eventoscon la misma regla (la marca de agua es el mayor entreregistrado_atycorregido_at): trae los eventos nuevos y los corregidos desde la marca.created_sincesigue sirviendo si solo te interesan los eventos nuevos. - Borrados: un despacho nunca desaparece de la lista una vez expuesto. Las solicitudes que entran por los portales de proveedor y cliente aparecen cuando el generador las aprueba o rechaza; mientras son borrador no se exponen.
- Precisión: los instantes van truncados al segundo y
updated_sincees inclusivo (>=), así que usar la marca exacta re-entrega como mucho las filas de ese mismo segundo; nunca pierde filas.
5. Recursos
| Recurso | Alcance | Fecha de desde/hasta |
Sincronización |
|---|---|---|---|
/despachos, /despachos/{id} (con entregas y cita) |
despachos:read | fecha_operacion |
updated_since |
/entregas, /entregas/{id} |
despachos:read | fecha_entrega |
updated_since |
/citas, /citas/{id} |
citas:read | día Bogotá de programada_at |
updated_since |
/citas/{id}/eventos |
citas:read | — (todos los eventos de la cita) | — |
/eventos |
citas:read | día Bogotá de registrado_at |
updated_since (nuevos y corregidos) o created_since (solo nuevos) |
/muelles, /transportadoras |
citas:read / despachos:read | — | — |
/liquidaciones, /{id}, /{id}/pdf |
liquidaciones:read | fecha_operacion del despacho |
updated_since |
/operaciones-logisticas, /{id} (con lineas) |
liquidaciones:read | fecha_operacion de la operación |
updated_since |
/servicios-portuarios, /{id} |
liquidaciones:read | fecha_operacion del servicio |
updated_since |
/metricas, /metricas/{key} |
metricas:read (+ metricas_dinero:read) | rango obligatorio si la métrica lo requiere | — |
Filtros de listas: estado (varios separados por coma), tipo_operacion (salida | entrada), responsable, transportadora_id, placa, muelle_id, despacho_id, cliente_nit, periodo (YYYY-MM), operacion_id; en liquidaciones, contexto (origen | destino); en costos portuarios, tipo (maritimo | aereo), agente_id y asociado. Los campos exactos de cada recurso están en la documentación interactiva.
valor_flete y sicetac_valor_piso del despacho llegan en null si la llave no tiene liquidaciones:read.
Despachos anulados. Un despacho anulado queda con estado = cancelado, anulado = true y anulado_at (el instante de la anulación); un cancelado corriente trae anulado = false. Si el transporte era contratado y el camión ya estaba en planta, la anulación genera una liquidación en borrador con cierre = anulacion: cuenta solo el tiempo logístico hasta la anulación (fin_conteo_at), su salida_at es null y el generador la valida antes de emitirla. Las demás liquidaciones traen cierre = salida y fin_conteo_at igual a salida_at.
Flujo en destino (v1.3.0). Cada entrega trae estado_destino: pendiente → llegada (el camión llegó al destino) → post_operacion (se finalizó la operación y faltan los documentos de soporte) → completado. El despacho trae el agregado: pendiente (ninguna entrega con llegada), en_curso o completado (todas completadas). Lo tienen las salidas sin nodo con transporte contratado o flota propia y el nodo puerto_cliente; en los demás llega null. Cada fase registrada mueve updated_at, así que updated_since basta para seguir el avance.
Registros corregidos (v1.4.0). Un administrador o un supervisor puede corregir la fecha y hora o la ubicación de un evento del muelle, con motivo obligatorio; el registro original se conserva en TruckDock. La API entrega siempre el valor vigente: en /eventos y /citas/{id}/eventos el evento corregido trae ocurrido_at, diferencia_min, llegada_clasificacion, latitud y longitud ya corregidos, corregido = true y corregido_at (la última corrección). Los tiempos de /citas y de la cita del despacho se recalculan con esos valores, y la corrección mueve updated_at de la cita y del despacho, así que updated_since los re-entrega. Si la corrección llega después de pagar la liquidación, el comprobante no cambia: trae corregida_despues_pago_at para conciliarlo.
Liquidación en destino (v1.5.0). Cada liquidación trae contexto: origen es el comprobante de siempre (el tiempo en el muelle del generador, uno por despacho); destino es el comprobante del tiempo en el destino de una entrega (entrega_id, entrega_numero), uno por entrega de las salidas con transporte contratado y del nodo puerto_cliente. En destino, cita_at es la cita de destino vigente, inicio_efectivo_at el mayor entre la llegada y esa cita, fin_operacion_at y fin_conteo_at son «Finalizar operación» (cierre = finalizacion), y inicio_operacion_at, salida_at y espera_min van en null; cita_id sigue siendo la cita del muelle del despacho (null en puerto_cliente). La penalidad y el stand-by se miden sobre el tiempo acumulado del viaje: minutos_previos trae lo acumulado antes del tramo (origen y destinos anteriores, sin tránsito) y penalidad_valor es la marginal del tramo. Un comprobante con requiere_nueva_cita = true (llegada tarde o entrega sin cita) trae total_min = 0 y sigue en borrador hasta que el generador reprograme la cita. Si tu integración suponía una liquidación por despacho, filtra con contexto=origen o agrupa por despacho_id.
Costos de operaciones portuarias y aéreas
Una operación logística (CA-AAAA-NNNN) junta los servicios que cobran los agentes portuarios (servicio marítimo, SM-AAAA-NNNN) o aéreos (servicio aéreo, SA-AAAA-NNNN) con los despachos terrestres de empresas contratadas que llevan la mercancía al puerto o aeropuerto, o la traen de allí. Todo con el alcance liquidaciones:read.
GET /operaciones-logisticas: filtrosdesde/hasta(fecha de la operación),tipo(maritimo|aereo) yupdated_since. Trae los totales:costo_terrestre,costo_servicios,costo_total,num_despachos,num_servicios.GET /operaciones-logisticas/{id}: la operación con suslineas[], una por despacho y una por servicio, en el orden del flujo (hacia el puerto → servicio → desde el puerto).tipo_lineadice si esdespachooservicio;tipo_operacion_codigoes el código estable (planta_puerto,puerto_cliente,puerto_planta,terrestre,maritimo,aereo) ytipo_operacionla etiqueta que muestra la app, cuya redacción puede cambiar.GET /servicios-portuariosy/servicios-portuarios/{id}: filtrosdesde/hasta(fecha del servicio),tipo,agente_id,operacion_id,asociado(true= ya está en una operación,false= aún libre) yupdated_since. Cada servicio trae el agente (agente_nombre,agente_nit,agente_nit_dv), nunca sus correos.- Los despachos traen
operacion_idyoperacion_numero(ypuerto_tipo:puerto|aeropuerto);GET /despachos?operacion_id=<id>lista los de una operación.
Cómo se calcula el costo. costo_total = costo_terrestre + costo_servicios, donde costo_terrestre es la suma del flete pactado de los despachos (un despacho cancelado aporta 0) y costo_servicios la suma de los servicios. Cada línea trae en costo lo que aporta al total. El tiempo logístico liquidado del despacho viaja en valor_tiempo_liquidado solo como información: no entra al costo de la operación. El KPI de costo de transporte del tablero (costo_transporte_cop) sí suma flete + liquidaciones: las dos cifras miden cosas distintas y no tienen por qué coincidir.
Sincronización. El updated_at de una operación se mueve cuando cambian sus totales (un flete, el costo de un servicio, una cancelación) o cuando entra o sale una línea. Un cambio de estado de un despacho que no toca el total no la mueve: sincroniza el estado de los despachos por /despachos?updated_since=… y las liquidaciones por /liquidaciones. Una operación o un servicio libre sí pueden eliminarse en la app: la API no publica borrados, así que concilia recorriendo la lista completa de vez en cuando. Al eliminar una operación, sus despachos y servicios se re-entregan con operacion_id = null.
curl -s -H "X-Api-Key: $KEY" "https://api.truckdock.co/v1/operaciones-logisticas/<id>"
{
"data": {
"id": "5b0e…",
"numero": "CA-2026-0007",
"tipo": "maritimo",
"fecha_operacion": "2026-09-22",
"costo_terrestre": 2800000.0,
"costo_servicios": 4150000.5,
"costo_total": 6950000.5,
"moneda": "COP",
"num_despachos": 1,
"num_servicios": 1,
"observaciones": null,
"created_at": "2026-09-22T15:10:04Z",
"updated_at": "2026-09-23T13:42:51Z",
"lineas": [
{
"tipo_linea": "despacho",
"id": "9f31…",
"numero": "D-260922-9F31",
"tipo_operacion_codigo": "planta_puerto",
"tipo_operacion": "Transporte a puerto",
"nodo": "planta_puerto",
"fecha_operacion": "2026-09-22",
"estado": "completado",
"transportadora_nombre": "Transportes del Pacífico",
"valor_flete": 2800000.0,
"valor_tiempo_liquidado": 337596.0,
"costo": 2800000.0
},
{
"tipo_linea": "servicio",
"id": "c7d2…",
"numero": "SM-2026-0015",
"tipo_operacion_codigo": "maritimo",
"tipo_operacion": "Servicio marítimo",
"fecha_operacion": "2026-09-22",
"agente_nombre": "Agencia Marítima del Pacífico S.A.S.",
"agente_nit": "900123456",
"agente_nit_dv": "8",
"descripcion": "Descargue, almacenaje y trámite aduanero",
"costo": 4150000.5
}
]
}
}
(Respuesta recortada: cada línea trae todos los campos del esquema, en null los que no aplican a su tipo.)
6. Métricas (Power BI / Excel)
GET /metricas lista el catálogo (clave, definición, unidad, dimensiones y agregaciones permitidas, si es de dinero). GET /metricas/{key}?desde&hasta[&dimension&granularidad&agregacion&muelle&transportadora&vehiculo&tipo_operacion&responsable&comparar&limit&detalle] devuelve tablas planas listas para pegar en un modelo:
data: una fila por valor. Sin dimensión: una fila convalor,ny los escalares de la métrica. Con dimensión: una fila por grupo (clave,etiqueta,valor,n, …); condimension=tiempocada fila traefecha. Histogramas:bin_inicio_min,bin_fin_min,n.columnas: las columnas presentes (todas las filas las traen;nullcuando no aplica). Cada fila incluyemetrica,desdeyhastapara que varias consultas se puedan anexar en una sola tabla.data_anterior: el período de comparación, misma forma, cuando pidescomparar=periodo_anterioromismo_periodo_anio_anterior(solo métricas escalares, agrupadas y series; histogramas, listas y estados no la traen).meta: los metadatos del tablero tal cual (claves técnicas en inglés:unit,range,direction,bin_minutes,resumencon los escalares cuando pidesdetalle).
El rango máximo es de 366 días; las métricas de dinero exigen metricas_dinero:read. limit (por defecto y máximo 100) acota las filas de una métrica agrupada o de lista, sin cursor: si un desglose supera 100 grupos, acótalo con filtros o con un rango más corto. Límite: 30 llamadas por minuto por llave (cuentan también en el límite general de 120). Las métricas con datos personales de conductores o mecánica interna de asignación no se exponen (responden 404).
7. Power BI
Obtener datos → Web → Avanzado: URL https://api.truckdock.co/v1/despachos?desde=2026-01-01&limit=500, cabecera X-Api-Key con la llave. Para la actualización programada en el servicio de Power BI usa una URL base fija con Web.Contents(Base, [RelativePath, Query]) y guarda la llave como credencial Web API (ApiKeyName), nunca dentro del código M. Consulta completa (Editor avanzado):
let
BaseUrl = "https://api.truckdock.co/v1",
GetPage = (cursor as nullable text) =>
Json.Document(Web.Contents(BaseUrl, [
RelativePath = "despachos",
Query = [desde = "2026-01-01", hasta = "2026-12-31", limit = "500"]
& (if cursor = null then [] else [cursor = cursor]),
ApiKeyName = "X-Api-Key",
Headers = [Accept = "application/json"]
])),
Pages = List.Generate(
() => GetPage(null),
each _ <> null,
each if _[next_cursor] = null then null else GetPage(_[next_cursor]),
each _[data]),
Tabla = Table.FromRecords(List.Combine(Pages), null, MissingField.UseNull)
in
Tabla
Para las métricas: RelativePath = "metricas/tiempo_operacion_min", Query = [desde = "…", hasta = "…", dimension = "muelle"] y toma Json.Document(...)[data] (las filas ya vienen en data; columnas describe sus campos).
Convierte *_at con DateTimeZone.SwitchZone(DateTimeZone.FromText(x), -5) para ver hora de Bogotá.
8. Excel
Datos → Obtener datos → Desde la web → Opciones avanzadas, misma URL y cabecera X-Api-Key. Para más de una página pega la consulta M del punto 7 en el Editor avanzado de Power Query.
9. Ejemplos
# Despachos de septiembre, primera página
curl -s -H "X-Api-Key: $KEY" "https://api.truckdock.co/v1/despachos?desde=2026-09-01&hasta=2026-09-30&limit=200"
# Sincronización incremental de citas
curl -s -H "X-Api-Key: $KEY" "https://api.truckdock.co/v1/citas?updated_since=2026-09-22T13:55:00-05:00"
# Costos portuarios: operaciones marítimas de septiembre
curl -s -H "X-Api-Key: $KEY" "https://api.truckdock.co/v1/operaciones-logisticas?desde=2026-09-01&hasta=2026-09-30&tipo=maritimo"
# PDF de una liquidación
curl -s -H "X-Api-Key: $KEY" -o LIQ.pdf "https://api.truckdock.co/v1/liquidaciones/<id>/pdf"
import httpx
BASE, KEY = "https://api.truckdock.co/v1", "td_live_..."
with httpx.Client(headers={"X-Api-Key": KEY}, timeout=30) as c:
url = f"{BASE}/despachos?desde=2026-09-01&hasta=2026-09-30&limit=500"
while url:
body = c.get(url).raise_for_status().json()
for d in body["data"]:
print(d["numero"], d["estado"], d["kg_brutos"])
url = body["next"]
10. Límites y errores
| Límite | Valor |
|---|---|
| Solicitudes por minuto por llave | 120 (30 para métricas) |
| Filas por página | 500 (listas) · 100 (métricas, sin cursor) |
| Rango de una métrica | 366 días |
| Llaves activas por empresa | 20 |
Cada respuesta trae X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset; al exceder recibes 429 rate_limited con Retry-After (segundos). Repite con espera exponencial ante 429 y 5xx; no repitas ante 4xx.
11. Versionado
Ver Cambios de la API. En v1 solo se añade; los cambios incompatibles salen en /v2 y v1 sigue viva al menos 12 meses con las cabeceras Deprecation y Sunset.