Mujib webhook events: every event and its fields
Mujib has 5 webhook events: contact.created, booking.created, booking.cancelled, conversation.handoff and message.received. Every event arrives in the same envelope, and the fields inside data depend on its type. This page is the reference for every field; setup, retries and what your server must answer are in WhatsApp webhook.
A value that does not apply to an event arrives as null; it is never left out of the body. Write your receiver so it ignores any field it does not know.
Which fields do all webhook events share?
Section titled “Which fields do all webhook events share?”Every event is a JSON object with five fixed fields, and all the details sit inside data.
| Field | Type | Meaning |
|---|---|---|
id | string (UUID) | The event id, the same in every webhook that receives this event |
type | string | The event type, matching the X-Mujib-Event header |
createdAt | string | When the event happened, in UTC, for example 2026-01-01T09:30:00.000Z |
workspaceId | string (UUID) | Your business’s id in Mujib, the same in all your events |
data | object | The event’s details, as in the sections below |
Mujib reads the data once, moments after the event, and every retry of a delivery carries that same body. If what the event is about was deleted before then, nothing is sent.
Which fields does contact.created carry?
Section titled “Which fields does contact.created carry?”New customer is sent once per new customer: on the first message from a new sender on WhatsApp, Instagram or Messenger, when you message a number that is not yet a customer from your business phone, or when someone on the team adds a customer in the dashboard.
| Field | Type | Meaning |
|---|---|---|
data.contact.id | string (UUID) | The customer’s id in Mujib |
data.contact.name | string or null | The customer’s saved name |
data.contact.phone | string or null | Their number as saved, for example 962790000000. Usually null for Messenger and Instagram customers |
data.contact.channel | string | Where they came from: whatsapp, instagram, facebook for Messenger, or manual when the team added them |
data.contact.stage | string | The customer’s stage: new, engaged, qualified, won or lost |
data.contact.createdAt | string | When the customer was first seen, in UTC |
{ "id": "5d7c1e2a-3b4f-4c6d-9e8f-0a1b2c3d4e5f", "type": "contact.created", "createdAt": "2026-01-01T00:00:00.000Z", "workspaceId": "7f3e9a10-2c4b-4d5e-8f60-71829a0b1c2d", "data": { "contact": { "id": "c1a2b3c4-d5e6-4f70-8192-a3b4c5d6e7f8", "name": "سارة", "phone": "962790000000", "channel": "whatsapp", "stage": "new", "createdAt": "2026-01-01T00:00:00.000Z" } }}Which fields do booking.created and booking.cancelled carry?
Section titled “Which fields do booking.created and booking.cancelled carry?”Both carry the same fields in data.booking: New booking is sent when the agent books an appointment or the team adds one in the calendar, and Booking cancelled when an appointment is cancelled. A recurring booking sends one event per appointment in it.
| Field | Type | Meaning |
|---|---|---|
id | string (UUID) | The booking id |
status | string | requested, confirmed, cancelled, completed or no_show |
source | string | Who made it: agent or staff |
service | string | The visit type code your business defined |
startsAt | string | The start instant in UTC, for example 2026-11-02T14:30:00Z |
localTime | string | The start in the branch’s local time, with no zone, for example 2026-11-02T17:30:00 |
timezone | string | The branch’s time zone, for example Asia/Amman |
durationMinutes | number or null | The length in minutes; null means your business’s default length |
branch | object or null | The branch: id and name |
specialist | string or null | The specialist’s name, when one is assigned |
customer | object | contactId, name and phone. contactId is null when the booking is not linked to a customer in Mujib |
createdAt | string | When the booking was made, in UTC |
{ "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d", "type": "booking.created", "createdAt": "2026-11-01T08:15:02.441Z", "workspaceId": "7f3e9a10-2c4b-4d5e-8f60-71829a0b1c2d", "data": { "booking": { "id": "b7e6d5c4-b3a2-4910-8f7e-6d5c4b3a2910", "status": "confirmed", "source": "agent", "service": "consultation", "startsAt": "2026-11-02T14:30:00Z", "localTime": "2026-11-02T17:30:00", "timezone": "Asia/Amman", "durationMinutes": 30, "branch": { "id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b", "name": "الفرع الرئيسي" }, "specialist": "ليلى", "customer": { "contactId": "c1a2b3c4-d5e6-4f70-8192-a3b4c5d6e7f8", "name": "سارة", "phone": "962790000000" }, "createdAt": "2026-11-01T08:15:01.982Z" } }}Which fields does conversation.handoff carry?
Section titled “Which fields does conversation.handoff carry?”Handed to staff is sent once per handoff to staff, including a handoff that happens because the agent could not reply.
| Field | Type | Meaning |
|---|---|---|
data.conversation.id | string (UUID) | The conversation id |
data.conversation.channel | string | whatsapp, instagram or facebook |
data.conversation.reason | string or null | Why it was handed over: a short code, or a sentence the agent wrote in the conversation’s language. Show it as it is; do not build fixed rules on it |
data.contact | object or null | The customer: id, name and phone |
{ "id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f", "type": "conversation.handoff", "createdAt": "2026-11-01T10:02:47.120Z", "workspaceId": "7f3e9a10-2c4b-4d5e-8f60-71829a0b1c2d", "data": { "conversation": { "id": "f0e1d2c3-b4a5-4697-8879-6a5b4c3d2e1f", "channel": "whatsapp", "reason": "العميلة تطلب عرض سعر خاص لمجموعة" }, "contact": { "id": "c1a2b3c4-d5e6-4f70-8192-a3b4c5d6e7f8", "name": "سارة", "phone": "962790000000" } }}Which fields does message.received carry?
Section titled “Which fields does message.received carry?”New message is sent with every message a customer sends on any channel, and carries its text only, never a file.
| Field | Type | Meaning |
|---|---|---|
data.message.id | string (UUID) | The message id |
data.message.conversationId | string (UUID) | The conversation id |
data.message.channel | string | whatsapp, instagram or facebook |
data.message.type | string | The kind of message, such as text, image, audio, video, document or location |
data.message.text | string or null | The message text, or the transcript of a voice note when one is available, cut after 4000 characters. null for the other kinds |
data.message.createdAt | string | When the message was received, in UTC |
data.contact | object or null | The customer: id, name and phone |
{ "id": "8e9f0a1b-2c3d-4e5f-9a6b-7c8d9e0f1a2b", "type": "message.received", "createdAt": "2026-11-01T18:40:11.305Z", "workspaceId": "7f3e9a10-2c4b-4d5e-8f60-71829a0b1c2d", "data": { "message": { "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "conversationId": "f0e1d2c3-b4a5-4697-8879-6a5b4c3d2e1f", "channel": "instagram", "type": "text", "text": "هل يوجد موعد غدًا مساءً؟", "createdAt": "2026-11-01T18:40:11.298Z" }, "contact": { "id": "d2e3f4a5-b6c7-4d8e-9f0a-1b2c3d4e5f6a", "name": "سارة", "phone": null } }}What does the webhook.test event look like?
Section titled “What does the webhook.test event look like?”The test event arrives when you press Send a test, signed with the same secret as real events, so it is the right way to test your signature check.
- It goes only to the webhook whose button you pressed, with a single attempt and no retries.
- It is not in the list of events you subscribe to, and shows in the log as Test delivery.
{ "id": "0f1e2d3c-4b5a-4968-8776-5a4b3c2d1e0f", "type": "webhook.test", "createdAt": "2026-11-01T12:00:00.000Z", "workspaceId": "7f3e9a10-2c4b-4d5e-8f60-71829a0b1c2d", "data": { "test": true, "message": "This is a test delivery from Mujib. Your endpoint received and can verify it." }}Frequently asked questions
Is there an event for a rescheduled booking?
No. Changing the time or the specialist sends no event. To get a booking's latest state, read it by its id from the Mujib API.
Why is the phone number sometimes empty?
Messenger and Instagram do not share the customer's phone number, so phone stays null unless a number was saved for them in Mujib.
Do the agent's replies arrive as message.received?
No. The event covers incoming customer messages only; replies from the agent and the team are not sent.
What is the difference between the body's id and the X-Mujib-Delivery header?
id identifies the event and is the same in every webhook that receives it. X-Mujib-Delivery identifies one delivery to one webhook and stays the same across retries.