Skip to navigation

Zapier

Start Zaps from calls, campaigns and contacts with Jelliu's REST Hook triggers.
View as Markdown

The Jelliu app for Zapier turns events in your workspace into Zap triggers: a call ends, a contact converts, a campaign finishes. Zapier subscribes to the event, and Jelliu POSTs each occurrence to Zapier as it happens.

The app works in one direction only: Jelliu tells Zapier what happened. It has triggers and no actions. If you want your agents to act inside the apps you have connected to Zapier (create a CRM deal, add a spreadsheet row) during a conversation, connect Zapier MCP to your agents instead. See Hosted automation providers.

How it works

Generate a Zapier API key (dashboard) zk_... (shown once) Connect the Jelliu account with the zk_ key GET /zapier/triggers/{event} (sample data) POST /zapier/subscribe (Zap turned on) 201 { data: { id, event, target_url } } POST target_url { event, timestamp, data } DELETE /zapier/subscribe/{id} (Zap turned off) A call ends, a contact converts... Workspace owner Jelliu Zapier

The Zapier API key

Zapier authenticates with its own credential, separate from workspace API keys:

Zapier API keyWorkspace API key
Formatzk_ followed by 64 hexadecimal characters (67 characters in total)jl_ followed by 64 hexadecimal characters
Headerx-zapier-api-key: zk_...Authorization: Bearer jl_...
ReachesOnly the /zapier/* endpoints belowThe REST API under /api/*

A zk_ key reads call and contact data out of your workspace and streams it to any URL it subscribes. Treat it like an admin credential: store it only in Zapier, and revoke it when you stop using the integration.

Generate a key

In the dashboard, open Integrations → Zapier → Generate API Key. The key is shown once. Jelliu stores only a keyed hash of it, so a lost key cannot be recovered; generate a new one.

Generating and revoking Zapier keys is restricted to the workspace owner, in a signed-in dashboard session. The underlying endpoints (POST /api/zapier/api-key and DELETE /api/zapier/api-key/{id}) refuse workspace API keys, even full ones, with 403 FORBIDDEN:

This action requires a signed-in admin session — API keys cannot perform credential-management operations.

That way a leaked jl_ key cannot mint a long-lived zk_ credential that would survive revoking it.

Revoke a key

Revoke the key in the same dashboard screen. From then on, requests carrying it receive 401.

Revoking a key does not delete the subscriptions it created. Active Zaps keep receiving events, but Zapier can no longer authenticate to unsubscribe them. Turn off your Zaps first, then revoke the key.

Connect Jelliu in Zapier

1

Pick a trigger

In the Zap editor, search for Jelliu and choose one of the triggers, for example Call Completed.

2

Connect your account

Paste the zk_ key. Zapier checks it with GET /zapier/triggers/contact.created?limit=1, which returns 200 for a valid key and 401 otherwise.

3

Load sample data

Zapier calls GET /zapier/triggers/{event}?limit=3 to show recent records from your workspace while you map fields. Each sample has the same shape as a live event, so the fields you map are there when the Zap runs. See Sample data.

4

Turn the Zap on

Zapier calls POST /zapier/subscribe. From that moment, every matching event is delivered to the Zap.

Triggers

Zapier triggerEventSent whendata fields
Call Completedcall.completedA call ended and was analysed, and it was neither a technical failure nor a voicemail.callId, campaignId, contactId, agentId, phoneNumber, outcome, sentimentScore (null when the call was not scored), duration, summary
Call Failedcall.failedA call ended in a technical failure. Also sent when background reconciliation closes a call that failed or was not answered.Same as Call Completed. Reconciled calls carry callId, agentId, campaignId, contactId, phoneNumber, outcome (failed or no_answer), duration and reconciled: true.
Voicemail Detectedcall.voicemailA call’s outcome is voicemail.Same as Call Completed.
Call Escalatedcall.escalatedA call was handed to a person: the agent transferred it during the call, or an admin completed a warm transfer.callId, phoneNumber, transferredTo, outcome (always escalated_to_human), escalatedBy (see below)
Campaign Completedcampaign.completedA campaign finished working through all of its contacts.campaignId
Contact Convertedcontact.convertedA contact converted, on a call or in a text conversation.From a call: contactId, callId, campaignId. From a conversation: contactId, callId: null, conversationId, campaignId, source: "conversation".
Contact Opted Outcontact.dncA contact opted out, on any channel: asked on a call, sent an opt-out keyword on WhatsApp or a social inbox, unsubscribed by email, or was reached by a campaign while already on the suppression list. Sent once per opt-out.contactId, campaignId, name, phoneNumber, email, whatsappNumber, channel (voice, whatsapp, email or social), basis (request or suppression_list), callId (null outside voice), optedOutAt
New Contactcontact.createdA contact was added, from any source: the dashboard, the API, a bulk or CSV import, an agent, a CRM import or push, or a first message on WhatsApp, web chat, Instagram, Messenger or email. One delivery per contact, so a large import starts one Zap run per row. source says where it came from (see Contact sources).contactId, campaignId, name, phoneNumber, email, whatsappNumber, status, source, createdAt

duration is in seconds. outcome is a string such as sale_closed, callback_scheduled, no_answer, voicemail or escalated_to_human, and can be null when there is no analysis. sentimentScore is null when the call was not scored; 0 is a real neutral score. Treat every field as optional.

Call Escalated has two producers

Since 2026-09-15, Call Escalated fires for the agent’s own hand-overs as well as for admin warm transfers. Tell them apart with escalatedBy:

escalatedByWhotransferredTo
"agent"The agent handed the call to a person during the callnull — this path never learns which number was dialled
absentAn admin completed a warm transferThe destination number

The admin path sends no escalatedBy key at all. A Zap that filters on escalatedBy equals admin will match nothing; filter on escalatedBy equals agent, or treat a missing value as the admin path. Expect many more Call Escalated runs than before — the admin warm transfer was rare, and agent hand-overs are not.

Routing a conversation to another agent of your workspace is deliberately not an escalation and fires nothing: no person picked up.

New Contact fires for every row of an import. Deliveries for a batch are sent one at a time, so a Zap on New Contact can take a while to catch up after a large CSV. If you only care about some contacts, add a Filter step in the Zap.

Zapier payloads are leaner than outbound webhooks: call.completed here does not include dataCollection or kpiData. If you need the extracted data, subscribe a webhook to call.completed instead, or use both.

Delivery

For every active subscription whose event matches, Jelliu sends:

POST {target_url} HTTP/1.1
Content-Type: application/json
X-Zapier-Event: call.completed
X-Zapier-Delivery-Id: 3f1c2b9a-8d4e-4f6a-9b1c-2e3d4f5a6b7c
{
"event": "call.completed",
"timestamp": "2026-09-14T15:42:07.318Z",
"data": {
"callId": "5b0d2f7e-9a41-4c3e-8f0a-2c6d1e7b9a10",
"campaignId": "0f3c8b52-6d1a-4e2f-9b7c-4a5e6d7f8a90",
"contactId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"agentId": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
"phoneNumber": "+573001234567",
"outcome": "callback_scheduled",
"sentimentScore": 0.6,
"duration": 184,
"summary": "El cliente pidió que lo llamen el jueves en la tarde."
}
}

The Jelliu Zapier app adds an id to each record (the callId, contactId or campaignId from data).

SuccessAny 2xx response.
Timeout10 seconds.
RetriesNone. Each event is attempted once per subscription.
SignatureNone. Zapier deliveries are not signed.
X-Zapier-Delivery-IdA new random UUID for each delivery.
Automatic disablingAfter 5 consecutive failed deliveries the subscription is deactivated. A successful delivery resets the count.

A deactivated subscription stops receiving events silently: Zapier is not told. If a Zap stops firing, check is_active and last_error with GET /api/zapier, then turn the Zap off and on again in Zapier, which creates a fresh subscription.

contact.dnc is sent once per contact, when it becomes opted out, whichever channel the request came from. A repeated request sends nothing. One opt-out can cover several contacts that share a number or address, and each one gets its own delivery.

Endpoints

The Zapier app calls these for you. You can also call them directly, for example to feed the same REST Hooks into your own automation tool.

All /zapier/* endpoints authenticate with x-zapier-api-key.

Subscribe

POST /zapier/subscribe

event
stringRequired

One of call.completed, call.failed, call.voicemail, call.escalated, campaign.completed, contact.created, contact.converted, contact.dnc.

target_url
stringRequired

Where events are delivered. A valid URL of up to 2000 characters that resolves to a public address: private, internal and loopback targets are rejected.

curl -sS -X POST "https://api.jelliu.co/zapier/subscribe" \
-H "x-zapier-api-key: $JELLIU_ZAPIER_KEY" \
-H "Content-Type: application/json" \
-d '{
"event": "call.completed",
"target_url": "https://hooks.example.com/jelliu/call-completed"
}'

Response 201:

{
"data": {
"id": "c9a4e1f2-3b5d-4c6e-8f7a-9b0c1d2e3f4a",
"event": "call.completed",
"target_url": "https://hooks.example.com/jelliu/call-completed"
}
}

Keep data.id: it is what you pass to unsubscribe.

Unsubscribe

DELETE /zapier/subscribe/{id}

curl -sS -X DELETE "https://api.jelliu.co/zapier/subscribe/c9a4e1f2-3b5d-4c6e-8f7a-9b0c1d2e3f4a" \
-H "x-zapier-api-key: $JELLIU_ZAPIER_KEY"

Response 200:

{ "data": { "removed": true } }

An unknown or already removed id returns 404 with code SUBSCRIPTION_NOT_FOUND and the message Zapier subscription not found.

Sample data

GET /zapier/triggers/{event}?limit=10

Returns the newest records for the event as a flat JSON array, without a response wrapper, which is the shape Zapier expects. Each record has the shape of a live delivery, { id, event, timestamp, data }. limit accepts 1 to 100 and defaults to 10. A page parameter is accepted and ignored.

curl -sS "https://api.jelliu.co/zapier/triggers/call.completed?limit=3" \
-H "x-zapier-api-key: $JELLIU_ZAPIER_KEY"
[
{
"id": "5b0d2f7e-9a41-4c3e-8f0a-2c6d1e7b9a10",
"event": "call.completed",
"timestamp": "2026-09-14T15:42:07.318Z",
"data": {
"callId": "5b0d2f7e-9a41-4c3e-8f0a-2c6d1e7b9a10",
"campaignId": "0f3c8b52-6d1a-4e2f-9b7c-4a5e6d7f8a90",
"contactId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"agentId": "7c1d9f30-5e2a-4a7e-9c8f-91a0b1c2d3e4",
"phoneNumber": "+573001234567",
"outcome": "callback_scheduled",
"sentimentScore": 0.6,
"duration": 184,
"summary": "El cliente pidió que lo llamen el jueves en la tarde."
}
}
]

Samples use the same envelope and the same camelCase data fields as live events (see Triggers), so a Zap built from a sample maps fields that exist on every delivery:

FieldIn a sample
idThe call, campaign or contact ID, the value Zapier uses to tell records apart. The Jelliu Zapier app adds the same id to live events.
eventThe trigger’s event name.
timestampWhen the record last changed: the call or campaign’s last update, the contact’s last update for contact.converted, the opt-out time for contact.dnc, and the creation time for contact.created.
dataThe trigger’s fields. A value the stored record cannot tell is null: callId for contact.converted, and channel, basis and callId for contact.dnc.

Samples come from records already in your workspace, so a new workspace can return [] for a trigger until the first matching call, campaign or contact exists.

How samples are selected:

EventNewest records where
call.completedcall status is completed
call.failed, call.voicemailcall outcome is failed / voicemail
call.escalatedcall outcome is escalated_to_human
campaign.completedcampaign status is completed
contact.converted, contact.dnccontact status is converted / dnc
contact.createdany contact

List subscriptions

GET /api/zapier lists the workspace’s Zapier rows. Unlike the endpoints above, it is part of the REST API: authenticate with a workspace API key (read is enough).

curl -sS "https://api.jelliu.co/api/zapier" \
-H "Authorization: Bearer $JELLIU_API_KEY"
{
"data": [
{
"id": "c9a4e1f2-3b5d-4c6e-8f7a-9b0c1d2e3f4a",
"tenant_id": "9f0fafb4-1c2d-4e5f-8a9b-0c1d2e3f4a5b",
"event": "call.completed",
"target_url": "https://hooks.zapier.com/hooks/standard/123456/abcdef/",
"is_active": true,
"last_delivered_at": "2026-09-14T15:42:07.512Z",
"delivery_count": 42,
"failure_count": 0,
"last_error": null,
"created_at": "2026-09-01T10:00:00.000Z",
"updated_at": "2026-09-14T15:42:07.512Z",
"deleted_at": null
}
]
}

Rows whose event is __api_key__ represent generated Zapier API keys, not subscriptions; their id is what the dashboard revokes. The key itself is never returned.

Errors

StatusCodeWhen
400VALIDATION_FAILEDUnknown event, invalid or disallowed target_url, or a malformed id.
401UNAUTHORIZEDMissing or invalid API key (header absent or not a 67-character zk_ key) or Invalid API key (unknown or revoked).
403PLAN_LIMIT_EXCEEDEDMaximum 50 Zapier credentials per tenant.
403FORBIDDENThe workspace has not accepted the Data Processing Agreement: You must accept the Data Processing Agreement before using the platform. Please visit the settings page.
403TENANT_SUSPENDEDThe workspace is suspended.
404SUBSCRIPTION_NOT_FOUNDUnsubscribing an id that does not exist or was already removed.
429RATE_LIMIT_EXCEEDEDMore than 200 requests per minute from one IP.

Limits

  • 50 per workspace, keys and subscriptions combined. Every active Zapier API key and every active subscription counts toward the same ceiling of 50. Remove Zaps you no longer use.
  • Rate limit. /zapier/* accepts 200 requests per minute per client IP.
  • No plan gate. The Zapier triggers are available on every plan.