> ## Documentation Index
> Fetch the complete documentation index at: https://developer.meetergo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Events

> Detailed payload documentation for each webhook event

This page documents the payload structure for each webhook event type.

## booking\_created

Triggered when a new appointment is booked.

### Payload

```json theme={null}
{
  "webhookType": "booking_created",
  "id": "appt-uuid-12345",
  "secret": "secret-token-xyz",
  "start": "2024-01-15T09:00:00+01:00",
  "end": "2024-01-15T09:30:00+01:00",
  "location": null,
  "isCancelled": false,
  "rescheduledAt": null,
  "meetingInfo": {
    "channel": "zoom",
    "meetingLink": "https://zoom.us/j/123456789"
  },
  "attendees": [
    {
      "id": "att-uuid-789",
      "email": "customer@example.com",
      "firstname": "Jane",
      "lastname": "Doe",
      "fullname": "Jane Doe",
      "phone": "+1-555-123-4567",
      "timezone": "America/New_York",
      "language": "en"
    }
  ],
  "hosts": [
    {
      "id": "host-uuid-123",
      "email": "john@example.com",
      "givenName": "John",
      "familyName": "Smith",
      "fullName": "John Smith",
      "slug": "john-smith",
      "picture": "https://example.com/photo.jpg"
    }
  ],
  "hostIds": ["host-uuid-123"],
  "meetingType": {
    "id": "mt-uuid-456",
    "slug": "discovery-call",
    "meetingInfo": {
      "name": "Discovery Call",
      "duration": 30
    },
    "metadata": {
      "ticketId": "EVAL-12345",
      "source": "crm-integration"
    }
  },
  "meetingTypeId": "mt-uuid-456",
  "companyId": "company-uuid-789",
  "createdAt": "2024-01-14T15:30:00Z",
  "updatedAt": "2024-01-14T15:30:00Z"
}
```

### Key Fields

| Field | Type | Description |
| - | - | - |
| `id` | string | Unique appointment identifier |
| `secret` | string | Token for cancel/reschedule operations |
| `start` | string | Start time (ISO 8601) |
| `end` | string | End time (ISO 8601) |
| `attendees` | array | List of attendee objects |
| `hosts` | array | List of host objects |
| `meetingType` | object | Meeting type details |
| `meetingType.metadata` | object | Custom key-value data set when creating the meeting type |
| `meetingInfo.channel` | string | Video platform used |
| `meetingInfo.meetingLink` | string | Video meeting URL |

## booking\_cancelled

Triggered when an appointment is cancelled by the host or attendee. For group bookings, this also fires when a single attendee cancels their spot — in that case `isCancelled` is `false` (the appointment remains active for other attendees) but the `cancel` object is still populated.

### Payload

```json theme={null}
{
  "webhookType": "booking_cancelled",
  "id": "appt-uuid-12345",
  "secret": "secret-token-xyz",
  "start": "2024-01-15T09:00:00+01:00",
  "end": "2024-01-15T09:30:00+01:00",
  "location": null,
  "isCancelled": true,
  "cancel": {
    "actionAt": "2024-01-14T18:00:00Z",
    "actionSource": "attendee",
    "reason": "Schedule conflict",
    "actionBy": null
  },
  "attendees": [
    {
      "id": "att-uuid-789",
      "email": "customer@example.com",
      "firstname": "Jane",
      "lastname": "Doe",
      "fullname": "Jane Doe"
    }
  ],
  "hosts": [
    {
      "id": "host-uuid-123",
      "email": "john@example.com",
      "givenName": "John",
      "familyName": "Smith",
      "fullName": "John Smith"
    }
  ],
  "hostIds": ["host-uuid-123"],
  "meetingType": {
    "id": "mt-uuid-456",
    "slug": "discovery-call"
  },
  "meetingTypeId": "mt-uuid-456",
  "companyId": "company-uuid-789",
  "createdAt": "2024-01-14T15:30:00Z",
  "updatedAt": "2024-01-14T18:00:00Z"
}
```

