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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hostelmate.co/api-documentation/webhooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
