Webhook Reference.

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.

How It Works

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.

MethodPOST
Content-Typeapplication/json
Expected responseA 2xx status code; any body
RetryOnce, only on a network error. HTTP 4xx/5xx responses are not retried.
AddressA 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:

define-webhook.sh
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).

Fields Present in Every Request

Tell events apart by the event_status field. The remaining fields are event-specific.

FieldTypeDescription
event_statusstringThe event code. Each heading below is one value.
complated_timeISO 8601When the request was sent (UTC). The field name is spelled this way.
webhook_adressarrayYour 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 Events

Survey flow events share a common structure: survey is the survey, contact the respondent and questions the questions and answers so far.

FieldTypeDescription
survey.id / survey.namestringSurvey id and name
survey.preDataobjectQuery parameters you added to the survey link (e.g. order number). Absent if the link was opened without parameters.
contact.contactIdstringContact id
contact.namestringWhatsApp profile name. Masked (J*** D**) if "collect contact info" is off for the survey.
contact.phonestringNumber with country code, without the plus sign
questions[].idstringQuestion id
questions[].nameobjectQuestion text keyed by language code: {"TR": "...", "EN": "..."}
questions[].answerobject{"text_reply": "..."} for free-text questions, {"button_reply": "..."} for button/list/star questions. Absent for unanswered questions.
start_timeISO 8601When the event was queued

START — When a survey is started

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

START.json
{
  "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 answered

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

FieldTypeDescription
instantReply.questionIdstringId of the question just answered
instantReply.questionTextobjectQuestion text by language code
instantReply.answerstringWhat the contact typed or selected, raw text
instantReply.preDataobjectSame as survey.preData
ANSWER_GRANTED.json
{
  "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 ends

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

COMPLETED.json
{
  "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 survey

When 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 automatic

When 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:

TIMEOUT.json
{
  "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 automatic

When 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 changed

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

FieldTypeDescription
result._id / result.namestringSurvey id and name
result.languagestringTR, EN, DE, FR, ES, IT, AR, HI, RU, PT_PT
result.keysstringThe short key in the survey link
result.startMessage / endMessagestringOpening and closing messages
result.disposablebooleanWhether it is single use
result.collectUserInfobooleanWhether contact info is collected
result.endDate / endDateActivedate / booleanEnd date and whether it is active
result.tagsarrayTags attached to the survey
result.transactions or result.questionarrayOrdered question list. Named transactions when survey settings are saved and question when a question or option is edited. Process both the same way.
…[].typestringTEXT, INTERACTIVE, IMAGE, VIDEO, DOCUMENT, LOCATION
…[].text / header / footerstring or objectQuestion text; an object keyed by language code for multilingual surveys
…[].response_typestringTEXT, NUMBER, EMAIL, DATE, TIME, NPS, STAR, LOCATION, MEDIA or empty
…[].nextProcess_idstringNext question; absent on the last question
…[].ordernumberOrder number, starting at 0
…[].interactives[]arrayButton/list options: title, description, processGoTo_id (target question or "end")
user_idstringYour account id
SURVEY_FORM_CREATE_OR_UPDATE.json
{
  "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": [ … ]
}

Ticket Events

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 created

When a new ticket is opened from the dashboard or the API. The full ticket record comes under createOneResult.

FieldTypeDescription
createOneResult._idstringTicket id. Matches ticket_id in the other ticket events.
createOneResult.taskIdstringThe ticket number shown in the dashboard (starts at 1001)
createOneResult.statusstringNEW, OPEN, PENDING, CLOSED
createOneResult.subject / messagestringSubject and description
createOneResult.priortystringPriority (the field name is spelled this way)
createOneResult.contactIdstringId of the contact who opened the ticket
createOneResult.assigneeIdstringAssigned user; absent if unassigned
createOneResult.dueDatedateDue date
createOneResult.tagsarrayTag ids
createOneResult.mediaNamearrayNames of attached files
createOneResult.createdAtdateCreation time
TICKET_CREATED.json
{
  "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 changes

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

TICKET_STATUS_CHANGE.json
{
  "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 changes

When 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).

TICKET_ASSIGNEE_CHANGE.json
{
  "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 ticket

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

TICKET_SEND_COMMENT_PANEL.json
{
  "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": [ … ]
}

Chat Events

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 created

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

CHAT_CREATED.json
{
  "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 message

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

CHAT_NORMAL_MESSAGE.json
{
  "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 sent

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

CHAT_TEMPLATE_MESSAGE.json
{
  "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 changes

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

CHAT_STATUS_CHANGE.json
{
  "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 changes

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

CHAT_ASSIGNEE_CHANGE.json
{
  "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": [ … ]
}

Sending Events

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 completes

When 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).

SURVEY_SENDING_COMPLETED.json
{
  "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 completes

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

NOTIFICATION_SENDING_COMPLETED.json
{
  "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": [ … ]
}

Assistant Events

Events about the AI assistant, its intents, localization texts and knowledge base files.

ASSISTANT_SESSION_TIMEOUT — When an assistant session times out

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

ASSISTANT_SESSION_TIMEOUT.json
{
  "contactId": "6612cc33dd44ee55ff667788",
  "event_status": "ASSISTANT_SESSION_TIMEOUT",
  "complated_time": "2026-09-07T12:00:05.000Z",
  "webhook_adress": [ … ]
}

ASSISTANT_INTENT_CHANGE — When an assistant intent changes

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

ASSISTANT_INTENT_CHANGE.json
{
  "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 changes

When assistant texts (localization keys) are added, updated or deleted. The fields depend on the place value:

placeFieldsDescription
CREATEcreatedData, userIdcreatedData is the list of added keys: [{key, value, languageId}]
CREATE_STATICcreatedDataWhen a new language is defined: {name, languageId, cultureCode}
UPDATEDkey, languageId, userIdThe changed key. The new value is not in the payload; read it from the API.
DELETEDkey, userIdThe key was deleted from all languages
ASSISTANT_LOCATION_CHANGE.json
{
  "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 automatic

When 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:

statusFieldsDescription
FILE_ADDEDstageId, files[], userIdfiles are the newly added files: originalName, storageLink, mimeType, size (bytes)
FILE_DELETEDstageId, file, userIdfile is the single deleted file, with the same fields
STAGE_DELETEDstageId, userIdThe whole knowledge base was deleted
FILE_UPLOAD_AGENT.json
{
  "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": [ … ]
}

Couldn't find what you're looking for?

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