### Key Fields

| Field | Type | Description |
| - | - | - |
| `isCancelled` | boolean | `true` for full cancellations, `false` when a single attendee leaves a group booking |
| `cancel.actionAt` | string | When the cancellation occurred (ISO 8601) |
| `cancel.actionSource` | string | Who cancelled: `attendee`, `host`, or `company` (admin) |
| `cancel.reason` | string \| null | Optional cancellation reason |
| `cancel.actionBy` | object \| null | Host/admin who cancelled. `null` when the attendee cancels |
| `cancel.actionBy.id` | string | User ID of the host/admin |
| `cancel.actionBy.email` | string | Email of the host/admin |

## booking\_rescheduled

Triggered when an appointment is moved to a new time.

### Payload

```json theme={null}
{
  "webhookType": "booking_rescheduled",
  "oldStartDate": "2024-01-15T09:00:00+01:00",
  "rescheduledAppointment": {
    "id": "appt-uuid-12345",
    "secret": "secret-token-xyz",
    "start": "2024-01-16T10:00:00+01:00",
    "end": "2024-01-16T10:30:00+01:00",
    "location": null,
    "isCancelled": false,
    "rescheduledAt": "2024-01-14T18:30:00Z",
    "attendees": [
      {
        "id": "att-uuid-789",
        "email": "customer@example.com",
        "firstname": "Jane",
        "lastname": "Doe",
        "fullname": "Jane Doe"
      }
    ],
    "hosts": [
      {
        "id": "host-uuid-123",
        "email": "john@example.com",
        "givenName": "John",
        "familyName": "Smith",
        "fullName": "John Smith"
      }
    ],
    "hostIds": ["host-uuid-123"],
    "meetingType": {
      "id": "mt-uuid-456",
      "slug": "discovery-call"
    },
    "meetingTypeId": "mt-uuid-456",
    "companyId": "company-uuid-789",
    "createdAt": "2024-01-14T15:30:00Z",
    "updatedAt": "2024-01-14T18:30:00Z"
  }
}
```

### Key Fields

| Field | Type | Description |
| - | - | - |
| `oldStartDate` | string | Original start time |
| `rescheduledAppointment` | object | Updated appointment details |
| `rescheduledAppointment.start` | string | New start time |
| `rescheduledAppointment.rescheduledAt` | string | When the reschedule occurred |

## new\_employee

Triggered when a new user is added to your company.

### Payload

```json theme={null}
{
  "webhookType": "new_employee",
  "id": "user-uuid-456",
  "email": "new.hire@example.com",
  "givenName": "Sarah",
  "familyName": "Johnson",
  "fullName": "Sarah Johnson",
  "companyId": "company-uuid-789",
  "userType": "REGULAR",
  "createdAt": "2024-01-14T10:00:00Z"
}
```

### Key Fields

| Field | Type | Description |
| - | - | - |
| `id` | string | User's unique identifier |
| `email` | string | User's email address |
| `givenName` | string | First name |
| `familyName` | string | Last name |
| `userType` | string | `REGULAR` or `API_PLATFORM` |

## form\_submission

Triggered when someone submits a routing form or funnel.

### Payload

```json theme={null}
{
  "webhookType": "form_submission",
  "formId": "form-uuid-123",
  "formName": "Contact Form",
  "formSlug": "contact-form",
  "companyId": "company-uuid-789",
  "workspaceId": "workspace-uuid-456",
  "submissionData": {
    "company_size": "50-200",
    "use_case": "sales"
  },
  "submittedAt": "2024-01-15T10:00:00.000Z",
  "contact": {
    "id": "contact-uuid-789",
    "email": "lead@example.com",
    "firstName": "Sarah",
    "lastName": "Johnson",
    "phoneNumber": "+1-555-987-6543"
  }
}
```

