reservation.request.created

A guest asked to book a listing that does not use Instant Book, and the request is waiting on you.

When it fires

Fires once per request, when it reaches Repull, whether the guest asked in the Airbnb app or anywhere else. data.object is the pending reservation in the shape GET /v1/reservations/{id} returns, and respondBy is when Airbnb expires it: 24 hours after the guest asked.

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.request.created",
  "eventId": "3f1c9a2e-8b7d-4c6a-9e0f-1a2b3c4d5e6f",
  "apiVersion": "2026-04",
  "timestamp": "2026-05-01T12:34:56.000Z",
  "data": {
    "object": {
      "id": "236354",
      "uid": "HMT3X9KQZ2",
      "channel": "airbnb",
      "listingId": "23892",
      "customerId": "1",
      "checkinDate": "2026-10-02",
      "checkoutDate": "2026-10-06",
      "status": "pending",
      "cancellationPolicy": "firm_14",
      "checkInTime": "16:00",
      "checkOutTime": "10:00",
      "guestId": "231877",
      "checkIn": "2026-10-02",
      "checkOut": "2026-10-06",
      "source": "airbnb",
      "platform": "airbnb",
      "confirmationCode": "HMT3X9KQZ2",
      "totalPrice": "812.00",
      "currency": "USD",
      "guestDetails": {
        "numberOfGuests": 2,
        "numberOfAdults": 2,
        "numberOfChildren": 0,
        "numberOfInfants": 0,
        "numberOfPets": 0
      },
      "primaryGuest": {
        "id": "231877",
        "firstName": "Jordan",
        "lastName": "Ellis",
        "language": "en-US"
      },
      "occupancy": {
        "adults": 2,
        "children": 0,
        "infants": 0,
        "pets": 0,
        "total": 2
      },
      "financials": {
        "totalPrice": 812,
        "currency": "USD",
        "cancellationPolicy": "firm_14",
        "host": {
          "accommodation": 700,
          "discounts": [],
          "guestFees": [],
          "hostFees": [
            {
              "name": "Host service fee",
              "type": "host_service",
              "amount": 24.36,
              "vat": 0
            }
          ],
          "taxes": [],
          "revenue": 787.64
        },
        "guest": {
          "totalPrice": 812,
          "fees": [
            {
              "name": "Cleaning Fee",
              "type": "cleaning",
              "amount": 112
            }
          ],
          "taxes": []
        }
      },
      "createdAt": "2026-09-22T09:00:00.000Z",
      "updatedAt": "2026-09-22T09:00:05.000Z",
      "bookedAt": "2026-09-22T09:00:00.000Z",
      "guestName": "Jordan Ellis",
      "respondBy": "2026-09-23T09:00:00.000Z"
    },
    "requestStatus": "pending",
    "respondBy": "2026-09-23T09:00:00.000Z",
    "occurredAt": "2026-09-22T09:00:05.000Z",
    "revision": "2026-09-22T09:00:05.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

Sign the raw request body exactly as received, not a re-stringified JSON object. Re-serialisation can reorder keys or change whitespace and break the signature.

Example handler

app.post('/webhooks/repull', express.raw({ type: 'application/json' }), async (req, res) => {
  if (!verifyRepullSignature(req)) return res.sendStatus(401)
  const { event, data } = JSON.parse(req.body.toString())
  res.sendStatus(200) // acknowledge first, work after

  if (event === 'reservation.request.created') {
    const r = data.object
    await notifyHost(`Booking request ${r.confirmationCode}: ${r.checkIn} to ${r.checkOut}, answer by ${data.respondBy}`)
    // Or decide automatically:
    // POST /v1/reservations/${r.id}/accept, or /decline with a reason and message
  }
})

Common patterns

  • Accept or decline with POST /v1/reservations/{id}/accept or /decline, using the reservation id from data.object, before respondBy.
  • The outcome arrives as reservation.request.updated. Accepting also produces reservation.created for the booking.
  • A request that lapses without Airbnb saying so fires no event. GET /v1/reservations still reports it as cancelled with statusDetail: "request_expired", so if you track deadlines, schedule your own check at respondBy.

Tip: Acknowledge with a 2xx status within 10 seconds. Failed deliveries are retried up to 5 times with exponential backoff.Webhook reliability →

AI