Zapini Integrations API
Crea tu propia integración consumiendo los recursos de Zapini — tableros Kanban, citas del calendario, conversaciones del chat y tickets de soporte — a través de una API REST simple y predecible.
Crea y gestiona tus claves de API en el panel, en Integraciones → "Integraciones Personalizadas".
https://zapini.app/api/v1
Autenticación
Cada petición se autentica con una clave de integración. Las claves tienen 51 caracteres, comienzan con el prefijo zpk_ y se generan en el panel, en Integraciones → "Integraciones Personalizadas".
Tu clave se muestra una sola vez, en el momento de crearla. Zapini almacena únicamente un hash SHA-256 de ella, por lo que no puede recuperarse después — si la pierdes, revoca la clave y crea una nueva.
Envía la clave en cada petición como Bearer token en la cabecera Authorization. Todas las peticiones y respuestas usan JSON: define Content-Type: application/json al enviar un cuerpo y Accept: application/json siempre.
Ejemplo de petición
curl https://zapini.app/api/v1/kanban/boards \
-H "Authorization: Bearer zpk_your_51_char_key_here" \
-H "Accept: application/json"
Alcances
Cada clave lleva una lista explícita de alcances elegida al crearla. Un alcance da acceso a un recurso, dividido en una mitad de lectura y otra de escritura.
| Alcance | Qué concede |
|---|---|
kanban.read |
Leer tableros, columnas, leads y actividades de leads del Kanban. |
kanban.write |
Crear, actualizar, mover y eliminar tableros, columnas y leads del Kanban. |
calendar.read |
Leer citas del calendario. |
calendar.write |
Crear, actualizar, confirmar, completar, cancelar y eliminar citas. |
chat.read |
Leer conversaciones e historial de mensajes. |
chat.write |
Enviar mensajes y medios, archivar conversaciones, gestionar etiquetas y automatización. |
tickets.read |
Leer tickets de soporte. |
tickets.write |
Crear y actualizar tickets de soporte. |
database.read |
Leer colecciones y registros de la base de datos interna (Base Inteligente). |
database.write |
Crear, actualizar y eliminar registros de la base de datos interna. |
tms.read |
Leer el módulo de transporte: flota, fletes, monitoreo, incidencias, CAP/CAR y listas de valores. |
tms.write |
Crear, modificar y eliminar registros del transporte. |
Las peticiones GET y HEAD exigen el alcance .read del recurso; las peticiones POST, PUT y DELETE exigen el alcance .write correspondiente.
Denegación por defecto: cualquier endpoint fuera de estos cuatro recursos se rechaza con HTTP 403 y el código de error ENDPOINT_NOT_IN_SCOPE, sin importar los alcances de la clave.
Seguridad y límites
Las claves de integración personalizada están diseñadas para entregarse a sistemas de terceros con seguridad. Los siguientes controles aplican a todas las claves:
Límite de peticiones
Cada clave puede realizar hasta 300 peticiones por minuto. Por encima de eso, la API responde HTTP 429 con el campo retry_after (en segundos) — espera y reintenta.
Lista de IPs permitidas (opcional)
Restringe una clave a una lista de direcciones IP exactas o rangos CIDR IPv4. Las peticiones desde cualquier otra dirección se rechazan con 403 IP_NOT_ALLOWED.
Vínculo con instancia (opcional)
Una clave puede vincularse a una única instancia de WhatsApp. Las claves vinculadas solo ven los datos de esa instancia y nunca necesitan el parámetro instance_uuid.
Revocar y rotar
Las claves pueden revocarse o rotarse en cualquier momento desde el panel. La revocación es inmediata — las peticiones hechas con una clave revocada reciben 401.
Solo HTTPS
La API se sirve exclusivamente por HTTPS. No se aceptan peticiones en HTTP plano.
Errores
Los errores usan códigos de estado HTTP convencionales más un código de error legible por máquina en el cuerpo JSON.
| Estado | Código de error | Descripción |
|---|---|---|
| 401 |
INVALID_INTEGRATION_KEY
|
La clave falta, está malformada, revocada o es desconocida. |
| 403 |
INSUFFICIENT_SCOPE
|
La clave no tiene el alcance que exige este endpoint. El payload incluye required_scope y granted_scopes para ayudarte a diagnosticarlo. |
| 403 |
ENDPOINT_NOT_IN_SCOPE
|
El endpoint está fuera de los recursos cubiertos por la API de Integraciones (denegación por defecto). |
| 403 |
IP_NOT_ALLOWED
|
La petición provino de una dirección IP fuera de la lista de permitidas de la clave. |
| 429 |
RATE_LIMITED
|
Demasiadas peticiones. Espera retry_after segundos antes de reintentar. |
| 422 | — | Falló la validación. Formato estándar de Laravel: un mensaje más un objeto errors indexado por nombre de campo. |
| 404 |
DATABASE_NOT_CONFIGURED
|
El tenant no tiene una Base Inteligente configurada, o la clave no está vinculada a una. |
| 404 |
COLLECTION_NOT_FOUND
|
La colección no existe o está fuera de la lista permitida para esta clave. |
| 404 |
RECORD_NOT_FOUND
|
No existe ese registro en la colección. |
| 403 |
TMS_NOT_ENABLED
|
El módulo TMS no está habilitado para este cliente (crm_layout no es "tms"). |
| 422 |
DIRECTION_REQUIRED
|
Listar asientos requiere direction=payable (CAP) o direction=receivable (CAR). |
Ejemplo de respuesta — 403 INSUFFICIENT_SCOPE
{
"success": false,
"error": "INSUFFICIENT_SCOPE",
"required_scope": "kanban.write",
"granted_scopes": ["kanban.read", "chat.read"]
}
Kanban
kanban.read
kanban.write
Gestiona tableros de ventas, columnas y leads — todo lo que ves en el módulo Kanban del panel.
Los tableros están separados por instancia de WhatsApp. Envía el parámetro instance_uuid (query/body) en las llamadas de tablero — a menos que tu clave esté vinculada a una instancia, en cuyo caso es implícito.
El prefijo canónico es /crm. El antiguo /kanban sigue respondiendo exactamente las mismas rutas, con el mismo formato — ninguna integración publicada necesita cambiar.
| Método | Endpoint | Descripción |
|---|---|---|
| Tableros | ||
| GET | /crm/boards |
Listar los tableros de la instancia. |
| POST | /crm/boards |
Crear un tablero. |
| GET | /crm/boards/{uuid} |
Obtener un tablero con sus columnas. |
| PUT | /crm/boards/{uuid} |
Actualizar un tablero (nombre, color...). |
| DELETE | /crm/boards/{uuid} |
Eliminar un tablero. |
| GET | /crm/boards/{uuid}/stats |
Totales del tablero: abiertos, ganados, perdidos, valores y distribución por columna. |
| POST | /crm/boards/{uuid}/duplicate |
Duplicar el tablero con sus columnas (sin los leads). |
| Columnas | ||
| GET | /crm/boards/{uuid}/columns |
Listar las columnas del tablero. |
| POST | /crm/boards/{uuid}/columns |
Agregar una columna al tablero. |
| POST | /crm/boards/{uuid}/columns/reorder |
Reordenar las columnas del tablero. |
| PUT | /crm/columns/{uuid} |
Actualizar una columna. |
| DELETE | /crm/columns/{uuid} |
Eliminar una columna. |
| POST | /crm/columns/{uuid}/clear |
Eliminar todos los leads de la columna. |
| Leads | ||
| GET | /crm/leads |
Buscar leads en todo el CRM (por teléfono, texto, estado, responsable o período). Siempre paginado. |
| GET | /crm/boards/{uuid}/leads |
Listar los leads de un tablero. |
| POST | /crm/leads |
Crear un lead en una columna. |
| GET | /crm/leads/{uuid} |
Obtener un lead. |
| PUT | /crm/leads/{uuid} |
Actualizar un lead. |
| DELETE | /crm/leads/{uuid} |
Eliminar un lead. |
| POST | /crm/leads/{uuid}/move |
Mover un lead a otra columna/posición. |
| POST | /crm/leads/{uuid}/won |
Marcar un lead como ganado. |
| POST | /crm/leads/{uuid}/lost |
Marcar un lead como perdido. |
| POST | /crm/leads/{uuid}/note |
Agregar una nota a la línea de tiempo del lead. |
| GET | /crm/leads/{uuid}/activities |
Listar el historial de actividades del lead. |
| POST | /crm/leads/{uuid}/assign |
Definir los responsables del lead. |
| POST | /crm/leads/{uuid}/ignore |
Descartar el lead y vetar el número en la detección automática. |
| POST | /crm/leads/{uuid}/convert-to-ticket |
Abrir un ticket a partir del lead (idempotente). |
| POST | /crm/boards/{uuid}/leads/import |
Importar hasta 500 leads en una llamada, con informe por fila. |
| POST | /crm/leads/from-conversation |
Crear un lead a partir de una conversación del chat. |
| POST | /crm/leads/from-messages |
Crear un lead a partir de mensajes específicos. |
| GET | /crm/conversations/{uuid}/lead |
Obtener el lead vinculado a una conversación. |
| Adjuntos del lead | ||
| GET | /crm/leads/{uuid}/attachments |
Listar los adjuntos del lead. |
| POST | /crm/leads/{uuid}/attachments |
Subir un adjunto al lead (hasta 25 MB, 10 por lead). |
| GET | /crm/leads/{uuid}/attachments/{attachmentUuid} |
Descargar un adjunto del lead. |
| DELETE | /crm/leads/{uuid}/attachments/{attachmentUuid} |
Eliminar un adjunto del lead. |
| IA sobre el lead | ||
| POST | /crm/leads/{uuid}/ai-qualify |
Puntuar el lead con IA y enrutarlo según la puntuación. |
| POST | /crm/leads/{uuid}/ai-suggest |
Sugerir la próxima acción para el lead con IA. |
| Automatizaciones | ||
| GET | /crm/boards/{uuid}/automations |
Listar las automatizaciones del tablero. |
| POST | /crm/boards/{uuid}/automations |
Crear una automatización (disparador + acción). |
| PUT | /crm/automations/{uuid} |
Actualizar una automatización. |
| DELETE | /crm/automations/{uuid} |
Eliminar una automatización. |
| POST | /crm/automations/{uuid}/test |
Ejecutar la automatización contra un lead real para verificar la configuración. |
| Detección automática | ||
| GET | /crm/boards/{uuid}/auto-detection |
Leer las reglas de detección automática de leads del tablero. |
| PUT | /crm/boards/{uuid}/auto-detection |
Guardar las reglas de detección automática del tablero. |
| POST | /crm/boards/{uuid}/auto-detection/preview |
Previsualizar lo que se importaría, sin crear nada. |
| POST | /crm/boards/{uuid}/auto-detection/sync |
Crear los leads de los elementos elegidos en la previsualización. |
| GET | /crm/boards/{uuid}/ignored-contacts |
Listar los contactos ignorados por el tablero. |
| POST | /crm/boards/{uuid}/ignored-contacts |
Devolver un contacto ignorado a la detección. |
| Campos personalizados | ||
| GET | /crm/field-definitions |
Esquema de los campos personalizados para armar el formulario. |
| GET | /crm/boards/{uuid}/fields |
Gestionar campos: incluye los desactivados y cuántos registros ya tienen valor en cada uno. |
| POST | /crm/boards/{uuid}/fields |
Crear un campo personalizado en el tablero. |
| POST | /crm/boards/{uuid}/fields/reorder |
Reordenar los campos del tablero. |
| PUT | /crm/fields/{id} |
Actualizar un campo personalizado (el nombre interno no cambia). |
| DELETE | /crm/fields/{id} |
Quitar un campo del formulario (los valores guardados se mantienen). |
| Configuración y apoyo | ||
| GET | /crm/config |
Configuración del CRM del tenant (diseño standard, TMS o VC). |
| GET | /crm/users |
Usuarios que pueden ser responsables de un lead. |
| GET | /crm/tags |
Etiquetas del tenant, usadas en los filtros de detección. |
Ejemplo de petición
curl -X POST https://zapini.app/api/v1/kanban/leads \
-H "Authorization: Bearer zpk_your_51_char_key_here" \
-H "Content-Type: application/json" \
-d '{
"instance_uuid": "550e8400-e29b-41d4-a716-446655440000",
"column_uuid": "c2b7e9d4-1f3a-4a5b-9c8d-7e6f5a4b3c2d",
"name": "Jane Doe",
"phone": "+5511999999999",
"value": 1500
}'
Calendario
calendar.read
calendar.write
Crea y gestiona las citas que se muestran en el calendario del panel, incluidas las transiciones de estado.
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /calendar/appointments |
Listar citas (filtro por período/estado). |
| POST | /calendar/appointments |
Crear una cita. |
| GET | /calendar/appointments/{uuid} |
Obtener una cita. |
| PUT | /calendar/appointments/{uuid} |
Actualizar una cita. |
| DELETE | /calendar/appointments/{uuid} |
Eliminar una cita. |
| POST | /calendar/appointments/{uuid}/confirm |
Marcar la cita como confirmada. |
| POST | /calendar/appointments/{uuid}/complete |
Marcar la cita como completada. |
| POST | /calendar/appointments/{uuid}/cancel |
Cancelar la cita. |
| POST | /calendar/appointments/{uuid}/to-kanban |
Crear un lead en el Kanban a partir de la cita. |
Ejemplo de petición
curl -X POST https://zapini.app/api/v1/calendar/appointments \
-H "Authorization: Bearer zpk_your_51_char_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Demo call",
"start_at": "2026-07-10 14:00",
"end_at": "2026-07-10 14:30",
"contact_name": "Jane Doe",
"contact_number": "+5511999999999"
}'
Chat
chat.read
chat.write
Lee conversaciones y mensajes, y envía texto o medios a través de tu instancia de WhatsApp conectada.
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /chat/conversations |
Listar las conversaciones del chat. |
| GET | /chat/conversations/{id} |
Obtener una conversación. |
| GET | /chat/conversations/{id}/messages |
Listar los mensajes de una conversación. |
| POST | /chat/send |
Enviar un mensaje de texto (cuerpo: conversation o recipient, más message). |
| POST | /chat/send-media |
Enviar un mensaje con medios (imagen, audio, video, documento). |
| POST | /messages/send |
Encolar un mensaje saliente (API de mensajes). |
| GET | /messages |
Listar mensajes (con filtros). |
| GET | /conversations |
Listar conversaciones (recurso conversations). |
| POST | /conversations/{id}/archive |
Archivar/desarchivar una conversación. |
| POST | /conversations/{id}/tags |
Gestionar las etiquetas de una conversación. |
| POST | /conversations/{id}/automation/pause |
Pausar la automatización de IA en la conversación. |
| POST | /conversations/{id}/automation/resume |
Reanudar la automatización de IA en la conversación. |
Ejemplo de petición
curl -X POST https://zapini.app/api/v1/chat/send \
-H "Authorization: Bearer zpk_your_51_char_key_here" \
-H "Content-Type: application/json" \
-d '{
"recipient": "+5511999999999",
"message": "Hello from my integration!"
}'
Tickets
tickets.read
tickets.write
Abre y da seguimiento a tickets de soporte de forma programática.
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /tickets |
Listar tickets. Filtros: status (open, in_progress, waiting, resolved, closed), priority (low, medium, high, urgent), category, search, per_page. |
| POST | /tickets |
Crear un ticket. subject es obligatorio; opcionales: description, priority, category, contact_name, contact_number, tags[], due_at. |
| GET | /tickets/{uuid} |
Obtener un ticket por UUID. |
| PUT | /tickets/{uuid} |
Actualizar campos y/o cambiar el estado. |
Ejemplo de petición
curl -X POST https://zapini.app/api/v1/tickets \
-H "Authorization: Bearer zpk_your_51_char_key_here" \
-H "Content-Type: application/json" \
-d '{
"subject": "Customer cannot log in",
"description": "Password reset e-mail never arrives.",
"priority": "low",
"contact_name": "Jane Doe",
"contact_number": "+5511999999999",
"tags": ["login", "email"]
}'
Ejemplo de respuesta — 201
{
"success": true,
"data": {
"uuid": "9d3f2c1a-7b6e-4d5c-8a9b-0c1d2e3f4a5b",
"code": "TK-8F3K2M1Q",
"subject": "Customer cannot log in",
"status": "open",
"priority": "low",
"category": null,
"contact_name": "Jane Doe",
"contact_number": "+5511999999999",
"tags": ["login", "email"],
"due_at": null,
"created_at": "2026-07-02T12:00:00Z"
}
}
Base de Datos (Base Inteligente)
database.read
database.write
Base Inteligente es la base de datos interna del tenant — inmuebles, carta, productos, servicios, profesionales, lo que pida el rubro. Esta API la convierte en un CMS headless: tu sitio lee y escribe exactamente los mismos registros que el cliente administra en el panel de Zapini o en el portal público. Nada se duplica, no hay sincronización que mantener.
La clave se resuelve a UNA Base Inteligente del tenant y, opcionalmente, a una lista de colecciones permitidas. Una colección fuera de esa lista responde 404 — igual que una inexistente.
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /database |
Información de la base: rubro (categoría) y colecciones que ve la clave. |
| GET | /database/collections |
Solo la lista de colecciones, con nombre visible y total de registros. |
| GET | /database/collections/{collection} |
Esquema de campos de la colección: tipo, obligatoriedad, opciones de enum y referencias. Úsalo para armar formularios dinámicos. |
| GET | /database/collections/{collection}/records |
Listar registros con búsqueda, filtros, orden, proyección y paginación. |
| POST | /database/collections/{collection}/records |
Crear un registro. El cuerpo se filtra contra el esquema — los campos desconocidos se ignoran. |
| GET | /database/collections/{collection}/records/{id} |
Obtener un registro por id. |
| PUT | /database/collections/{collection}/records/{id} |
Actualizar un registro (PUT o PATCH). Envía solo los campos que cambian. |
| DELETE | /database/collections/{collection}/records/{id} |
Eliminar un registro. |
| POST | /database/uploads |
Subir un archivo (multipart, campo file) a la galería del tenant y recibir la URL pública para guardarla en un campo de tipo image. |
Consultando registros
El listado acepta los parámetros de abajo. Los nombres de campo se validan contra el esquema de la colección y los operadores salen de una lista fija — la query string nunca llega cruda a la base.
| Parámetro | Descripción |
|---|---|
page |
Número de página (por defecto 1). |
per_page |
Registros por página (por defecto 25, máximo 100). |
search |
Búsqueda de texto en los campos de texto, correo, teléfono, documento, URL y enum de la colección. |
sort / order |
Campo de orden (del esquema, o created_at / updated_at) y dirección asc | desc. Por defecto: más recientes primero. |
fields |
Lista separada por comas de los campos a devolver. El id siempre vuelve. |
filter[campo] |
Igualdad: filter[ativo]=true, filter[cidade]=Niterói. |
filter[campo][op] |
Con operador: filter[preco][gte]=500000, filter[tipo][in]=casa,apartamento. |
eq
ne
gt
gte
lt
lte
in
nin
like
starts
ends
exists
Ejemplo de petición
# Inmuebles activos hasta R$ 800 mil, del más barato al más caro
curl "https://zapini.app/api/v1/database/collections/imoveis/records?per_page=12&sort=preco&order=asc&filter[ativo]=true&filter[preco][lte]=800000" \
-H "Authorization: Bearer zpk_your_51_char_key_here"
Ejemplo de respuesta — 200
{
"success": true,
"data": [
{
"id": "66b3f1c2a4d5e6f708192a3b",
"titulo": "Apartamento 3 quartos — Icaraí",
"preco": 780000,
"quartos": 3,
"foto_principal": "https://storage.zapini.app/gallery/8/image/....jpg",
"ativo": true,
"created_at": "2026-08-01 10:22:41"
}
],
"meta": { "current_page": 1, "per_page": 12, "total": 37, "last_page": 4 }
}
Creando un registro
curl -X POST https://zapini.app/api/v1/database/collections/imoveis/records \
-H "Authorization: Bearer zpk_your_51_char_key_here" \
-H "Content-Type: application/json" \
-d '{
"titulo": "Cobertura duplex — Santa Rosa",
"preco": 1250000,
"quartos": 4,
"ativo": true
}'
Consejo: llama a GET /database/collections/{collection} antes de escribir. El esquema devuelve el nombre exacto de cada campo, su tipo y las opciones válidas de los campos enum — eso es lo que evita el 422 al escribir.
TMS — Transporte
tms.read
tms.write
Todo el módulo de transporte por HTTP: registrar vehículos y conductores, programar fletes, seguir la entrega factura por factura, registrar incidencias y mantener al día las cuentas por pagar y por cobrar — los mismos datos que ve el operador en el panel.
Estos endpoints solo responden a clientes que usan el diseño TMS. Cualquier otro recibe 403 TMS_NOT_ENABLED, por más alcances que tenga la clave.
Flota — un registro por VEHÍCULO, con la patente como clave y el conductor incluido. El Grid (la lista de disponibilidad del día) es una marca en ese registro, no un alta aparte.
Programación — el PLAN: origen, destino, carga, vehículo. El "code" (Nº de embarque) es secuencial por cliente y lo asigna el servidor.
Monitoreo — la EJECUCIÓN: una fila por factura. Un flete tiene N filas; envíe "nr_embarque" y el servidor hace el vínculo.
CAP/CAR — el dinero, una tabla separada por "direction". Cuentas por pagar y por cobrar nunca se mezclan en un listado.
Módulo y paneles
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /tms |
Índice del módulo: cliente, cantidad de registros y enlaces de los recursos. |
| GET | /tms/overview |
Indicadores del panel — estado de los vehículos, estado de las cargas, cargas por cliente, cargas abiertas por vehículo. |
| GET | /tms/grid |
Lista de disponibilidad de la flota. Filtre con status[]=01..04; por defecto oculta "04 En viaje". |
| GET | /tms/municipios?uf=SP |
Municipios del IBGE de un estado — alimenta el selector de ciudad. |
Flota (vehículos y conductores)
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /tms/fleet |
Lista los vehículos. Filtros: search, placa, classificacao, cadastro_status, uf, cidade, source, vehicle_type_id, body_type_id, in_grid, tem_mopp, per_page, sort, order. |
| GET | /tms/fleet/options |
IDs válidos de los selects del alta: tipos de vehículo, carrocerías, tags de peaje, rastreadoras, estados, clasificaciones. |
| GET | /tms/fleet/by-plate?placa=ABC1D23 |
Encuentra el vehículo por patente. Guion, espacio y mayúsculas no importan. |
| POST | /tms/fleet |
Da de alta un vehículo. placa y nome_motorista son obligatorios; lo demás (licencia, banco, semirremolques, dimensiones) es opcional. |
| GET | /tms/fleet/{uuid} |
Alta completa por UUID, con todo el legajo. |
| PUT | /tms/fleet/{uuid} |
Actualización parcial — solo se validan y graban las claves enviadas. |
| DELETE | /tms/fleet/{uuid} |
Elimina el alta definitivamente. Para sacar el vehículo solo de la lista del día, use el endpoint del Grid. |
| POST | /tms/fleet/{uuid}/grid |
Pone el vehículo en el Grid (lista de disponibilidad). Idempotente. |
| DELETE | /tms/fleet/{uuid}/grid |
Saca el vehículo del Grid. El alta sigue en la flota. |
| PUT | /tms/fleet/{uuid}/grid-status |
Sobrescribe el estado del Grid (01 Vacío, 02 Interesado, 05 Negociado, 04 En viaje; 03 Homologar solo viene del automático). 05 guarda la carga (freight_uuid) y la programa (06, con la placa del vehículo). null vuelve a automático. La placa no queda 05 en dos cargas: con otra carga programada/en curso con ella, vuelve 409 PLATE_ALREADY_NEGOTIATED. |
Programación (fletes)
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /tms/freights |
Lista los fletes. Filtros: status, operational_status, code, vehicle_plate, driver_phone, search, pickup_from, pickup_to, per_page, sort, order. |
| POST | /tms/freights |
Crea un flete. origin y destination son obligatorios; una vehicle_plate que exista en la flota trae conductor y vehículo. |
| GET | /tms/freights/{uuid} |
Busca un flete por UUID, con observaciones y el objeto meta. |
| PUT | /tms/freights/{uuid} |
Actualización parcial. Enviar operational_status sincroniza también el estado grueso. Opcional: `expected_operational_status` (código o lista) — si el flete no está en él, nada cambia y vuelve 409 FREIGHT_STATUS_CHANGED. |
| DELETE | /tms/freights/{uuid} |
Elimina el flete (soft delete). |
| PUT | /tms/freights/{uuid}/status |
Mueve el estado grueso: open, assigned, in_route, delivered, cancelled. |
| GET | /tms/freights/{uuid}/compatible-drivers |
Conductores compatibles con la carga, en tres niveles: flota propia, terceros del mismo revendedor y base de distribución. |
Monitoreo (por factura)
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /tms/monitoring |
Lista las facturas. Filtros: nr_embarque, freight_uuid, nf_entrega, placa, status_entrega_nf, status_embarque, cliente_operacao, uf, data_from, data_to, search. |
| POST | /tms/monitoring |
Crea la fila de una factura. Enviar nr_embarque la vincula al flete y copia patente, conductor y cliente cuando vengan vacíos. |
| GET | /tms/monitoring/{uuid} |
Busca la fila de una factura por UUID. |
| PUT | /tms/monitoring/{uuid} |
Actualización parcial — aquí el rastreador o el WMS graba el estado de la entrega y el comprobante. |
| DELETE | /tms/monitoring/{uuid} |
Elimina la fila de la factura (soft delete). |
Incidencias
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /tms/occurrences |
Lista las incidencias. Filtros: status, type, severity, freight_uuid, pending, search, from, to. |
| POST | /tms/occurrences |
Registra una incidencia: type, severity y title son obligatorios; freight_uuid es opcional (existen incidencias sueltas). |
| GET | /tms/occurrences/{uuid} |
Busca una incidencia por UUID, con descripción y resolución. |
| PUT | /tms/occurrences/{uuid} |
Actualización parcial. Pasar a resolved o closed sella quién la cerró y cuándo. |
| DELETE | /tms/occurrences/{uuid} |
Elimina la incidencia (soft delete). |
Cuentas por pagar y por cobrar
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /tms/cap |
Cuentas por pagar (CAP) — el mismo listado con la dirección ya fijada. |
| GET | /tms/car |
Cuentas por cobrar (CAR) — el mismo listado con la dirección ya fijada. |
| GET | /tms/finance |
Lista los asientos. direction es obligatorio. Filtros: status, category, freight_uuid, counterparty, due_from, due_to, pending, search. |
| POST | /tms/finance |
Crea un asiento. direction, category, description y amount_brl son obligatorios. |
| GET | /tms/finance/{uuid} |
Busca un asiento por UUID, con referencia y observaciones. |
| PUT | /tms/finance/{uuid} |
Actualización parcial. direction no cambia — para corregir, anule y vuelva a emitir. |
| DELETE | /tms/finance/{uuid} |
Elimina el asiento (soft delete). |
| POST | /tms/finance/{uuid}/pay |
Salda el asiento. Sin cuerpo es pago total; un amount_paid_brl menor lo deja parcial. |
Listas de valores (altas auxiliares)
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /tms/option-lists |
Todas las listas de valores de una vez. Acote con ?lists=operational_statuses,finance_categories. |
| GET | /tms/option-lists/{list} |
Una lista con sus valores y cuántos registros usan cada uno. |
| POST | /tms/option-lists/{list} |
Agrega un valor. stage aplica a operational_statuses; directions aplica a finance_categories. |
| PUT | /tms/option-lists/{list}/{id} |
Renombra, cambia el color, reordena o desactiva un valor. |
| DELETE | /tms/option-lists/{list}/{id} |
Elimina un valor. Un valor en uso o del sistema se rechaza — en ese caso, desactívelo. |
Ejemplo de petición
curl -X POST https://zapini.app/api/v1/tms/freights \
-H "Authorization: Bearer zpk_your_51_char_key_here" \
-H "Content-Type: application/json" \
-d '{
"origin": "Santos/SP",
"destination": "Betim/MG",
"cargo_description": "Bobinas de aco",
"cargo_weight_kg": 24000,
"freight_value_brl": 7400.00,
"vehicle_plate": "ABC1D23",
"pickup_at": "2026-09-22T08:00:00-03:00",
"delivery_eta": "2026-09-23T18:00:00-03:00",
"operational_status": "06",
"meta": { "nr_carga": "99812", "km": 780, "pedagio": 310.50 }
}'
Ejemplo de respuesta — 201
{
"success": true,
"data": {
"uuid": "9d3f2c1a-7b6e-4d5c-8a9b-0c1d2e3f4a5b",
"code": "1482",
"status": "assigned",
"operational_status": "06",
"origin": "Santos/SP",
"destination": "Betim/MG",
"driver_name": "Joao da Silva",
"driver_phone": "+5513999999999",
"vehicle_plate": "ABC1D23",
"meta": { "nr_carga": "99812", "km": 780 },
"created_at": "2026-09-17T12:00:00-03:00"
}
}
El Nº de embarque (code) lo asigna el servidor y no cambia: el Monitoreo ata las facturas por él, así que renumerar un embarque dejaría huérfanas las entregas ya cargadas.
operational_status, status_entrega_nf, status_embarque y category solo aceptan valores que existan en las listas del cliente. Lea /tms/option-lists una vez para conocerlos — o cree los suyos.
Todo listado es paginado (per_page hasta 200; meta trae current_page, last_page, per_page y total) y acepta sort y order.
Webhooks (salida)
Una integración personalizada puede registrar una URL de webhook para recibir eventos en tiempo real — por ejemplo message.received y message.sent — entregados como peticiones POST en JSON.
Las claves con database.read también reciben database.record.created, database.record.updated y database.record.deleted cuando cambia un registro de la Base Inteligente — el gancho para revalidar la caché de tu sitio. Estos eventos son del tenant y no llevan instance_uuid.
Las claves con kanban.read también reciben crm.lead.created, crm.lead.updated, crm.lead.moved, crm.lead.won, crm.lead.lost y crm.lead.deleted cada vez que un lead cambia — por cualquier camino: panel, API, formulario de sitio, importación, detección automática o automatización. Son eventos del tenant y no llevan instance_uuid. Movimiento, ganado y perdido tienen evento propio y no se repiten en crm.lead.updated. Eliminar un TABLERO quita sus leads en cascada en la base de datos, sin evento por lead: ese caso emite un único crm.board.deleted con el recuento de lo eliminado.
Cada entrega se firma con HMAC-SHA256. La cabecera X-Zapini-Signature lleva sha256= seguido del HMAC del cuerpo crudo de la petición, calculado con tu secreto de webhook.
El secreto del webhook comienza con zws_ y, al igual que la clave de API, se muestra solo una vez al generarse.
Para verificar una entrega, calcula el HMAC sobre el cuerpo crudo de la petición (antes de cualquier parseo) y compáralo con la cabecera usando una comparación de tiempo constante.
Ejemplo de entrega
POST https://your-app.example.com/webhooks/zapini
Content-Type: application/json
X-Zapini-Signature: sha256=5257a869e7ecebeda32affa27f4245b1...
{
"event": "message.received",
"timestamp": "2026-07-02T12:00:00Z",
"data": {
"conversation_uuid": "550e8400-e29b-41d4-a716-446655440000",
"message": {
"direction": "incoming",
"sender_number": "+5511999999999",
"content": "Hi! I would like a quote."
}
}
}
Verificando la firma (PHP)
$secret = 'zws_your_webhook_secret';
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_ZAPINI_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
// Timing-safe comparison
if (! hash_equals($expected, $signature)) {
http_response_code(401);
exit;
}
¿Listo para construir?
Crea tu primera clave de integración y empieza a llamar a la API en minutos.
Crear una clave de integración