### Key Fields

| Field | Type | Description |
| - | - | - |
| `formId` | string | Unique form identifier |
| `formName` | string | Display name of the form |
| `formSlug` | string | URL-friendly form identifier |
| `submissionData` | object | Key-value pairs from form fields |
| `submittedAt` | string | When the form was submitted (ISO 8601) |
| `contact` | object \| null | Contact info if available |
| `contact.email` | string | Contact's email address |
| `space` | object | Present when the form was filled inside a [client portal](/developer-docs/core-concepts/client-portals): `spaceId`, `blockId` (the form block), `viewerId`, `viewerEmail` |

## signature\_completed

Triggered when an e-signature request is fully signed (see [E-Signatures](/developer-docs/core-concepts/signatures)).

### Payload

```json theme={null}
{
  "webhookType": "signature_completed",
  "id": "signature-uuid-123",
  "name": "Order confirmation",
  "status": "completed",
  "completedAt": "2026-01-15T10:00:00.000Z",
  "signer": {
    "email": "john.doe@example.com",
    "name": "John Doe",
    "role": "First Party"
  },
  "documentUrl": "https://files.meetergo.com/.../signed.pdf"
}
```

### Key Fields

| Field | Type | Description |
| - | - | - |
| `id` | string | Signature request identifier |
| `name` | string | Document name |
| `status` | string | Always `completed` for this event |
| `signer` | object | The signer (`email`, `name`, `role`) |
| `documentUrl` | string | Time-limited URL to download the signed PDF |

## whatsapp\_message\_received

Triggered when an inbound WhatsApp message arrives on a connected number (see [WhatsApp](/developer-docs/core-concepts/whatsapp)). Reply by calling `POST /v4/whatsapp/messages` with the `from` number.

### Payload

```json theme={null}
{
  "webhookType": "whatsapp_message_received",
  "conversationId": "conv-uuid-123",
  "from": "+491234567890",
  "contactName": "Jane Doe",
  "contactId": null,
  "type": "text",
  "text": "Do you have this in size M?",
  "isFirstMessage": false,
  "windowReopened": true,
  "labels": ["Sales"],
  "receivedAt": "2026-06-25T10:00:00.000Z"
}
```

### Key Fields

| Field | Type | Description |
| - | - | - |
| `conversationId` | string | Conversation id — use it to reply or fetch history |
| `from` | string | Sender's phone number (E.164) |
| `contactName` | string \| null | WhatsApp profile name, when available |
| `contactId` | string \| null | Linked meetergo CRM contact id, or `null` |
| `type` | string | Message kind (`text`, `image`, `audio`, `document`, ...) |
| `text` | string | Body text; empty string for non-text messages |
| `isFirstMessage` | boolean | `true` if this is the first message in the conversation |
| `windowReopened` | boolean | `true` if an expired 24h window just reopened |
| `labels` | string\[] | Conversation labels at the time of receipt |
| `receivedAt` | string | When meetergo received the message (ISO 8601) |

