The complete list of which events are sent to the webhook address you define in the dashboard, and with which fields — real field names, representative values.
When an event occurs, Cxperium sends a single HTTP request to the address registered in the dashboard. Delivery is one-way; your response body is not read. Webhooks are available on the Enterprise plan.
| Method | POST |
| Content-Type | application/json |
| Expected response | A 2xx status code; any body |
| Retry | Once, only on a network error. HTTP 4xx/5xx responses are not retried. |
| Address | A publicly reachable HTTPS endpoint with a valid TLS certificate. Query parameters are preserved as is. |
Defining in the dashboard: on the Webhook screen enter the address, choose which events trigger it and which surveys it covers, then activate it. For a step-by-step walkthrough see Webhook Integration. For chat and ticket events to be delivered, the webhook's survey access must be "All".
Defining through the API: the same record can be created and updated with POST /api/webhook (it updates when the address matches). The body fields are the same as in the dashboard:
curl -X POST https://api.cxperium.com/api/webhook \
-H "apikey: $CX_API_KEY" -H "Content-Type: application/json" \
-d '{
"webhook_adress": "https://example.com/cxperium-hook",
"webhook_time": ["COMPLETED", "ANSWER_GRANTED", "TICKET_CREATED"],
"access_survey": ["*"],
"webhook_active": true
}'
webhook_time is the list of event codes (the headings below). access_survey is a list of survey ids; "*" means all surveys. List the defined webhooks with GET /api/webhook and delete one with DELETE /api/webhook (with webhook_adress in the body).
Tell events apart by the event_status field. The remaining fields are event-specific.
| Field | Type | Description |
|---|---|---|
event_status | string | The event code. Each heading below is one value. |
complated_time | ISO 8601 | When the request was sent (UTC). The field name is spelled this way. |
webhook_adress | array | Your webhook definitions that matched this event. A copy of your own configuration; no need to process it. |
All identifier fields are 24-character hexadecimal strings and all dates are ISO 8601 UTC. Fields without a value are omitted from the JSON; check for presence when processing. "webhook_adress": [ … ] in the examples is an abbreviation.
Survey flow events share a common structure: survey is the survey, contact the respondent and questions the questions and answers so far.
| Field | Type | Description |
|---|---|---|
survey.id / survey.name | string | Survey id and name |
survey.preData | object | Query parameters you added to the survey link (e.g. order number). Absent if the link was opened without parameters. |
contact.contactId | string | Contact id |
contact.name | string | WhatsApp profile name. Masked (J*** D**) if "collect contact info" is off for the survey. |
contact.phone | string | Number with country code, without the plus sign |
questions[].id | string | Question id |
questions[].name | object | Question text keyed by language code: {"TR": "...", "EN": "..."} |
questions[].answer | object | {"text_reply": "..."} for free-text questions, {"button_reply": "..."} for button/list/star questions. Absent for unanswered questions. |
start_time | ISO 8601 | When the event was queued |
START — When a survey is startedWhen the contact taps the survey link and sends the first WhatsApp message, right before the first question is delivered. No question has an answer at this point.
{
"survey": { "id": "6650aabbccddeeff00112233", "name": "Customer Satisfaction Survey", "preData": { "orderId": "SP-99123" } },
"contact": { "contactId": "6612cc33dd44ee55ff667788", "name": "J*** D**", "phone": "905321112233" },
"questions": [
{ "id": "6650bbccddeeff0011223345", "name": { "EN": "How would you rate our service from 1 to 5?" } },
{ "id": "6650bbccddeeff0011223346", "name": { "EN": "Anything you would like to add?" } }
],
"event_status": "START",
"start_time": "2026-09-07T09:45:12.512Z",
"complated_time": "2026-09-07T09:45:12.740Z",
"webhook_adress": [ … ]
}
ANSWER_GRANTED — When each question is answeredWhen the contact's answer passes validation and is saved. In addition to the common structure, only this event contains the instantReply block, which gives the question just answered on its own.
| Field | Type | Description |
|---|---|---|
instantReply.questionId | string | Id of the question just answered |
instantReply.questionText | object | Question text by language code |
instantReply.answer | string | What the contact typed or selected, raw text |
instantReply.preData | object | Same as survey.preData |
{
"survey": { "id": "6650aabbccddeeff00112233", "name": "Customer Satisfaction Survey", "preData": { "orderId": "SP-99123" } },
"contact": { "contactId": "6612cc33dd44ee55ff667788", "name": "J*** D**", "phone": "905321112233" },
"instantReply": {
"questionId": "6650bbccddeeff0011223345",
"questionText": { "EN": "How would you rate our service from 1 to 5?" },
"answer": "5",
"preData": { "orderId": "SP-99123" }
},
"questions": [
{ "id": "6650bbccddeeff0011223345", "name": { "EN": "How would you rate our service from 1 to 5?" }, "answer": { "button_reply": "5" } },
{ "id": "6650bbccddeeff0011223346", "name": { "EN": "Anything you would like to add?" } }
],
"event_status": "ANSWER_GRANTED",
"start_time": "2026-09-07T09:46:02.101Z",
"complated_time": "2026-09-07T09:46:02.330Z",
"webhook_adress": [ … ]
}
COMPLETED — When a survey endsWhen the last question is answered and the closing message is sent. The structure is the same as START; the difference is that every question in questions has its answer field filled.
{
"survey": { "id": "6650aabbccddeeff00112233", "name": "Customer Satisfaction Survey" },
"contact": { "contactId": "6612cc33dd44ee55ff667788", "name": "J*** D**", "phone": "905321112233" },
"questions": [
{ "id": "6650bbccddeeff0011223345", "name": { "EN": "How would you rate our service from 1 to 5?" }, "answer": { "button_reply": "5" } },
{ "id": "6650bbccddeeff0011223346", "name": { "EN": "Anything you would like to add?" }, "answer": { "text_reply": "Thanks, very satisfied." } }
],
"event_status": "COMPLETED",
"start_time": "2026-09-07T09:47:40.000Z",
"complated_time": "2026-09-07T09:47:40.212Z",
"webhook_adress": [ … ]
}
LEAVE — When the contact leaves a surveyWhen the contact types the exit command or taps the "Leave" button during an ongoing survey. The structure is the same as START; answers given so far are in questions. This event goes only to webhooks whose survey access is "All".
TIMEOUT — When a survey has expired automaticWhen a contact sends a message to a survey with an end date that has passed. Not selected separately in the dashboard; it goes to all your active webhooks. It has two forms: if the contact had already started the survey, the structure is the same as START and questions is populated. If the contact tapped an expired link for the first time, there is no questions field and no contact.contactId:
{
"survey": { "id": "6650aabbccddeeff00112233", "name": "Customer Satisfaction Survey" },
"contact": { "name": "Jane Doe", "phone": "905321112233" },
"event_status": "TIMEOUT",
"start_time": "2026-09-07T09:52:00.000Z",
"complated_time": "2026-09-07T09:52:00.180Z",
"webhook_adress": [ … ]
}
REPEATED — When a single-use survey is reopened automaticWhen a contact who already completed a survey marked as "single use" taps the same link again. Not selected separately in the dashboard; it goes to all your active webhooks. The structure is the same as TIMEOUT for an expired link; only the event_status value is REPEATED.
SURVEY_FORM_CREATE_OR_UPDATE — When a survey form is created or changedWhen a survey is created in the dashboard, its settings are saved, a question is edited, or a button/list option is added or removed. In every case the full current definition of the survey is sent; which part changed is not indicated.
| Field | Type | Description |
|---|---|---|
result._id / result.name | string | Survey id and name |
result.language | string | TR, EN, DE, FR, ES, IT, AR, HI, RU, PT_PT |
result.keys | string | The short key in the survey link |
result.startMessage / endMessage | string | Opening and closing messages |
result.disposable | boolean | Whether it is single use |
result.collectUserInfo | boolean | Whether contact info is collected |
result.endDate / endDateActive | date / boolean | End date and whether it is active |
result.tags | array | Tags attached to the survey |
result.transactions or result.question | array | Ordered question list. Named transactions when survey settings are saved and question when a question or option is edited. Process both the same way. |
…[].type | string | TEXT, INTERACTIVE, IMAGE, VIDEO, DOCUMENT, LOCATION |
…[].text / header / footer | string or object | Question text; an object keyed by language code for multilingual surveys |
…[].response_type | string | TEXT, NUMBER, EMAIL, DATE, TIME, NPS, STAR, LOCATION, MEDIA or empty |
…[].nextProcess_id | string | Next question; absent on the last question |
…[].order | number | Order number, starting at 0 |
…[].interactives[] | array | Button/list options: title, description, processGoTo_id (target question or "end") |
user_id | string | Your account id |
{
"result": {
"_id": "66a0f1e2d3c4b5a697887766",
"name": "Customer Satisfaction Survey",
"language": "EN",
"keys": "Kd83Ba7Z",
"disposable": false,
"collectUserInfo": true,
"startMessage": "Welcome to our survey.",
"endMessage": "Thank you for taking part.",
"firstProcess_id": "66a0f1e2d3c4b5a697887767",
"tags": [ { "_id": "66b1c2d3e4f5a60718293a4b", "name": "satisfaction" } ],
"transactions": [
{
"_id": "66a0f1e2d3c4b5a697887768",
"type": "INTERACTIVE",
"header": "Rating",
"text": "Were you satisfied with our service?",
"response_type": "",
"order": 1,
"interactives": [
{ "_id": "66a0f1e2d3c4b5a697887780", "title": "Yes", "processGoTo_id": "end" },
{ "_id": "66a0f1e2d3c4b5a697887781", "title": "No", "processGoTo_id": "66a0f1e2d3c4b5a697887769" }
]
}
],
"createdAt": "2026-09-07T11:14:58.442Z",
"updatedAt": "2026-09-07T11:31:02.900Z"
},
"user_id": "6512b0f4a1c2d3e4f5a6b7c8",
"event_status": "SURVEY_FORM_CREATE_OR_UPDATE",
"complated_time": "2026-09-07T11:31:03.117Z",
"webhook_adress": [ … ]
}
In ticket events, userId is the dashboard user who performed the action and userType tells whether that user is the main account (NORMAL) or a sub-user (SUB).
TICKET_CREATED — When a ticket is createdWhen a new ticket is opened from the dashboard or the API. The full ticket record comes under createOneResult.
| Field | Type | Description |
|---|---|---|
createOneResult._id | string | Ticket id. Matches ticket_id in the other ticket events. |
createOneResult.taskId | string | The ticket number shown in the dashboard (starts at 1001) |
createOneResult.status | string | NEW, OPEN, PENDING, CLOSED |
createOneResult.subject / message | string | Subject and description |
createOneResult.priorty | string | Priority (the field name is spelled this way) |
createOneResult.contactId | string | Id of the contact who opened the ticket |
createOneResult.assigneeId | string | Assigned user; absent if unassigned |
createOneResult.dueDate | date | Due date |
createOneResult.tags | array | Tag ids |
createOneResult.mediaName | array | Names of attached files |
createOneResult.createdAt | date | Creation time |
{
"createOneResult": {
"_id": "665f1a2b3c4d5e6f70819200",
"taskId": "1042",
"status": "NEW",
"subject": "Delivery delay",
"message": "My order has not arrived for 5 days.",
"priorty": "NORMAL",
"contactId": "6612cc33dd44ee55ff667788",
"tags": [ "66201122334455667788990a" ],
"mediaName": [ "invoice.pdf" ],
"comments": [],
"createdAt": "2026-09-07T09:12:44.123Z",
"updatedAt": "2026-09-07T09:12:44.123Z"
},
"userId": "6600aa11bb22cc33dd44ee55",
"userType": "NORMAL",
"event_status": "TICKET_CREATED",
"complated_time": "2026-09-07T09:12:44.400Z",
"webhook_adress": [ … ]
}
TICKET_STATUS_CHANGE — When a ticket's status changesWhen a ticket's status is set to NEW, OPEN, PENDING or CLOSED. Fields: status (new status), ticket_id, tagsId (the ticket's tag ids), userId / userType.
{
"status": "CLOSED",
"ticket_id": "665f1a2b3c4d5e6f70819200",
"tagsId": [ "66201122334455667788990a" ],
"userId": "6600aa11bb22cc33dd44ee55",
"userType": "NORMAL",
"event_status": "TICKET_STATUS_CHANGE",
"complated_time": "2026-09-07T10:02:10.000Z",
"webhook_adress": [ … ]
}
TICKET_ASSIGNEE_CHANGE — When a ticket's assignee changesWhen a ticket is assigned to a user. Fields: assigneeId (the user the ticket was assigned to), ticket_id, tagsId, userId / userType (the user who made the assignment).
{
"assigneeId": "6633445566778899aabbccdd",
"ticket_id": "665f1a2b3c4d5e6f70819200",
"tagsId": [ "66201122334455667788990a" ],
"userId": "6600aa11bb22cc33dd44ee55",
"userType": "NORMAL",
"event_status": "TICKET_ASSIGNEE_CHANGE",
"complated_time": "2026-09-07T10:05:00.000Z",
"webhook_adress": [ … ]
}
TICKET_SEND_COMMENT_PANEL — When a comment is written on a ticketWhen a comment sent to the contact is added to a ticket from the dashboard or the API. Comments marked as "internal note" do not trigger this event; files attached to the comment are not in the payload. Fields: message (comment text), ticket_id, taskId (despite its name, not the ticket number but the ticket's tag ids), userId / userType.
{
"message": "Spoke with the courier, it will be delivered tomorrow.",
"ticket_id": "665f1a2b3c4d5e6f70819200",
"taskId": [ "66201122334455667788990a" ],
"userId": "6633445566778899aabbccdd",
"userType": "SUB",
"event_status": "TICKET_SEND_COMMENT_PANEL",
"complated_time": "2026-09-07T10:08:30.000Z",
"webhook_adress": [ … ]
}
Events for live support chats. chat_id is the chat id; contactId is the contact, and userId and userType identify the agent who performed the action.
CHAT_CREATED — When a chat is createdWhen a chat with a contact is started from the dashboard or the API. If the contact already has an open chat, no new record is created and the existing chat is returned. The fields are under createOneResult: _id (chat id), status (NEW, OPEN, PENDING, CLOSED), contactId, teamId, assigneeId (absent if unassigned), messages, tags, sessionTime.startTime.
{
"createOneResult": {
"_id": "667700112233445566778899",
"status": "NEW",
"contactId": "6612cc33dd44ee55ff667788",
"teamId": "6644556677889900aabbccdd",
"messages": [],
"tags": [],
"sessionTime": { "startTime": "2026-09-07T09:30:00.000Z" },
"createdAt": "2026-09-07T09:30:00.000Z",
"updatedAt": "2026-09-07T09:30:00.000Z"
},
"userId": "6600aa11bb22cc33dd44ee55",
"userType": "NORMAL",
"event_status": "CHAT_CREATED",
"complated_time": "2026-09-07T09:30:00.250Z",
"webhook_adress": [ … ]
}
CHAT_NORMAL_MESSAGE — When an agent sends a messageTriggered when an agent sends a text message to the contact from the dashboard (not on messages from the customer). Line breaks in the message are converted to spaces; media attached to the message is not in the payload. Fields: message, chat_id, contactId, userId / userType.
{
"message": "Hello, how can I help you?",
"chat_id": "667700112233445566778899",
"contactId": "6612cc33dd44ee55ff667788",
"userId": "6600aa11bb22cc33dd44ee55",
"userType": "SUB",
"event_status": "CHAT_NORMAL_MESSAGE",
"complated_time": "2026-09-07T09:31:12.000Z",
"webhook_adress": [ … ]
}
CHAT_TEMPLATE_MESSAGE — When a template message is sentWhen an agent sends a WhatsApp template message from the chat. The template content is not in the payload; the chat id comes as conversationId in this event.
{
"conversationId": "667700112233445566778899",
"userId": "6600aa11bb22cc33dd44ee55",
"userType": "NORMAL",
"event_status": "CHAT_TEMPLATE_MESSAGE",
"complated_time": "2026-09-07T09:33:00.000Z",
"webhook_adress": [ … ]
}
CHAT_STATUS_CHANGE — When a chat's status changesWhen a chat's status is changed manually, when an agent writes the first message to a NEW chat (OPEN), when a chat is closed because the contact was deleted (CLOSED), or when a chat closes through session timeout (CLOSED). Fields: status, chat_id, contactId, userId / userType. userType may be absent on automatic closures.
{
"status": "CLOSED",
"chat_id": "667700112233445566778899",
"contactId": "6612cc33dd44ee55ff667788",
"userId": "6600aa11bb22cc33dd44ee55",
"userType": "NORMAL",
"event_status": "CHAT_STATUS_CHANGE",
"complated_time": "2026-09-07T10:40:00.000Z",
"webhook_adress": [ … ]
}
CHAT_ASSIGNEE_CHANGE — When a chat's assignee changesWhen a chat is assigned to another agent, or when the first agent to write in an unassigned chat takes it over. Fields: assigneeId, chat_id, contactId, userId / userType.
{
"assigneeId": "6633445566778899aabbccdd",
"chat_id": "667700112233445566778899",
"contactId": "6612cc33dd44ee55ff667788",
"userId": "6600aa11bb22cc33dd44ee55",
"userType": "SUB",
"event_status": "CHAT_ASSIGNEE_CHANGE",
"complated_time": "2026-09-07T10:41:00.000Z",
"webhook_adress": [ … ]
}
When a bulk send completes, a single summary event arrives. success and failed are arrays of phone numbers; use the array length for counts.
SURVEY_SENDING_COMPLETED — When a survey send completesWhen delivery has been attempted to every number in a survey sending session. Fields: surveyName, success, failed, startingDate, endDate, preData (custom data attached to the send; absent if none).
{
"surveyName": "Customer Satisfaction Survey",
"success": [ "905321112233", "905334445566" ],
"failed": [ "905559998877" ],
"startingDate": "2026-09-07T08:00:00.000Z",
"endDate": "2026-09-07T08:04:12.000Z",
"event_status": "SURVEY_SENDING_COMPLETED",
"complated_time": "2026-09-07T08:04:12.300Z",
"webhook_adress": [ … ]
}
NOTIFICATION_SENDING_COMPLETED — When a notification send completesWhen delivery has been attempted to every number in a notification (template message) sending session. Same structure as above, with templateName instead of the survey name.
{
"templateName": "campaign_september_2026",
"success": [ "905321112233", "905334445566" ],
"failed": [],
"startingDate": "2026-09-07T08:00:00.000Z",
"endDate": "2026-09-07T08:02:40.000Z",
"event_status": "NOTIFICATION_SENDING_COMPLETED",
"complated_time": "2026-09-07T08:02:41.000Z",
"webhook_adress": [ … ]
}
Events about the AI assistant, its intents, localization texts and knowledge base files.
ASSISTANT_SESSION_TIMEOUT — When an assistant session times outWhen a contact's assistant session is closed because no message arrived for the configured period. If the contact has an open chat, a CHAT_STATUS_CHANGE with status CLOSED is also sent. Single field: contactId.
{
"contactId": "6612cc33dd44ee55ff667788",
"event_status": "ASSISTANT_SESSION_TIMEOUT",
"complated_time": "2026-09-07T12:00:05.000Z",
"webhook_adress": [ … ]
}
ASSISTANT_INTENT_CHANGE — When an assistant intent changesWhen an intent is created, updated or deleted in the dashboard. The place field says what happened (CREATE, UPDATED, DELETED). The intent's regex rules are not in the payload; this event is typically used to refresh a cache. Fields: place, name (intent name), languageId (e.g. en-US), userId.
{
"place": "UPDATED",
"name": "order_status_query",
"languageId": "en-US",
"userId": "6512b0f4a1c2d3e4f5a6b7c8",
"event_status": "ASSISTANT_INTENT_CHANGE",
"complated_time": "2026-09-07T11:45:02.117Z",
"webhook_adress": [ … ]
}
ASSISTANT_LOCATION_CHANGE — When assistant localization changesWhen assistant texts (localization keys) are added, updated or deleted. The fields depend on the place value:
| place | Fields | Description |
|---|---|---|
CREATE | createdData, userId | createdData is the list of added keys: [{key, value, languageId}] |
CREATE_STATIC | createdData | When a new language is defined: {name, languageId, cultureCode} |
UPDATED | key, languageId, userId | The changed key. The new value is not in the payload; read it from the API. |
DELETED | key, userId | The key was deleted from all languages |
{
"place": "UPDATED",
"key": "welcome_message",
"languageId": "en-US",
"userId": "6512b0f4a1c2d3e4f5a6b7c8",
"event_status": "ASSISTANT_LOCATION_CHANGE",
"complated_time": "2026-09-07T11:45:02.117Z",
"webhook_adress": [ … ]
}
FILE_UPLOAD_AGENT — When assistant knowledge files change automaticWhen a file is uploaded to the AI assistant as a knowledge source, a file is deleted, or a knowledge base (stage) is deleted. Not selected separately in the dashboard; it goes to your active webhooks. The "Knowledge bases this bot receives" selection in the webhook definition narrows which knowledge bases' events you receive. The status field says what happened:
| status | Fields | Description |
|---|---|---|
FILE_ADDED | stageId, files[], userId | files are the newly added files: originalName, storageLink, mimeType, size (bytes) |
FILE_DELETED | stageId, file, userId | file is the single deleted file, with the same fields |
STAGE_DELETED | stageId, userId | The whole knowledge base was deleted |
{
"status": "FILE_ADDED",
"stageId": "66f1c2a9b4e5d6f7a8b9c0d1",
"files": [
{
"originalName": "product-catalogue-2026.pdf",
"storageLink": "https://storage.cxperium.com/…/product-catalogue-2026.pdf",
"mimeType": "application/pdf",
"size": 481920
}
],
"userId": "6512b0f4a1c2d3e4f5a6b7c8",
"event_status": "FILE_UPLOAD_AGENT",
"complated_time": "2026-09-07T11:45:02.117Z",
"webhook_adress": [ … ]
}
Let our technical team guide you through your integration — send us your question and we'll get back to you the same day.
Dedicated technical support · Free integration consulting