Zapini Integrations API
Build your own integration consuming Zapini resources — Kanban boards, calendar appointments, chat conversations and support tickets — through a simple, predictable REST API.
Create and manage your API keys in the panel under Integrations → "Custom Integrations".
https://zapini.app/api/v1
Authentication
Every request is authenticated with an integration key. Keys are 51 characters long, start with the zpk_ prefix and are generated in the panel under Integrations → "Custom Integrations".
Your key is displayed only once, at creation time. Zapini stores only a SHA-256 hash of it, so it can never be recovered later — if you lose it, revoke the key and create a new one.
Send the key on every request as a Bearer token in the Authorization header. All requests and responses use JSON: set Content-Type: application/json when sending a body and Accept: application/json always.
Example request
curl https://zapini.app/api/v1/kanban/boards \
-H "Authorization: Bearer zpk_your_51_char_key_here" \
-H "Accept: application/json"
Scopes
Each key carries an explicit list of scopes chosen when it is created. A scope grants access to one resource, split into a read half and a write half.
| Scope | What it grants |
|---|---|
kanban.read |
Read Kanban boards, columns, leads and lead activities. |
kanban.write |
Create, update, move and delete Kanban boards, columns and leads. |
calendar.read |
Read calendar appointments. |
calendar.write |
Create, update, confirm, complete, cancel and delete appointments. |
chat.read |
Read conversations and message history. |
chat.write |
Send messages and media, archive conversations, manage tags and automation. |
tickets.read |
Read support tickets. |
tickets.write |
Create and update support tickets. |
database.read |
Read collections and records of the internal database (Base Inteligente). |
database.write |
Create, update and delete records in the internal database. |
tms.read |
Read the transport module: fleet, freights, monitoring, occurrences, CAP/CAR and value lists. |
tms.write |
Create, change and remove transport records. |
GET and HEAD requests require the resource's .read scope; POST, PUT and DELETE requests require the corresponding .write scope.
Default deny: any endpoint outside these four resources is rejected with HTTP 403 and error code ENDPOINT_NOT_IN_SCOPE, no matter which scopes the key holds.
Security & limits
Custom integration keys are designed to be handed to third-party systems safely. The following controls apply to every key:
Rate limiting
Each key may perform up to 300 requests per minute. Above that the API answers HTTP 429 with a retry_after field (in seconds) — back off and retry.
IP allowlist (optional)
Restrict a key to a list of exact IP addresses or IPv4 CIDR ranges. Requests from any other address are rejected with 403 IP_NOT_ALLOWED.
Instance binding (optional)
A key can be bound to a single WhatsApp instance. Bound keys only see that instance's data and never need the instance_uuid parameter.
Revoke & rotate
Keys can be revoked or rotated at any time in the panel. Revocation is immediate — requests made with a revoked key receive 401.
HTTPS only
The API is served exclusively over HTTPS. Plain HTTP requests are not accepted.
Errors
Errors use conventional HTTP status codes plus a machine-readable error code in the JSON body.
| Status | Error code | Description |
|---|---|---|
| 401 |
INVALID_INTEGRATION_KEY
|
The key is missing, malformed, revoked or unknown. |
| 403 |
INSUFFICIENT_SCOPE
|
The key lacks the scope this endpoint requires. The payload includes required_scope and granted_scopes to help you diagnose it. |
| 403 |
ENDPOINT_NOT_IN_SCOPE
|
The endpoint is outside the resources covered by the Integrations API (default deny). |
| 403 |
IP_NOT_ALLOWED
|
The request came from an IP address outside the key's allowlist. |
| 429 |
RATE_LIMITED
|
Too many requests. Wait retry_after seconds before retrying. |
| 422 | — | Validation failed. Standard Laravel format: a message plus an errors object keyed by field name. |
| 404 |
DATABASE_NOT_CONFIGURED
|
The tenant has no Base Inteligente database configured, or the key is not bound to one. |
| 404 |
COLLECTION_NOT_FOUND
|
The collection does not exist or is outside this key's allowlist. |
| 404 |
RECORD_NOT_FOUND
|
No such record in that collection. |
| 403 |
TMS_NOT_ENABLED
|
The TMS module is not enabled for this tenant (crm_layout is not "tms"). |
| 422 |
DIRECTION_REQUIRED
|
Listing finance entries requires direction=payable (CAP) or direction=receivable (CAR). |
Example response — 403 INSUFFICIENT_SCOPE
{
"success": false,
"error": "INSUFFICIENT_SCOPE",
"required_scope": "kanban.write",
"granted_scopes": ["kanban.read", "chat.read"]
}
Kanban
kanban.read
kanban.write
Manage sales boards, columns and leads — everything you see in the panel's Kanban module.
Boards are scoped per WhatsApp instance. Pass the instance_uuid query/body parameter on board-level calls — unless your key is bound to an instance, in which case it is implied.
The canonical prefix is /crm. The former /kanban still serves exactly the same routes, with the same payloads — no published integration has to change.
| Method | Endpoint | Description |
|---|---|---|
| Boards | ||
| GET | /crm/boards |
List the instance's boards. |
| POST | /crm/boards |
Create a board. |
| GET | /crm/boards/{uuid} |
Get a board with its columns. |
| PUT | /crm/boards/{uuid} |
Update a board (name, color...). |
| DELETE | /crm/boards/{uuid} |
Delete a board. |
| GET | /crm/boards/{uuid}/stats |
Board totals: open, won, lost, values and the per-column breakdown. |
| POST | /crm/boards/{uuid}/duplicate |
Duplicate the board with its columns (leads not copied). |
| Columns | ||
| GET | /crm/boards/{uuid}/columns |
List the board's columns. |
| POST | /crm/boards/{uuid}/columns |
Add a column to the board. |
| POST | /crm/boards/{uuid}/columns/reorder |
Reorder the board's columns. |
| PUT | /crm/columns/{uuid} |
Update a column. |
| DELETE | /crm/columns/{uuid} |
Delete a column. |
| POST | /crm/columns/{uuid}/clear |
Delete every lead in the column. |
| Leads | ||
| GET | /crm/leads |
Search leads across the whole CRM (by phone, text, status, assignee or date range). Always paginated. |
| GET | /crm/boards/{uuid}/leads |
List the leads on a board. |
| POST | /crm/leads |
Create a lead in a column. |
| GET | /crm/leads/{uuid} |
Get a lead. |
| PUT | /crm/leads/{uuid} |
Update a lead. |
| DELETE | /crm/leads/{uuid} |
Delete a lead. |
| POST | /crm/leads/{uuid}/move |
Move a lead to another column/position. |
| POST | /crm/leads/{uuid}/won |
Mark a lead as won. |
| POST | /crm/leads/{uuid}/lost |
Mark a lead as lost. |
| POST | /crm/leads/{uuid}/note |
Add a note to the lead's timeline. |
| GET | /crm/leads/{uuid}/activities |
List the lead's activity history. |
| POST | /crm/leads/{uuid}/assign |
Set the lead assignees. |
| POST | /crm/leads/{uuid}/ignore |
Discard the lead and block the number in auto-detection. |
| POST | /crm/leads/{uuid}/convert-to-ticket |
Open a ticket from the lead (idempotent). |
| POST | /crm/boards/{uuid}/leads/import |
Import up to 500 leads in one call, with a per-row report. |
| POST | /crm/leads/from-conversation |
Create a lead from a chat conversation. |
| POST | /crm/leads/from-messages |
Create a lead from specific messages. |
| GET | /crm/conversations/{uuid}/lead |
Get the lead linked to a conversation. |
| Lead attachments | ||
| GET | /crm/leads/{uuid}/attachments |
List the lead attachments. |
| POST | /crm/leads/{uuid}/attachments |
Upload an attachment to the lead (up to 25 MB, 10 per lead). |
| GET | /crm/leads/{uuid}/attachments/{attachmentUuid} |
Download a lead attachment. |
| DELETE | /crm/leads/{uuid}/attachments/{attachmentUuid} |
Delete a lead attachment. |
| AI on the lead | ||
| POST | /crm/leads/{uuid}/ai-qualify |
Score the lead with AI and route it by the score. |
| POST | /crm/leads/{uuid}/ai-suggest |
Suggest the next action for the lead with AI. |
| Automations | ||
| GET | /crm/boards/{uuid}/automations |
List the board automations. |
| POST | /crm/boards/{uuid}/automations |
Create an automation (trigger + action). |
| PUT | /crm/automations/{uuid} |
Update an automation. |
| DELETE | /crm/automations/{uuid} |
Delete an automation. |
| POST | /crm/automations/{uuid}/test |
Run the automation against a real lead to check the configuration. |
| Auto-detection | ||
| GET | /crm/boards/{uuid}/auto-detection |
Read the board lead auto-detection rules. |
| PUT | /crm/boards/{uuid}/auto-detection |
Save the board auto-detection rules. |
| POST | /crm/boards/{uuid}/auto-detection/preview |
Preview what would be imported, without creating anything. |
| POST | /crm/boards/{uuid}/auto-detection/sync |
Create leads from the items picked in the preview. |
| GET | /crm/boards/{uuid}/ignored-contacts |
List the contacts the board ignores. |
| POST | /crm/boards/{uuid}/ignored-contacts |
Return an ignored contact to detection. |
| Custom fields | ||
| GET | /crm/field-definitions |
Custom-field schema for building the form. |
| GET | /crm/boards/{uuid}/fields |
Manage fields: includes disabled ones and how many records already hold a value in each. |
| POST | /crm/boards/{uuid}/fields |
Create a custom field on the board. |
| POST | /crm/boards/{uuid}/fields/reorder |
Reorder the board fields. |
| PUT | /crm/fields/{id} |
Update a custom field (the internal name never changes). |
| DELETE | /crm/fields/{id} |
Remove a field from the form (stored values are kept). |
| Configuration and helpers | ||
| GET | /crm/config |
The tenant CRM configuration (standard, TMS or VC layout). |
| GET | /crm/users |
Users who can be assigned to a lead. |
| GET | /crm/tags |
Tenant tags, used in the detection filters. |
Example request
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
}'
Calendar
calendar.read
calendar.write
Create and manage the appointments shown in the panel's calendar, including status transitions.
| Method | Endpoint | Description |
|---|---|---|
| GET | /calendar/appointments |
List appointments (filter by period/status). |
| POST | /calendar/appointments |
Create an appointment. |
| GET | /calendar/appointments/{uuid} |
Get an appointment. |
| PUT | /calendar/appointments/{uuid} |
Update an appointment. |
| DELETE | /calendar/appointments/{uuid} |
Delete an appointment. |
| POST | /calendar/appointments/{uuid}/confirm |
Mark the appointment as confirmed. |
| POST | /calendar/appointments/{uuid}/complete |
Mark the appointment as completed. |
| POST | /calendar/appointments/{uuid}/cancel |
Cancel the appointment. |
| POST | /calendar/appointments/{uuid}/to-kanban |
Create a Kanban lead from the appointment. |
Example request
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
Read conversations and messages, and send text or media through your connected WhatsApp instance.
| Method | Endpoint | Description |
|---|---|---|
| GET | /chat/conversations |
List chat conversations. |
| GET | /chat/conversations/{id} |
Get one conversation. |
| GET | /chat/conversations/{id}/messages |
List a conversation's messages. |
| POST | /chat/send |
Send a text message (body: conversation or recipient, plus message). |
| POST | /chat/send-media |
Send a media message (image, audio, video, document). |
| POST | /messages/send |
Queue an outgoing message (messages API). |
| GET | /messages |
List messages (filterable). |
| GET | /conversations |
List conversations (conversations resource). |
| POST | /conversations/{id}/archive |
Archive / unarchive a conversation. |
| POST | /conversations/{id}/tags |
Manage a conversation's tags. |
| POST | /conversations/{id}/automation/pause |
Pause AI automation on the conversation. |
| POST | /conversations/{id}/automation/resume |
Resume AI automation on the conversation. |
Example request
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
Open and track support tickets programmatically.
| Method | Endpoint | Description |
|---|---|---|
| GET | /tickets |
List tickets. Filters: status (open, in_progress, waiting, resolved, closed), priority (low, medium, high, urgent), category, search, per_page. |
| POST | /tickets |
Create a ticket. subject is required; optional: description, priority, category, contact_name, contact_number, tags[], due_at. |
| GET | /tickets/{uuid} |
Get a ticket by UUID. |
| PUT | /tickets/{uuid} |
Update fields and/or transition the status. |
Example request
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"]
}'
Example response — 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"
}
}
Database (Base Inteligente)
database.read
database.write
Base Inteligente is the tenant's internal database — properties, menu items, products, services, staff, whatever the vertical calls for. This API turns it into a headless CMS: your website reads and writes exactly the same records the client administers in the Zapini panel or the public portal. Nothing is duplicated, there is no sync to keep.
A key resolves to ONE Base Inteligente database of the tenant and, optionally, to a list of allowed collections. A collection outside that list answers 404 — the same as one that does not exist.
| Method | Endpoint | Description |
|---|---|---|
| GET | /database |
Database info: vertical (category) and the collections this key can see. |
| GET | /database/collections |
Just the collection list, with display name and record count. |
| GET | /database/collections/{collection} |
Field schema of a collection: type, required flag, enum options and references. Use it to build dynamic forms. |
| GET | /database/collections/{collection}/records |
List records with search, filters, sorting, projection and pagination. |
| POST | /database/collections/{collection}/records |
Create a record. The body is filtered against the schema — unknown fields are ignored. |
| GET | /database/collections/{collection}/records/{id} |
Fetch one record by id. |
| PUT | /database/collections/{collection}/records/{id} |
Update a record (PUT or PATCH). Send only the fields that change. |
| DELETE | /database/collections/{collection}/records/{id} |
Delete a record. |
| POST | /database/uploads |
Upload a file (multipart, field file) to the tenant gallery and get back the public URL to store in an image field. |
Querying records
The listing endpoint accepts the parameters below. Field names are validated against the collection schema and operators come from a fixed allowlist — the query string never reaches the database raw.
| Parameter | Description |
|---|---|
page |
Page number (default 1). |
per_page |
Records per page (default 25, maximum 100). |
search |
Free-text search across the text, e-mail, phone, document, URL and enum fields of the collection. |
sort / order |
Sort field (from the schema, or created_at / updated_at) and direction asc | desc. Defaults to newest first. |
fields |
Comma-separated list of fields to return. id always comes back. |
filter[campo] |
Equality: filter[ativo]=true, filter[cidade]=Niterói. |
filter[campo][op] |
With an operator: filter[preco][gte]=500000, filter[tipo][in]=casa,apartamento. |
eq
ne
gt
gte
lt
lte
in
nin
like
starts
ends
exists
Example request
# Active listings up to R$ 800k, cheapest first
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"
Example response — 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 }
}
Creating a record
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
}'
Tip: call GET /database/collections/{collection} before writing. The schema returns each field's exact name, its type and the valid options of enum fields — that is what keeps writes from failing with 422.
TMS — Transport
tms.read
tms.write
The whole transport module over HTTP: register vehicles and drivers, plan freights, track deliveries note by note, log incidents and keep payables and receivables in sync — the same data the operator sees in the panel.
These endpoints only answer for tenants running the TMS layout. Any other tenant gets 403 TMS_NOT_ENABLED, whatever scopes the key carries.
Fleet — one record per VEHICLE, keyed by plate, with its driver. The Grid (today's availability list) is a flag on that record, not a separate register.
Freights — the PLAN: origin, destination, cargo, vehicle. "code" (shipment number) is sequential per tenant and assigned by the server.
Monitoring — the EXECUTION: one row per invoice (NF). One freight has many rows; send "nr_embarque" and the server links them.
CAP/CAR — the money, one table split by "direction". Payables and receivables never mix in a single list.
Module & dashboards
| Method | Endpoint | Description |
|---|---|---|
| GET | /tms |
Module index: tenant, record counts and resource links. |
| GET | /tms/overview |
Dashboard indicators — vehicle status, cargo status, cargo per customer, open loads per vehicle. |
| GET | /tms/grid |
Fleet availability list. Filter with status[]=01..04; default hides "04 On a trip". |
| GET | /tms/municipios?uf=SP |
IBGE municipalities for a state — feeds the city selector. |
Fleet (vehicles & drivers)
| Method | Endpoint | Description |
|---|---|---|
| GET | /tms/fleet |
List vehicles. Filters: 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 |
Valid IDs for the registration selects: vehicle types, body types, toll tags, trackers, statuses, classifications. |
| GET | /tms/fleet/by-plate?placa=ABC1D23 |
Find a vehicle by plate. Hyphen, space and case are ignored. |
| POST | /tms/fleet |
Register a vehicle. placa and nome_motorista are required; everything else (CNH, bank, trailers, dimensions) is optional. |
| GET | /tms/fleet/{uuid} |
Full registration by UUID, including the complete dossier. |
| PUT | /tms/fleet/{uuid} |
Partial update — only the keys you send are validated and written. |
| DELETE | /tms/fleet/{uuid} |
Delete the registration for good. To take the vehicle off today's list only, use the Grid endpoint. |
| POST | /tms/fleet/{uuid}/grid |
Put the vehicle on the Grid (availability list). Idempotent. |
| DELETE | /tms/fleet/{uuid}/grid |
Take the vehicle off the Grid. The registration stays in the fleet. |
| PUT | /tms/fleet/{uuid}/grid-status |
Override the Grid status (01 Empty, 02 Interested, 05 Negotiated, 04 On a trip; 03 To approve only comes from automatic). 05 keeps the load (freight_uuid) and schedules it (06, with the vehicle plate). null returns it to automatic. A plate cannot be 05 on two loads: if another scheduled/ongoing load has it, 409 PLATE_ALREADY_NEGOTIATED is returned. |
Freights (planning)
| Method | Endpoint | Description |
|---|---|---|
| GET | /tms/freights |
List freights. Filters: status, operational_status, code, vehicle_plate, driver_phone, search, pickup_from, pickup_to, per_page, sort, order. |
| POST | /tms/freights |
Create a freight. origin and destination are required; a known vehicle_plate fills in driver and vehicle from the fleet. |
| GET | /tms/freights/{uuid} |
Get a freight by UUID, with notes and the meta object. |
| PUT | /tms/freights/{uuid} |
Partial update. Sending operational_status also syncs the coarse status. Optional: `expected_operational_status` (code or list) — if the freight is not in it, nothing changes and 409 FREIGHT_STATUS_CHANGED is returned. |
| DELETE | /tms/freights/{uuid} |
Soft-delete the freight. |
| PUT | /tms/freights/{uuid}/status |
Move the coarse status: open, assigned, in_route, delivered, cancelled. |
| GET | /tms/freights/{uuid}/compatible-drivers |
Drivers that fit this load, in three tiers: own fleet, third parties of the same reseller, distribution base. |
Monitoring (per invoice)
| Method | Endpoint | Description |
|---|---|---|
| GET | /tms/monitoring |
List invoice rows. Filters: nr_embarque, freight_uuid, nf_entrega, placa, status_entrega_nf, status_embarque, cliente_operacao, uf, data_from, data_to, search. |
| POST | /tms/monitoring |
Create an invoice row. Sending nr_embarque links it to the freight and copies plate, driver and customer when blank. |
| GET | /tms/monitoring/{uuid} |
Get an invoice row by UUID. |
| PUT | /tms/monitoring/{uuid} |
Partial update — this is where a tracker or WMS writes the delivery status and the proof of delivery. |
| DELETE | /tms/monitoring/{uuid} |
Soft-delete the invoice row. |
Occurrences (incidents)
| Method | Endpoint | Description |
|---|---|---|
| GET | /tms/occurrences |
List incidents. Filters: status, type, severity, freight_uuid, pending, search, from, to. |
| POST | /tms/occurrences |
Log an incident: type, severity and title are required; freight_uuid is optional (standalone incidents exist). |
| GET | /tms/occurrences/{uuid} |
Get an incident by UUID, with description and resolution. |
| PUT | /tms/occurrences/{uuid} |
Partial update. Moving to resolved or closed stamps who closed it and when. |
| DELETE | /tms/occurrences/{uuid} |
Soft-delete the incident. |
Payables & receivables
| Method | Endpoint | Description |
|---|---|---|
| GET | /tms/cap |
Payables (CAP) — the same listing with direction already fixed. |
| GET | /tms/car |
Receivables (CAR) — the same listing with direction already fixed. |
| GET | /tms/finance |
List entries. direction is required. Filters: status, category, freight_uuid, counterparty, due_from, due_to, pending, search. |
| POST | /tms/finance |
Create an entry. direction, category, description and amount_brl are required. |
| GET | /tms/finance/{uuid} |
Get an entry by UUID, with reference and notes. |
| PUT | /tms/finance/{uuid} |
Partial update. direction cannot change — cancel and re-issue instead. |
| DELETE | /tms/finance/{uuid} |
Soft-delete the entry. |
| POST | /tms/finance/{uuid}/pay |
Settle the entry. No body means paid in full; a smaller amount_paid_brl makes it partial. |
Value lists (auxiliary registers)
| Method | Endpoint | Description |
|---|---|---|
| GET | /tms/option-lists |
All value lists at once. Narrow with ?lists=operational_statuses,finance_categories. |
| GET | /tms/option-lists/{list} |
One list with its values and how many records use each one. |
| POST | /tms/option-lists/{list} |
Add a value. stage applies to operational_statuses; directions applies to finance_categories. |
| PUT | /tms/option-lists/{list}/{id} |
Rename, recolour, reorder or deactivate a value. |
| DELETE | /tms/option-lists/{list}/{id} |
Remove a value. Values in use or owned by the system are refused — deactivate those instead. |
Example request
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 }
}'
Example response — 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"
}
}
The shipment number (code) is assigned by the server and never changes: Monitoring links invoices by it, so renumbering a shipment would orphan deliveries already posted.
operational_status, status_entrega_nf, status_embarque and category only accept values that exist in the tenant's lists. Read /tms/option-lists once to learn them, or create your own.
Every listing is paginated (per_page up to 200, meta carries current_page, last_page, per_page and total) and accepts sort and order.
Webhooks (outbound)
A custom integration may register a webhook URL to receive real-time events — for example message.received and message.sent — delivered as JSON POST requests.
Keys holding database.read also receive database.record.created, database.record.updated and database.record.deleted whenever a Base Inteligente record changes — the hook to revalidate your site cache. These events are tenant-level and carry no instance_uuid.
Keys with kanban.read also receive crm.lead.created, crm.lead.updated, crm.lead.moved, crm.lead.won, crm.lead.lost and crm.lead.deleted whenever a lead changes — through any path: panel, API, web form, import, auto-detection or automation. These are tenant events and carry no instance_uuid. Moves, wins and losses have their own event and are not repeated as crm.lead.updated. Deleting a BOARD removes its leads by database cascade, with no per-lead event: that case emits a single crm.board.deleted carrying the counts of what was removed.
Every delivery is signed with HMAC-SHA256. The X-Zapini-Signature header carries sha256= followed by the HMAC of the raw request body, computed with your webhook secret.
The webhook secret starts with zws_ and, like the API key, is shown only once when generated.
To verify a delivery, compute the HMAC over the raw request body (before any parsing) and compare it against the header using a timing-safe comparison.
Example delivery
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."
}
}
}
Verifying the signature (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;
}
Ready to build?
Create your first integration key and start calling the API in minutes.
Create an integration key