<Tip>
  Because the contact just messaged you, the 24-hour customer-service window is open — a free-form `text` reply is allowed. See the [WhatsApp 24h window](/developer-docs/core-concepts/whatsapp#the-24-hour-window-read-this-first).
</Tip>

## whatsapp\_message\_status

Triggered when an outbound message's delivery status changes (`sent` → `delivered` → `read`, or `failed`). Correlate with the `waMessageId` returned from `POST /v4/whatsapp/messages`.

### Payload

```json theme={null}
{
  "webhookType": "whatsapp_message_status",
  "conversationId": "conv-uuid-123",
  "waMessageId": "wamid.HBgN...",
  "recipient": "+491234567890",
  "status": "delivered",
  "occurredAt": "2026-06-25T10:00:05.000Z"
}
```

### Key Fields

| Field | Type | Description |
| - | - | - |
| `waMessageId` | string | The message id returned by the send API — use it to correlate |
| `recipient` | string | Recipient phone number (E.164) |
| `status` | string | `sent`, `delivered`, `read`, or `failed` |
| `errorCode` | number | Meta error code (present only when `status` is `failed`) |
| `errorMessage` | string | Meta error description (present only when `status` is `failed`) |
| `occurredAt` | string | When the status change occurred (ISO 8601) |

<Note>
  Only real status upgrades fire (e.g. you won't get a duplicate `delivered`). A `failed` status includes `errorCode`/`errorMessage` — a common one is the recipient being outside the 24-hour window for a non-template message.
</Note>

## Client portal events

Five events follow what a visitor does inside a [client portal](/developer-docs/core-concepts/client-portals): `space_viewed`, `space_invite_accepted`, `space_task_completed`, `space_task_reopened`, `space_file_uploaded`. They share one head, so a receiver can attribute every event without a follow-up request.

### Common fields

| Field | Type | Description |
| - | - | - |
| `occurredAt` | string | Server time of the event (ISO 8601) |
| `spaceId` | string | The portal |
| `space` | object | `id`, `name`, `customerCompanyId`, `customerCompanyName` (the client the portal is for, if linked) |
| `viewer` | object | `id`, `email`, `name`, `verified` (email proven via invite link or code) |

### space\_task\_completed / space\_task\_reopened

Triggered when a verified viewer checks off an action-plan step, or unchecks one they had completed.

```json theme={null}
{
  "webhookType": "space_task_completed",
  "occurredAt": "2026-09-02T14:05:12.000Z",
  "spaceId": "9c4a5e3e-8f2b-4b1e-9a6d-1f0e2d3c4b5a",
  "space": {
    "id": "9c4a5e3e-8f2b-4b1e-9a6d-1f0e2d3c4b5a",
    "name": "Partner onboarding · Example GmbH",
    "customerCompanyId": "2b7c1d9e-6a5f-4e3d-8c2b-1a0f9e8d7c6b",
    "customerCompanyName": "Example GmbH"
  },
  "viewer": {
    "id": "5d6e7f80-1a2b-4c3d-9e8f-7a6b5c4d3e2f",
    "email": "anna@example.com",
    "name": "Anna Example",
    "verified": true
  },
  "blockId": "0f1e2d3c-4b5a-4968-8776-655443322110",
  "blockTitle": "Your next steps",
  "taskId": "step-2",
  "task": {
    "id": "step-2",
    "description": "Upload your CV",
    "done": true,
    "dueDate": "2026-09-10",
    "phase": "Phase 1",
    "assignee": { "name": "Anna Example", "side": "them" },
    "completedAt": "2026-09-02T14:05:12.000Z",
    "updatedAt": "2026-09-02T14:05:12.000Z"
  },
  "progress": { "total": 4, "done": 2, "open": 2 }
}
```

| Field | Type | Description |
| - | - | - |
| `blockId` | string | The action-plan block |
| `taskId` | string | The step; stable across the life of the portal |
| `task.done` | boolean | `true` on `space_task_completed`, `false` on `space_task_reopened` |
| `task.completedAt` | string \| null | When the step was checked off; `null` once reopened |
| `task.assignee.side` | string | `them` (the client) or `us` (your team) |
| `progress` | object | Steps in this block after the change: `total`, `done`, `open` |

### space\_file\_uploaded

Triggered when a viewer uploads a file to the portal's upload zone. Files attached to a form answer arrive with `form_submission` instead.

```json theme={null}
{
  "webhookType": "space_file_uploaded",
  "occurredAt": "2026-09-02T14:07:40.000Z",
  "spaceId": "9c4a5e3e-…",
  "space": { "…": "…" },
  "viewer": { "…": "…" },
  "uploadId": "7a8b9c0d-…",
  "fileName": "credit-report.pdf",
  "mimeType": "application/pdf",
  "sizeBytes": 184233,
  "downloadPath": "/space/9c4a5e3e-…/uploads/7a8b9c0d-…/download"
}
```

`GET {downloadPath}` with your token returns a short-lived download URL.

### space\_viewed

Triggered when a visitor opens the portal, once per session. Owner previews do not fire it. Adds `linkId` and `viewId`.

### space\_invite\_accepted

Triggered the first time an invited person opens the portal with a verified email (invite link or one-time code). Fires once per invite. Adds `inviteId`, `linkId` and `viewId`.

## Common Attendee Fields

All booking events include attendee information:

| Field | Type | Description |
| - | - | - |
| `id` | string | Attendee record ID |
| `email` | string | Email address |
| `firstname` | string | First name |
| `lastname` | string | Last name |
| `fullname` | string | Combined full name |
| `phone` | string | Phone number (if provided) |
| `timezone` | string | Attendee's timezone |
| `language` | string | Preferred language code |
| `notes` | string | Notes from booking form |
| `attendeeEnrichment` | object | Enriched data from integrations |

## Common Host Fields

All booking events include host information:

| Field | Type | Description |
| - | - | - |
| `id` | string | Host user ID |
| `email` | string | Email address |
| `givenName` | string | First name |
| `familyName` | string | Last name |
| `fullName` | string | Combined full name |
| `slug` | string | URL-friendly identifier |
| `picture` | string | Profile photo URL |

## Handling Events

### Example: Sync to CRM

```javascript theme={null}
async function handleBookingCreated(payload) {
  const attendee = payload.attendees[0];

  // Create or update contact in your CRM
  await crm.contacts.upsert({
    email: attendee.email,
    firstName: attendee.firstname,
    lastName: attendee.lastname,
    phone: attendee.phone,
    lastMeetingDate: payload.start,
    meetingType: payload.meetingType.meetingInfo.name
  });

  // Log the activity
  await crm.activities.create({
    type: 'meeting_scheduled',
    contactEmail: attendee.email,
    subject: `${payload.meetingType.meetingInfo.name} scheduled`,
    scheduledAt: payload.start,
    hostEmail: payload.hosts[0].email
  });
}
```

### Example: Send Slack Notification

```javascript theme={null}
async function handleBookingCreated(payload) {
  const attendee = payload.attendees[0];
  const host = payload.hosts[0];

  await slack.chat.postMessage({
    channel: '#bookings',
    text: `New booking!`,
    blocks: [
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: `*New ${payload.meetingType.meetingInfo.name}*\n` +
                `Attendee: ${attendee.fullname} (${attendee.email})\n` +
                `Host: ${host.fullName}\n` +
                `Time: ${new Date(payload.start).toLocaleString()}`
        }
      }
    ]
  });
}
```

### Example: Correlate with External Ticket

Use `metadata` to link bookings back to external systems (e.g., CRM tickets, support cases):

```javascript theme={null}
async function handleBookingCreated(payload) {
  // Access custom metadata set when creating the meeting type
  const ticketId = payload.meetingType.metadata?.ticketId;

  if (ticketId) {
    // Update your ticket system with the booking details
    await ticketSystem.updateTicket(ticketId, {
      status: 'appointment_scheduled',
      appointmentTime: payload.start,
      appointmentId: payload.id,
      attendeeEmail: payload.attendees[0].email,
      hostName: payload.hosts[0].fullName
    });

    // Optionally clean up the meeting type after booking
    await fetch(`https://api.meetergo.com/v4/meeting-type/${payload.meetingType.id}`, {
      method: 'DELETE',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'x-meetergo-api-user-id': payload.hosts[0].id
      }
    });
  }
}
```

<Tip>
  The `metadata` field is set when [creating the meeting type](/api-reference/meeting-types/create-meeting-type) and is included in all booking webhooks. Use it to correlate bookings with your external systems.
</Tip>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.