Cargando...
Documentación para desarrolladores

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".

Abrir Integraciones Personalizadas
URL base 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

Alcances requeridos: 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

Alcances requeridos: 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

Alcances requeridos: 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

Alcances requeridos: 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)

Alcances requeridos: 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.
Operadores aceptados: 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

Alcances requeridos: 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