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

# Managing Webhooks

> Create, update, and delete webhook endpoints via the API

Manage your webhook endpoints programmatically using the Webhooks API.

## Create a Webhook

Register a new webhook endpoint:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.meetergo.com/webhooks" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "x-meetergo-api-user-id: {userId}" \
    -H "Content-Type: application/json" \
    -d '{
      "endpoint": "https://your-server.com/webhooks/meetergo",
      "description": "Production webhook",
      "eventTypes": ["booking_created", "booking_cancelled", "booking_rescheduled"]
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.meetergo.com/webhooks', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'x-meetergo-api-user-id': userId,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      endpoint: 'https://your-server.com/webhooks/meetergo',
      description: 'Production webhook',
      eventTypes: ['booking_created', 'booking_cancelled', 'booking_rescheduled']
    })
  });
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.meetergo.com/webhooks',
      headers={
          'Authorization': 'Bearer YOUR_API_KEY',
          'x-meetergo-api-user-id': user_id,
          'Content-Type': 'application/json'
      },
      json={
          'endpoint': 'https://your-server.com/webhooks/meetergo',
          'description': 'Production webhook',
          'eventTypes': ['booking_created', 'booking_cancelled', 'booking_rescheduled']
      }
  )
  ```
</CodeGroup>

### Request Body

| Field         | Type      | Required | Description                          |
| ------------- | --------- | -------- | ------------------------------------ |
| `endpoint`    | string    | Yes      | URL to receive webhook POST requests |
| `description` | string    | No       | Human-readable description           |
| `eventTypes`  | string\[] | Yes      | Events to subscribe to               |

### Event Types

| Value                 | Description                                                    |
| --------------------- | -------------------------------------------------------------- |
| `booking_created`     | New appointment booked                                         |
| `booking_cancelled`   | Appointment cancelled (or attendee removed from group booking) |
| `booking_rescheduled` | Appointment time changed                                       |
| `new_employee`        | New user added to company                                      |
| `form_submission`     | Routing form or funnel submitted                               |

### Response

```json theme={null}
{
  "id": "webhook-uuid-123",
  "endpoint": "https://your-server.com/webhooks/meetergo",
  "description": "Production webhook",
  "eventTypes": ["booking_created", "booking_cancelled", "booking_rescheduled"],
  "companyId": "company-uuid-789",
  "createdAt": "2024-01-15T10:00:00Z"
}
```

## List Webhooks

Get all webhooks for your company:

```bash theme={null}
curl -X GET "https://api.meetergo.com/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-meetergo-api-user-id: {userId}"
```

### Response

```json theme={null}
[
  {
    "id": "webhook-uuid-123",
    "endpoint": "https://your-server.com/webhooks/meetergo",
    "description": "Production webhook",
    "eventTypes": ["booking_created", "booking_cancelled"],
    "companyId": "company-uuid-789",
    "createdAt": "2024-01-15T10:00:00Z"
  },
  {
    "id": "webhook-uuid-456",
    "endpoint": "https://staging.your-server.com/webhooks/meetergo",
    "description": "Staging webhook",
    "eventTypes": ["booking_created"],
    "companyId": "company-uuid-789",
    "createdAt": "2024-01-14T09:00:00Z"
  }
]
```

## Update a Webhook

Modify an existing webhook:

```bash theme={null}
curl -X PATCH "https://api.meetergo.com/webhooks/{webhookId}" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-meetergo-api-user-id: {userId}" \
  -H "Content-Type: application/json" \
  -d '{
    "eventTypes": ["booking_created", "booking_cancelled", "booking_rescheduled", "new_employee"]
  }'
```

### Request Body

All fields are optional:

| Field         | Type      | Description             |
| ------------- | --------- | ----------------------- |
| `endpoint`    | string    | New endpoint URL        |
| `description` | string    | New description         |
| `eventTypes`  | string\[] | New event subscriptions |

## Delete a Webhook

Remove a webhook endpoint:

```bash theme={null}
curl -X DELETE "https://api.meetergo.com/webhooks/{webhookId}" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-meetergo-api-user-id: {userId}"
```

Returns `204 No Content` on success.

## Limits

| Limit                           | Value |
| ------------------------------- | ----- |
| Maximum webhooks per company    | 6     |
| Minimum event types per webhook | 1     |

### Error: Maximum Webhooks Reached

```json theme={null}
{
  "statusCode": 406,
  "message": "Company reached maximum",
  "error": "Not Acceptable"
}
```

**Solution:** Delete unused webhooks before creating new ones.

## Testing Webhooks

### Local Development

Use a tunneling service for local development:

**ngrok**

```bash theme={null}
ngrok http 3000
# Returns: https://abc123.ngrok.io
```

**localtunnel**

```bash theme={null}
npx localtunnel --port 3000
# Returns: https://your-subdomain.loca.lt
```

Register the tunnel URL as your webhook endpoint during development.

### Test with curl

Simulate a webhook delivery to your endpoint:

```bash theme={null}
curl -X POST "https://your-server.com/webhooks/meetergo" \
  -H "Content-Type: application/json" \
  -d '{
    "webhookType": "booking_created",
    "id": "test-appt-123",
    "start": "2024-01-15T09:00:00+01:00",
    "end": "2024-01-15T09:30:00+01:00",
    "attendees": [{
      "email": "test@example.com",
      "firstname": "Test",
      "lastname": "User"
    }],
    "hosts": [{
      "email": "host@example.com",
      "givenName": "Host",
      "familyName": "User"
    }],
    "meetingType": {
      "id": "mt-123",
      "meetingInfo": { "name": "Test Meeting" }
    }
  }'
```

## Webhook Reliability

### Delivery Guarantees

* Webhooks are delivered **at least once**
* Failed deliveries are **not automatically retried**
* Implement idempotent handlers using the appointment `id`

### Handling Failures

Your endpoint should:

1. **Return 200 quickly** - Process asynchronously if needed
2. **Be idempotent** - Same webhook delivered twice should have same result
3. **Log all requests** - For debugging failed deliveries

### Example: Idempotent Handler

```javascript theme={null}
const processedEvents = new Set();

app.post('/webhooks/meetergo', async (req, res) => {
  const payload = req.body;
  const eventKey = `${payload.webhookType}:${payload.id || payload.formId}:${payload.updatedAt || payload.submittedAt}`;

  // Return 200 immediately
  res.status(200).send('OK');

  // Skip if already processed
  if (processedEvents.has(eventKey)) {
    console.log('Duplicate event, skipping:', eventKey);
    return;
  }

  processedEvents.add(eventKey);

  // Process asynchronously
  try {
    await processEvent(payload.webhookType, payload);
  } catch (error) {
    console.error('Failed to process webhook:', error);
    // Add to retry queue
    await retryQueue.add(payload);
  }
});
```

## Best Practices

<Check>
  **Use HTTPS** - All production webhook endpoints should use HTTPS
</Check>

<Check>
  **Return 200 immediately** - Don't block the response while processing
</Check>

<Check>
  **Implement idempotency** - Handle duplicate deliveries gracefully
</Check>

<Check>
  **Log requests** - Keep logs for debugging
</Check>

<Check>
  **Monitor failures** - Alert when webhook processing fails
</Check>

<Warning>
  **Don't expose secrets** - Never include API keys or secrets in webhook endpoint URLs
</Warning>
