# Webhooks

Every change to a booking, and to an affiliate, in order, signed, posted to an endpoint you give the rental. Endpoints are made under Settings, API keys. An endpoint hears about one channel or all of them, and about the events it names or all of them.

## The request

```
POST https://your.example/hooks/mellow
content-type: application/json
x-mellow-event: booking.created
x-mellow-signature: 9f2c…
```

```json
{
  "event": "booking.created",
  "at": "2026-09-14T14:26:50.249Z",
  "sentAt": "2026-09-14T14:26:51.020Z",
  "booking": {
    "id": "…",
    "reference": 109,
    "code": "V83BEH",
    "status": "reserved",
    "channelId": "…",
    "customFields": { "voucher": "A-1042" }
  },
  "data": { "…": "what the event carries" }
}
```

`booking` is the same for every event: who it is about, and where it stands now. `customFields` holds the rental's custom fields on the booking that it sends through the API, as they are when the post goes out. See [Custom fields](/bookings#custom-fields). `data` follows the event: a cancel carries the reason, a car assignment carries the plate, a price change carries the delta.

`at` is when the event happened. `sentAt` is when this post was sent, new on each try. The signature covers both, so a receiver can refuse a post that somebody copied and sends again later. See [Checking the signature](/signatures).

## The events

`x-mellow-event` names the event. Booking events:

| Event                                                                              | When                                                                                                                                                                     |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `booking.created`                                                                  | A booking was made, on any channel the endpoint hears.                                                                                                                   |
| `booking.imported`                                                                 | A booking came in from the rental's previous system.                                                                                                                     |
| `booking.reserved`                                                                 | The status moved to `reserved`. No screen and no call makes this move today, so the event does not come yet. A cancelled booking taken back comes as `booking.reopened`. |
| `booking.modified`                                                                 | Dates, locations or extras changed and the price was made again, or the status moved, such as to `closed` or `no_show`. `data.status` names the new status then.         |
| `booking.extended` and `booking.shortened`                                         | The return moved out or in.                                                                                                                                              |
| `booking.class_changed`                                                            | The car type changed.                                                                                                                                                    |
| `booking.cancelled`                                                                | Cancelled, with the reason in `data`.                                                                                                                                    |
| `booking.reopened`                                                                 | A cancelled booking was taken back.                                                                                                                                      |
| `booking.unit_assigned` and `booking.unit_swapped`                                 | A car was put on it, or two bookings traded cars.                                                                                                                        |
| `booking.checked_in` and `booking.check_in_undone`                                 | The booking became checked in: every required fact is there. Or it stopped being so, as a fact went, such as an unpaid extra.                                            |
| `booking.agreement_made` and `booking.agreement_signed`                            | The rental agreement, made and signed.                                                                                                                                   |
| `booking.started` and `booking.returned`                                           | The car went out, and came back.                                                                                                                                         |
| `booking.payment`, `booking.refund` and `booking.chargeback`                       | Money came in, went back, or was taken back by the card issuer.                                                                                                          |
| `booking.payment_authorized` and `booking.payment_failed`                          | The processor authorised a payment, or refused it.                                                                                                                       |
| `booking.deposit_held`, `booking.deposit_released` and `booking.deposit_expired`   | A deposit was held on the card, let go, or ran out.                                                                                                                      |
| `booking.card_saved`                                                               | The customer's card was saved for later charges. `data` carries the last four digits.                                                                                    |
| `booking.line_voided`                                                              | A line was voided.                                                                                                                                                       |
| `booking.note` and `booking.email`                                                 | A note was written, an email sent.                                                                                                                                       |
| `booking.damage_reported`                                                          | Damage recorded on the car while this booking had it.                                                                                                                    |
| `booking.key_code_made`, `booking.key_code_moved` and `booking.key_code_cancelled` | A key box code for the car's key was made, moved to new times, or cancelled. `data` never carries the code.                                                              |
| `booking.key_picked_up`, `booking.key_returned` and `booking.key_collected`        | The key box says the key was taken, or put back, or staff took the returned key out of the box.                                                                          |

Referral events, heard only by an endpoint for every channel:

| Event                                                    | When                                         |
| -------------------------------------------------------- | -------------------------------------------- |
| `booking.referred`                                       | An affiliate was credited with the booking.  |
| `booking.affiliate_changed`                              | A person changed or removed the affiliate.   |
| `booking.commission_due`                                 | The car came back, and the hold started.     |
| `booking.commission_approved`, `booking.commission_paid` | The commission was approved, and paid out.   |
| `booking.commission_void`, `booking.commission_reversed` | Voided before payment, or reversed after it. |
| `booking.commission_pending`                             | The commission went back to pending.         |

Affiliate events, also only for an endpoint for every channel. Their body names the `affiliate` in place of the `booking`:

| Event                   | When                                                                       |
| ----------------------- | -------------------------------------------------------------------------- |
| `affiliate.joined`      | A signup, or an affiliate added. `data.status` says if it waits for a yes. |
| `affiliate.approved`    | Active, the first time or again after a pause.                             |
| `affiliate.paused`      | Paused.                                                                    |
| `affiliate.rejected`    | The signup was turned down.                                                |
| `affiliate.payout_paid` | A payout went out. `data` has the amount, the currency and the date.       |

```json
{
  "event": "affiliate.joined",
  "at": "2026-09-30T12:00:00.000Z",
  "sentAt": "2026-09-30T12:00:00.840Z",
  "affiliate": { "id": "…", "name": "Hótel Borg", "code": "borg", "status": "pending" },
  "data": { "status": "pending" }
}
```

Name the events you want when you make the endpoint, or leave the list empty for all of them. A new event can appear in `v1`, so an endpoint that hears all of them must ignore an event it does not know.

## Order and retries

Delivery is in order per endpoint. Answer with any 2xx within a few seconds: a post with no answer in 10 seconds counts as a timeout. A failure, a timeout or a non-2xx answer stops that endpoint at that event, and it is tried again on the next delivery round: right after the next booking change, and at least hourly. A round that runs long leaves the rest of the events for the next round. Nothing is skipped and nothing is sent twice.

An endpoint that keeps failing is switched off after a run of failures. The Webhooks card on Settings, API keys shows it, with the last deliveries and their answers, and a person switches it back on there.

Deliveries begin with the events after the endpoint was made. Nothing older is replayed.
