> For the complete documentation index, see [llms.txt](https://docs.hostelmate.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hostelmate.co/api-documentation/webhooks.md).

# Webhooks

Webhooks let you subscribe to events and receive an HTTP `POST` to your server the moment something happens in Hostel Mate — no polling required.

## Managing endpoints

Webhook endpoints are managed from the **API Access → Webhooks** tab in your Hostel Mate dashboard. From there you can:

* Register a new endpoint (URL + event subscriptions)
* Enable or disable an endpoint without deleting it
* Delete an endpoint and its delivery history
* Inspect the last 50 delivery attempts per endpoint

You can also manage endpoints programmatically via the Client API — see [Webhook API reference](#api-reference) below.

***

## Supported events

| Event               | Fired when                                            |
| ------------------- | ----------------------------------------------------- |
| `booking.created`   | A new booking is created via the Client API           |
| `booking.updated`   | A booking's dates, room, status, or notes are changed |
| `booking.cancelled` | A booking is deleted (treated as a cancellation)      |
| `payment.created`   | A new payment is recorded                             |
| `payment.updated`   | A payment's amount, method, or description changes    |
| `message.received`  | An inbound OTA message (Booking.com, Airbnb) arrives  |

***

## Payload envelope

Every webhook delivery sends a JSON body in this shape:

```json
{
  "id": "f3c2a1b0-...",
  "type": "booking.created",
  "property_id": "73d9244a-44df-4254-b304-999c122b8dc1",
  "created_at": "2025-04-22T14:30:00Z",
  "data": { ... }
}
```

| Field         | Description                                                   |
| ------------- | ------------------------------------------------------------- |
| `id`          | Unique delivery ID (UUID). Use this to deduplicate retries.   |
| `type`        | The event name — same strings as the subscription list above. |
| `property_id` | Your property UUID.                                           |
| `created_at`  | ISO 8601 UTC timestamp of when the event was fired.           |
| `data`        | Event-specific payload (see per-event shapes below).          |

### `booking.created` / `booking.updated` data

```json
{
  "bookingId": "abc-123",
  "status": "new",
  "room": "uuid-of-room",
  "total": 18000,
  "dates": [
    { "date": "2025-06-01", "amount": 9000 },
    { "date": "2025-06-02", "amount": 9000 }
  ]
}
```

Amounts are in **minor units** (integer cents). `booking.updated` from a status-only change includes only `bookingId` and `status`.

### `booking.cancelled` data

```json
{ "bookingId": "abc-123" }
```

### `payment.created` data

```json
{
  "paymentId": "uuid",
  "amount": "150.00",
  "paymentMethod": "Cash",
  "type": "income"
}
```

Payment amounts are **decimal strings** (not minor units).

### `payment.updated` data

```json
{
  "paymentId": "uuid",
  "amount": "175.00"
}
```

### `message.received` data

```json
{
  "bookingId": "abc-123",
  "messageId": "channex-message-uuid",
  "threadId": "channex-thread-uuid",
  "content": "Hi, can I check in early?"
}
```

***

## Request headers

Each delivery includes these headers on the `POST` request to your endpoint:

| Header           | Value                                    |
| ---------------- | ---------------------------------------- |
| `Content-Type`   | `application/json`                       |
| `X-HM-Signature` | `sha256=<hex digest>`                    |
| `X-HM-Event`     | Event name, e.g. `booking.created`       |
| `X-HM-Delivery`  | Delivery UUID (matches `id` in the body) |

***

## Verifying the signature

Every delivery is signed with HMAC-SHA256 using your endpoint secret. **Always verify the signature** before processing.

The signature covers the raw request body bytes.

```javascript
// Node.js example
const crypto = require('crypto');

function verifySignature(secret, rawBody, signatureHeader) {
    const expected = 'sha256=' + crypto
        .createHmac('sha256', secret)
        .update(rawBody)          // Buffer or string of the raw request body
        .digest('hex');
    return crypto.timingSafeEqual(
        Buffer.from(expected),
        Buffer.from(signatureHeader)
    );
}
```

```python
# Python example
import hmac, hashlib

def verify_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)
```

Your endpoint must return a **2xx status code** within **5 seconds** for the delivery to be considered successful.

***

## Retries and backoff

Failed deliveries (non-2xx response or timeout) are retried automatically with exponential backoff:

| Attempt | Delay after previous failure           |
| ------- | -------------------------------------- |
| 2       | 1 minute                               |
| 3       | 5 minutes                              |
| 4       | 30 minutes                             |
| 5       | 2 hours                                |
| —       | Marked **dead** after 5 total attempts |

Dead deliveries are visible in the dashboard delivery log. The `id` field in the payload is stable across retries — use it to deduplicate.

***

## API reference

All webhook endpoints require `X-API-Key` authentication. `POST` and `PATCH` require an `Idempotency-Key` header.

### List endpoints

```
GET /client/webhooks
```

Returns all webhook endpoints for your property.

```json
[
  {
    "id": "uuid",
    "url": "https://example.com/webhook",
    "events": ["booking.created", "payment.created"],
    "is_active": true,
    "created_at": "2025-04-22T10:00:00Z",
    "updated_at": "2025-04-22T10:00:00Z"
  }
]
```

### Create endpoint

```
POST /client/webhooks
```

```json
{
  "url": "https://example.com/webhook",
  "events": ["booking.created", "booking.cancelled", "payment.created"]
}
```

The response includes the endpoint `secret` — this is the **only time** it is returned. Store it immediately.

```json
{
  "id": "uuid",
  "url": "https://example.com/webhook",
  "secret": "abc123...",
  "events": ["booking.created", "booking.cancelled", "payment.created"],
  "is_active": true,
  "created_at": "2025-04-22T10:00:00Z"
}
```

### Update endpoint

```
PATCH /client/webhooks/{endpoint_id}
```

All fields are optional. Pass only what you want to change:

```json
{
  "is_active": false
}
```

Returns the updated endpoint (without `secret`).

### Delete endpoint

```
DELETE /client/webhooks/{endpoint_id}
```

Returns `204 No Content`. Deletes the endpoint and all delivery records.

### List deliveries

```
GET /client/webhooks/{endpoint_id}/deliveries
```

Returns the last 50 deliveries for the endpoint, newest first.

```json
[
  {
    "id": "uuid",
    "event_type": "booking.created",
    "status": "delivered",
    "attempts": 1,
    "last_attempted_at": "2025-04-22T14:30:05Z",
    "delivered_at": "2025-04-22T14:30:05Z",
    "response_status": 200,
    "response_body": "ok",
    "created_at": "2025-04-22T14:30:00Z"
  }
]
```

Delivery `status` values: `pending`, `delivered`, `failed`, `dead`.
