reservation.created
A new reservation arrived from any connected channel or direct booking.
When it fires
Fires once when a new reservation lands in Repull from a channel sync (Airbnb, Booking.com, Plumguide), an OAuth-linked PMS push, a direct-booking website, or a manual API call. It will not fire again on subsequent edits — those raise reservation.updated.
Payload
Every delivery uses the same outer envelope (event, eventId, apiVersion, timestamp, data). Dedupe on eventId — it stays stable across retries and replays, while the X-Repull-Delivery-Id header changes on every attempt.
{
"event": "reservation.created",
"eventId": "3f1c9a2e-8b7d-4c6a-9e0f-1a2b3c4d5e6f",
"apiVersion": "2026-04",
"timestamp": "2026-05-01T12:34:56.000Z",
"data": {
"object": {
"id": "900001",
"uid": "HMEXAMPLE1",
"channel": "airbnb",
"listingId": "5668",
"customerId": "1",
"checkinDate": "2026-06-10",
"checkoutDate": "2026-06-16",
"status": "confirmed",
"cancellationPolicy": "firm_14",
"checkInTime": "16:00",
"checkOutTime": "10:00",
"guestId": "900002",
"checkIn": "2026-06-10",
"checkOut": "2026-06-16",
"source": "airbnb",
"platform": "airbnb",
"confirmationCode": "HMEXAMPLE1",
"totalPrice": "1320.00",
"currency": "GBP",
"guestDetails": {
"numberOfGuests": 2,
"numberOfAdults": 2,
"numberOfChildren": 0,
"numberOfInfants": 0,
"numberOfPets": 0
},
"primaryGuest": {
"id": "900002",
"firstName": "Taylor",
"lastName": "Reed",
"phone": "15555550142",
"language": "en-US"
},
"occupancy": {
"adults": 2,
"children": 0,
"infants": 0,
"pets": 0,
"total": 2
},
"financials": {
"totalPrice": 1320,
"currency": "GBP",
"paymentStatus": "pending",
"cancellationPolicy": "firm_14",
"host": {
"accommodation": 1160,
"discounts": [],
"guestFees": [
{
"name": "Guest service fee",
"type": "guest_service",
"amount": 0,
"vat": 0
}
],
"hostFees": [
{
"name": "Host service fee",
"type": "host_service",
"amount": 39.6,
"vat": 0
}
],
"taxes": [],
"revenue": 1280.4
},
"guest": {
"totalPrice": 1320,
"fees": [
{
"name": "Cleaning Fee",
"type": "cleaning",
"amount": 160
}
],
"taxes": []
}
},
"createdAt": "2026-05-01T12:34:56.000Z",
"updatedAt": "2026-05-01T12:34:56.000Z",
"bookedAt": "2026-05-01T12:34:50.000Z",
"guestName": "Taylor Reed"
},
"revision": "2026-05-01T12:34:56.000Z"
}
}Verifying signatures
Every delivery includes a timestamped X-Repull-Signature header of the form t=<unix_ts>,v1=<hex>, where v1 is HMAC-SHA256(signing_secret, `${t}.${raw_body}`). Verify it before processing — see Verify Signatures for full Node.js and Python examples.
Use the raw body
Example handler
// Express handler — verify signature, then route on type
app.post('/webhooks/repull', express.raw({ type: 'application/json' }), (req, res) => {
if (!verifyRepullSignature(req)) return res.sendStatus(401)
const { type, deliveryId, data } = JSON.parse(req.body.toString())
if (type === 'reservation.created') {
// Idempotent on deliveryId — retries reuse the same id
if (alreadyProcessed(deliveryId)) return res.sendStatus(200)
onNewReservation(data) // sync to your CRM, notify the cleaner, etc.
}
res.sendStatus(200)
})Common patterns
- Treat the payload as a hint, not the source of truth. Always re-fetch the full reservation via GET /v1/reservations/{id} before charging cards or sending guest comms — the canonical record may have additional fields the payload trims.
- Dedupe on deliveryId. Retries reuse the same id, so a simple seen-set or unique index on deliveryId is enough to make your handler idempotent.
- Subscribe to reservation.created together with reservation.updated and reservation.cancelled. The three events form the full lifecycle — alone, reservation.created leaves you blind to date shifts and cancellations.
Related
- Full event catalog
- reservation.updated
- reservation.cancelled
- Verify webhook signatures
- Fetch a reservation by id
Tip: Acknowledge with a 2xx status within 10 seconds. Failed deliveries are retried up to 5 times with exponential backoff.Webhook reliability →