Hotels

POST/v1/hotels/bookings

Book a rate — charged to your account

We reserve the amount on your account, book with the supplier, then charge what was actually due. If the supplier declines, the reservation is released and you are not charged.

Auth
API key
Scope
hotels:book
Rate limit
BOOKING
Search quota
Free
Idempotency
Idempotency-Key

Request headers

HeaderTypeRequiredDescriptionExample
Idempotency-KeystringRequiredMandatory on every POST. The same request with the same key returns the stored response without reaching the supplier again. The same key with a DIFFERENT body is rejected with 409 idempotency_key_reuse. Retained for 24 hours.4f1c358f-6b2e-5acb-8ef5-0c3d0bcc35f5

Request body

CreateHotelBookingDto — required, sent as application/json.

Responses

Errors

Every one of these carries the same envelope. What each error.type means, and whether a retry can succeed, is on the error index — once, for all operations.

StatusWhen
401

No key, or an invalid, revoked or expired key, or a valid key sent to the other environment. error.type tells them apart: authentication_required, invalid_api_key, api_key_revoked, api_key_expired, wrong_environment. On wrong_environment the key is fine and the base URL is not — a vcb_live_… key was sent to the sandbox host or a vcb_test_… key to the live host; error.gatewayEnvironment names the host, error.keyEnvironment the key. Do not rotate the key, change the URL.

402

The balance does not cover this operation (insufficient_funds), or the account is not active (account_not_active). Do NOT retry — the answer only changes once a top-up happens. error.availableCents and error.requiredCents name the shortfall, error.currency the account's currency.

403

The key is not allowed to do this. On insufficient_scope, error.missingScope names the missing scope; on ip_not_allowed the request came from an address outside the allowlist.

409

The same idempotency key was used with a different body (idempotency_key_reuse), or a request with this key is still in flight (idempotency_key_in_progress). The second case may be retried after a short wait.

429

Quota exhausted. Retry-After and error.retryAfter give the wait in seconds. This response MAY be retried — after the stated time.

500

A failure on our side. The requestId in the response belongs in every support request.

503

A supplier is unreachable (upstream_unavailable). May be retried, with growing backoff.

Response headers

Set on every response of this operation, successful or not.

HeaderTypeMeaning
X-RateLimit-LimitintegerUpper limit of the class that applied to this request.
X-RateLimit-RemainingintegerRemaining requests in the running window.
X-RateLimit-ResetintegerUnix time at which the window starts anew.
X-Request-Idstring (uuid)Identifier of this request. Quote it in any support request — including on a successful response, in case the outcome becomes questionable later.

Example

POST /v1/hotels/bookings
curl -sS -X POST "$VACABEE_API_URL/v1/hotels/bookings" \
  -H "Authorization: Bearer $VACABEE_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "rateId": "rate_8f2c…",
  "externalReference": "booking-2026-0915-abc",
  "guests": [
    {
      "firstName": "Ada",
      "lastName": "Lovelace"
    }
  ],
  "contact": {
    "email": "ada@example.com"
  }
}'

Set VACABEE_API_URL to https://api.vacabee.com with a live key, or to https://sandbox-api.vacabee.com with a vcb_test_ key. Binding a host to one kind of key is planned and is not enforced by the host yet, so nothing stops a key from being answered at the wrong base URL — assert the prefix against your configured URL yourself. What the sandbox answers

TypeScript SDK

await vacabee.hotels.book(…);

The typed method sends the right headers, derives the idempotency key where one is required, and returns the response type generated from this document. Installing and using the SDK