Brightwild Booking API
The Brightwild Booking API lets your platform search, price and reserve Brightwild vacation homes in real time. Your servers call it over HTTPS with JSON. This guide covers everything an integration needs: environments, authentication, the reservation flow, retries and errors, limits, and how onboarding works. The API reference documents every operation's exact inputs and outputs, and the OpenAPI document is the machine-readable contract.
The current wire version is 0.2.0-preview.2. Within it, changes are only
additive. It is a preview version, so a breaking change would ship as a new
wire version on a timeline agreed with every enrolled partner
(Versioning).
Overview
Brightwild registers an application for your integration. The application is bound to a catalog of homes published to you, and it holds grants for the capabilities you use. Every call is made by your servers on behalf of that application; nothing in this API is called from a guest's browser or device.
| Term | Meaning |
|---|---|
| Listing | A home you can offer, identified by a stable listingId (a UUID). |
| Quote | The exact price and terms for one listing, dates and party. It expires and is not a hold. |
| Checkout | The process that turns an accepted quote into a reservation request: guest details, payment or partner settlement, and submission. |
| Session capability | A secret returned with each new checkout. Every later call on that checkout must present it. |
| Reservation | The confirmed result of a checkout, identified to your guest and to Brightwild by its confirmation reference. |
Operations are named <domain>.<entity>.<verb>. The ones a reservation flow
uses, in order:
| Step | Operation | HTTP |
|---|---|---|
| Read your site settings | distribution.site.get |
GET /v1/site |
| Sync content | distribution.listing.list, distribution.listing.get |
GET /v1/listings, GET /v1/listings/{listingId} |
| Check availability | distribution.listing.search, inventory.calendar.get |
POST /v1/listings/search, GET /v1/listings/{listingId}/calendar |
| Price a stay | reservation.quote.create |
POST /v1/quotes |
| Start a checkout | reservation.checkout.create |
POST /v1/checkouts |
| Save the guest | reservation.checkout.saveGuest |
PATCH /v1/checkouts/{checkoutId}/guest |
| Card payment (card-paying applications only) | reservation.checkout.setupPayment, reservation.checkout.verifyPayment |
POST /v1/checkouts/{checkoutId}/payment-setup, POST /v1/checkouts/{checkoutId}/payment-method |
| Submit | reservation.checkout.submit |
POST /v1/checkouts/{checkoutId}/submit |
| Follow the outcome | reservation.checkout.getStatus, reservation.checkout.get, reservation.checkout.getConfirmation |
GET /v1/checkouts/{checkoutId}/status, GET /v1/checkouts/{checkoutId}, GET /v1/checkouts/{checkoutId}/confirmation |
| Read a reservation | reservation.reservation.get |
GET /v1/reservations/{reference} |
Webhooks tell your servers when a checkout resolves and when one of your reservations is confirmed, cancelled or changed.
reservation.checkout.applyPromo applies a promotion code, and
cdp.signal.capture records optional measurement signals for browser-facing
applications; a server-to-server integration can ignore both unless agreed
with Brightwild.
Environments
| Environment | API base URL | Token endpoint | What it serves |
|---|---|---|---|
| Sandbox | https://sandbox.api.brightwild.com |
https://sandbox.api.brightwild.com/oauth/token |
The same contract with simulated listings, prices and outcomes. It never creates a real reservation, charges anyone or contacts a property. |
| Production | https://api.brightwild.com |
https://api.brightwild.com/oauth/token |
Live inventory and real reservations. |
Each environment has its own application ID and key; a key works only in the environment it was issued for. Build and test against the sandbox first. Brightwild issues production credentials after the sandbox review in the onboarding checklist.
Sandbox responses say they are simulated: distribution.site.get reports
environment: "simulation", and freshness metadata carries
meta.definitionVersion: "booking-simulation/1". Never show a sandbox result
to a real guest or treat it as a reservation.
The sandbox
The sandbox applies production's rules: authentication and token caching,
quotas and the token endpoint's rate limit, client-named IDs and
Idempotency-Key, version preconditions (expectedVersion or If-Match),
the 16 KB request limit, party rules and the error envelope. Your
application's settlement (partner-settled or card-paying) applies as it will
in production. What it serves is simulated:
- Listings. Six fictional homes in Key West, Rhode Island and Florida's
Gulf Coast, in three collections, each with 3 or 4 photos served from the
sandbox host. Each listing's minimum stay (2 or 3 nights) is the calendar's
minimumNights, and a shorter stay is refused with400 invalid_input. - Availability. Past dates are closed, and about one week in three each
listing has a booked block of 2 to 4 nights. The blocks never move, so
a date that is taken today is taken whenever you test. Taken nights read
available: falsein the calendar, leave the listing out of searches, and refuse a quote with400 invalid_input. The rule, if you want to reproduce it: for the week starting MondayW(weeks counted from Monday 1970-01-05), take the SHA-256 of<listingId>:<week number>; when its first byte is divisible by 3, the block startssecond byte mod 7days afterWand lasts2 + third byte mod 3nights. - Prices. Weeknight and Friday or Saturday rates, cleaning and a service
fee, and each listing's own tax rate (12.5% to 14%). They are the same for
every application. For card-paying applications the promotion code
SIM10takes 10% off accommodation; partner-settled checkouts refuse promotions. - Timing. Payment setup answers after about 2 seconds and a submitted checkout settles after about 5; poll as you would in production.
- Card payment (card-paying applications) has no card form:
paymentSetup.kindissimulation. Verify it with itssetupReferenceand anypaymentMethodIdstartingsim_, such assim_card. - State. Quotes, checkouts and their session capabilities last at most 24 hours and may be cleared sooner. Never depend on sandbox data persisting.
The guest's email chooses the outcome of a checkout, so you can exercise each
unhappy path. Add the suffix to the email's local part, such as
avery+failed@example.com; any other email confirms.
| Email suffix | Outcome |
|---|---|
+failed |
The submitted checkout ends failed with failure.reason refused. |
+sold_out, +price_changed, +policy_changed, +authority_revoked |
The submitted checkout ends failed with that failure.reason. |
+quote_expired |
The submitted checkout ends expired with failure.reason quote_expired and nextActions refresh_quote. |
+uncertain |
The submitted checkout ends uncertain and stays there; production reconciles an uncertain checkout later. |
+uncertain_confirmed, +uncertain_failed |
The submitted checkout is uncertain, then about 5 seconds later confirms, or fails with failure.reason refused, as reconciliation would. |
+setup_unavailable |
Card-paying only: the first payment setup fails with failure: "unavailable" and nextActions offers setup_payment; requesting it again succeeds. |
+setup_declined |
Card-paying only: every payment setup fails with failure: "not_authorized" and nextActions offers contact_support. |
+cancelled |
The reservation confirms, then is cancelled about 5 seconds later, as if by Brightwild's reservations team. |
+changed |
The reservation confirms, then its dates move one day later about 5 seconds after. |
A simulated reservation's cancellation or change applies when your integration next reads its checkout or reservation.
The OpenAPI document lists both base URLs as servers. All paths in this
guide are relative to the base URL.
Authentication
Your application authenticates with an application ID (a UUID) and an
application key (a secret beginning bwak_). Exchange them for a
short-lived access token, then send that token on every API call.
Keep the key on your servers. Store it in a secret manager. Never put it in a browser or mobile app, a URL, a log line, a support ticket, chat or source control. If a key may have been exposed, tell Brightwild at once so it can be revoked.
Get an access token
POST /oauth/token is an OAuth 2.0 token endpoint for the
client_credentials grant. Send a form-encoded body
(application/x-www-form-urlencoded) with grant_type=client_credentials,
and authenticate with the application ID as client_id and the key as
client_secret, either with HTTP Basic authentication (preferred) or as form
fields, not both.
curl -s -X POST https://sandbox.api.brightwild.com/oauth/token \
-u "$BOOKING_APPLICATION_ID:$BOOKING_APPLICATION_KEY" \
-d grant_type=client_credentials
{
"access_token": "eyJhbGciOiJFUzI1NiIs…",
"token_type": "Bearer",
"expires_in": 900,
"scope": "distribution.listing.read distribution.listing.search inventory.calendar.read reservation.checkout.read reservation.checkout.write reservation.quote.create"
}
The token lives at most 15 minutes (expires_in, in seconds) and carries the
capabilities your application's grants give it (scope). You may send
scope to ask for fewer capabilities, and resource (which must equal the
API base URL); neither can widen what was granted.
Send the token on every call:
curl -s https://sandbox.api.brightwild.com/v1/site -H "Authorization: Bearer $ACCESS_TOKEN"
Cache the token. Reuse it for every call until about a minute before it
expires, then exchange the key again; let concurrent requests share one
exchange. The token endpoint is rate limited per application, so requesting a
token per call fails. If an API call answers 401 unauthenticated (the token
expired early or was revoked), exchange the key once and resend that call: the
API refuses an unauthenticated call before any operation runs, so the resend
never applies a write twice.
Token endpoint errors use the OAuth format, { "error", "error_description" }:
| HTTP | error |
Meaning |
|---|---|---|
| 400 | invalid_request, unsupported_grant_type |
The request is malformed: check the content type, grant_type and that credentials are sent once. |
| 400 | invalid_scope, invalid_target |
scope asked for a capability that is not granted, or resource names another API. |
| 401 | invalid_client |
The ID or key is wrong, expired or revoked, belongs to the other environment, or the application is suspended. Do not retry; check the credentials. |
| 429 | rate_limited |
Too many token requests. Wait for Retry-After seconds; cache tokens. |
| 503 | temporarily_unavailable |
Retry after Retry-After seconds. |
Reservation flow
A reservation goes through eight steps. Each step's response tells you what
can happen next, so drive your integration from the API's nextActions rather
than a fixed sequence.
1. Sync content
distribution.site.get returns your site: its capabilities (catalog,
search, quotes, checkout, signals), collections, and the public
policies and standards to show your guests. Offer only the flows its
capabilities include.
distribution.listing.list pages through every listing published to you, in a
stable order. Request up to 50 at a time with limit, and pass the returned
nextCursor as cursor until it is null; filter by collectionId when you
need one collection. Each item is the listing's full content: name,
descriptions, destination, timeZone, capacity (sleeps, bedrooms,
bathrooms), spaces and beds, amenities, images, policies, coordinates and
indexable. distribution.listing.get reads one listing.
- Key everything on
listingId. Theslugis for display and URLs only. - Content is not availability. Never infer that a listed home is bookable; check availability and quote it.
- Sync the full catalog at least daily. A listing that disappears from the
list, or answers
404 not_found, is no longer offered to you: stop selling it. - When
indexableisfalse, keep that listing's page out of search engines. - A missing value (
null) is unknown, not zero: do not invent photos, coordinates or capacity.
2. Check availability
distribution.listing.search finds listings available for exact dates and a
party:
curl -s -X POST https://sandbox.api.brightwild.com/v1/listings/search \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"checkIn":"2026-12-04","checkOut":"2026-12-07",
"party":{"adults":2,"children":0,"infants":0,"pets":0},"limit":20}'
Each result has the listing and its total for the stay, or null where no
live price was returned. A search total is indicative; the quote is the price.
Search pages like the listing list (limit, cursor, nextCursor).
inventory.calendar.get returns one listing's open dates and minimum nights
for up to 93 days (startDate to endDate, both included). Use it to build a
date picker, not to promise a price.
Search and calendars read live availability, with a lower quota than content
reads (Rate limits). To answer bursts of identical requests,
the API may serve a search result up to about a minute old and a calendar up
to about two minutes old; meta.asOf says when the result was read. A home
can therefore be booked by someone else between your search and your quote,
and a total can change: the quote is the price, so quote before you take the
guest to checkout. Do not add a cache of your own on top. Do not poll search
or calendars across your whole catalog; query what your guests are looking
at. A failed search is an error, never an empty result: do not show "no
availability" when the call failed. A live search or calendar read that takes
longer than about five seconds answers 503 unavailable, which is retryable,
rather than holding your request open; a retry a few seconds later often gets
the answer that read went on to produce.
3. Quote
reservation.quote.create prices one listing for exact dates and party. You
name the quote with a new UUIDv7 (Idempotency):
curl -s -X POST https://sandbox.api.brightwild.com/v1/quotes \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"quoteId":"01928a4e-7b1c-7d3e-9f00-6c2b8a1d4e55",
"listingId":"<listingId>","checkIn":"2026-12-04","checkOut":"2026-12-07",
"party":{"adults":2,"children":0,"infants":0,"pets":0}}'
The quote returns charges (accommodation, fees, cleaning, taxes, total,
amount due now and the payment schedule), its terms (a version and the
policies the guest accepts, such as cancellation and house rules) and
expiresAt. Show the guest the exact charges and policies. A quote is not a
hold: the home stays available to others until a reservation is confirmed.
4. Start a checkout
reservation.checkout.create starts a checkout from a current quote. You name
it with a new UUIDv7:
curl -s -X POST https://sandbox.api.brightwild.com/v1/checkouts \
-H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"checkoutId":"01928a4f-0c2d-7e41-8a22-31f5b7c9d0aa",
"quoteId":"01928a4e-7b1c-7d3e-9f00-6c2b8a1d4e55"}'
The response holds the checkout, its sessionCapability and
sessionExpiresAt. Store the session capability with your record of the
checkout (Checkout session capability).
Every checkout carries a version, a state and nextActions.
5. Save the guest
reservation.checkout.saveGuest records the guest who will stay: first and
last name, email, and phone in E.164 format (for example +13055550123).
Send the checkout's session capability and the version you last read:
curl -s -X PATCH https://sandbox.api.brightwild.com/v1/checkouts/$CHECKOUT_ID/guest \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Session-Capability: $SESSION_CAPABILITY" -H "Content-Type: application/json" \
-d '{"expectedVersion":1,
"guest":{"firstName":"Avery","lastName":"Jones",
"email":"avery@example.com","phone":"+13055550123"}}'
The details are recorded on the reservation, and Brightwild uses them to contact the guest about the stay.
6. Payment or partner settlement
What follows depends on how your application settles:
- Partner-settled applications collect payment from the guest on their own
platform. There are no payment steps: once the guest is saved, the checkout
is
readyandnextActionsofferssubmit. See Partner-settled reservations. - Card-paying applications collect the guest's card through hosted card fields. See Card payment.
Brightwild sets this on your application; no request field changes it.
7. Submit
reservation.checkout.submit asks for the reservation. Send the version of
the quote's terms the guest accepted, with termsAccepted: true:
curl -s -X POST https://sandbox.api.brightwild.com/v1/checkouts/$CHECKOUT_ID/submit \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Session-Capability: $SESSION_CAPABILITY" -H "Content-Type: application/json" \
-d '{"expectedVersion":2,"termsVersion":"<quote.terms.version>",
"termsAccepted":true}'
Submit answers 202 Accepted with the checkout's status, normally pending.
Accepted is not confirmed: tell the guest the reservation is being confirmed.
8. Status and confirmation
Read reservation.checkout.getStatus until the checkout leaves pending,
waiting pollAfterSeconds between reads. The read never changes the
checkout. When the state is confirmed, read
reservation.checkout.getConfirmation for the reservation's reference, the
stay, the final charges and confirmedAt. Show your guest the reference;
it is also how you and Brightwild's teams identify the reservation.
References are Brightwild's: BW- and two groups of four letters and digits,
such as BW-4F7K-9QXM, never using I, L, O or U. Store a reference as an
opaque string: a reservation made before Brightwild references existed keeps
the reference it was given.
materialization on the confirmation is pending until the reservation
reaches all of Brightwild's records and ready after; the reservation is
confirmed either way.
Reading a reservation
reservation.reservation.get (GET /v1/reservations/{reference}) returns a
reservation your application's checkout made, as Brightwild holds it now:
state (confirmed, cancelled or pending), listingId, checkIn,
checkOut, party, charges, cancelledAt and updatedAt. It reflects
changes Brightwild's reservations team made, so read it after a
webhook rather than keeping your own copy of what changed. Until
the reservation reaches all of Brightwild's records (materialization: "pending", usually under a minute), it answers with the confirmed checkout's
dates, party and charges. A reference your application did not reserve
answers 404 not_found.
Checkout states
state |
Meaning | What to do |
|---|---|---|
needs_guest |
Guest details are missing. | saveGuest. |
needs_payment |
Card-paying only: payment is not set up. | Follow nextActions (Card payment). |
ready |
Everything needed to submit is in place. | Present the terms; submit. |
pending |
Submitted; the outcome is not known yet. | Read the status after pollAfterSeconds. |
requires_action |
Card-paying only: the guest must complete a step, such as card authentication. | Follow nextActions. |
confirmed |
The reservation is confirmed. | Read the confirmation. |
uncertain |
Brightwild cannot yet tell whether the reservation was made and is reconciling it. | Do not submit again or start a new checkout. Tell the guest it is being confirmed, and contact support with the checkout ID if it persists. |
failed |
The reservation was not made. | Tell the guest; quote again if they want to retry. failure.reason says why, when it is known. |
expired |
The quote expired before the reservation was made. | Create a new quote and a new checkout, and have the guest accept the new terms. |
A failed or expired checkout, and its status, carry failure.reason when
Brightwild knows why no reservation was made. An uncertain checkout never
has one: it may yet confirm.
failure.reason |
Meaning |
|---|---|
sold_out |
The home is no longer available for those dates. |
quote_expired |
The quote expired before the reservation was made. |
price_changed |
The price changed since the quote. |
policy_changed |
The home's terms changed since the quote. |
authority_revoked |
Your application's permission to book was withdrawn before a reservation was attempted. |
refused |
The reservation was refused. |
In every case no reservation exists. Offer the guest another quote, or other
dates or homes for sold_out. A checkout without failure has no known
reason; treat it as refused.
nextActions lists what the checkout allows now: save_guest,
setup_payment, verify_payment, submit, poll, refresh_quote,
view_confirmation or contact_support. New states and actions may be
added; treat an unknown one as "read the checkout again later" and never as
success.
Card payment
Card-paying applications collect the card in the guest's browser through the payment provider's hosted fields, so card numbers never reach your servers or Brightwild:
reservation.checkout.setupPaymentanswers202with the checkout. Read the checkout untilpaymentSetup.stateisready(with the hosted fields'kind,publishableKey,clientSecretandsetupReference) orfailed. A failed setup whosefailureisunavailableorunresolved, or a request unanswered for a minute, can be requested again whennextActionsofferssetup_payment.- Confirm the card with the hosted fields in the browser.
kind: "stripe"uses Stripe's hosted setup flow; the sandbox returnskind: "simulation", which has no card form. reservation.checkout.verifyPaymentrecords the verified payment method: thesetupReference, thepaymentMethodIdandconsentAccepted: true.- Present the payment authorization and the current terms, then
submit.
dueNow and schedule in the charges say when Brightwild collects the
total. A verified card is not a confirmed reservation.
Checkout session capability
Creating a checkout returns a sessionCapability: the only credential that
reaches that checkout afterwards. A checkout ID alone grants nothing. Send it
in the Session-Capability header on every checkout operation after
create: reads, guest, payment, submit, status and confirmation.
- Keep it on your servers with your record of the checkout, encrypted at rest. Never send it to a browser or log it.
- It is bound to your application and to that checkout, and it expires at
sessionExpiresAt. - If you lose the create response, repeat the identical create request with
the same
checkoutId: you receive the same checkout and capability.
Idempotency and client-named IDs
Every create names its new resource with an ID you generate: quoteId,
checkoutId and signalId. Use a UUIDv7 (time-ordered; any RFC 9562 UUID is
accepted) and store it before you send the request.
- Repeating a create with the same ID and the same input returns the existing resource, so a create is always safe to retry after a timeout or lost response.
- The same ID with different input, or an ID already used elsewhere, answers
409 conflict. - A caller that cannot generate IDs may send an
Idempotency-Keyheader (1–255 printable characters) on a create instead; the API derives the ID from it. Send an ID or a key, not both.
Repeated requests do not count against your quota.
Version preconditions
Every checkout has a version, and every change moves it. Each checkout
command (saveGuest, applyPromo, setupPayment, verifyPayment, submit)
names the version it read as expectedVersion in the body, or as an HTTP
If-Match: "3" header.
- If the checkout changed since you read it, the command answers
412 precondition_failed. Read the checkout, show the guest anything that changed (for example a new total), and send the command again with the new version. - Repeating a command that already applied returns the checkout as it is, so a command is safe to retry with the same version.
Asynchronous steps
Three steps finish after their response, and each answers 202 Accepted
with a Location header naming the resource to read for the outcome, and,
while there is something to wait for, a Retry-After header with the
response's pollAfterSeconds:
- Reservation request (
reservation.reservationRequest.create).Locationis the request (/v1/reservation-requests/{reservationRequestId}); see Reservation requests. - Submit (
reservation.checkout.submit).Locationis the checkout's status (/v1/checkouts/{checkoutId}/status). The reservation is made durably after the response. Readreservation.checkout.getStatus, waitingpollAfterSeconds(1–60 seconds) between reads, until the state leavespending. A submitted request is never lost, so recovery after a timeout or restart is reading the same checkout. Never submit a different checkout for the same guest because a response was slow or lost. - Payment setup (
reservation.checkout.setupPayment, card-paying only).Locationis the checkout. Read it untilpaymentSetup.stateleavesrequested.
Status and checkout reads are pure: they never advance, retry or expire the
checkout. A webhook (checkout.resolved) also tells your
servers when a submitted checkout reaches its final state; keep polling as
above, because a delivery can be delayed.
Webhooks
Brightwild sends your servers an HTTPS POST when one of your checkouts
resolves and whenever one of your reservations is confirmed, cancelled or
changed, including changes Brightwild's reservations team makes. Deliveries
follow the Standard Webhooks
specification: every request is signed, and the body is a small JSON event.
You receive events only for checkouts and reservations your own application
made.
| Event type | Sent when | data |
|---|---|---|
checkout.resolved |
A submitted checkout reaches its final state: confirmed, failed or expired. uncertain is not final and sends none; an uncertain checkout that later confirms sends it then. |
checkoutId, state, version (the checkout's version after it resolved), reference (when confirmed), resolvedAt |
reservation.confirmed |
A reservation is confirmed. | reference, checkoutId, state, confirmedAt |
reservation.cancelled |
A reservation is cancelled. | reference, checkoutId, state, cancelledAt |
reservation.changed |
A reservation's dates, party, price or home changed. | reference, checkoutId, state, changes (any of dates, party, price, listing), changedAt |
An event is thin on purpose: identifiers, state, version and time, never the
guest's details or prices. For the current dates, party and charges, read the
reservation (GET /v1/reservations/{reference}) or the checkout.
{
"id": "evt_5f2c1a0e9b7d4c3a8e6f1b2d3c4a5e6f",
"type": "reservation.changed",
"schemaVersion": 1,
"timestamp": "2026-10-22T14:03:11.000Z",
"data": {
"reference": "BW-4F7K-9QXM",
"checkoutId": "0190a000-0000-7000-8000-0000000000c1",
"state": "confirmed",
"changes": ["dates"],
"changedAt": "2026-10-22T14:03:11.000Z"
}
}
ididentifies the event: the same on every delivery of it.timestampis when the change happened, not when it was delivered.schemaVersionis the envelope's version. Within it, fields are only added; ignore fields and event types you do not know. A change that would break a receiver ships as a new version or event type, announced in the changelog.
Verify every request. Each request carries webhook-id,
webhook-timestamp and webhook-signature headers. The signature is
v1, followed by the base64 HMAC-SHA256 of
<webhook-id>.<webhook-timestamp>.<raw body>, keyed with your endpoint's
signing secret (the base64 part after whsec_). Compute it over the raw
body before parsing it, compare it in constant time with each signature in
the header (it may list several, space-separated, during a secret rotation),
and reject a request whose timestamp is more than five minutes from your
clock. The Standard Webhooks project publishes verification libraries for
most languages.
Answer quickly. Respond with any 2xx status within 15 seconds, then
process the event: store it and work from a queue. Any other answer, or none,
is a failed delivery. Failed deliveries are retried with increasing delays for
at least a day; an endpoint that keeps failing may be paused, and Brightwild
tells your technical contact.
Expect repeats and any order. An event can arrive more than once, and
events can arrive out of order. Record the webhook-id of each event you
process and skip a repeat. Do not rebuild state from the sequence of events:
on each event, read the reservation or checkout and act on what it says now.
Manage endpoints in the webhook portal. Brightwild enables a webhook portal for your application, where your technical contact adds and removes endpoint URLs (HTTPS only), chooses event types, reveals and rotates the signing secret, and sees and replays recent deliveries. Ask your Brightwild contact to enable it for each environment.
Catching up from the event feed
GET /v1/events (reservation.event.list) returns your application's
events, oldest first, for the last 30 days, whether or not they reached your
endpoint. Use it to catch up after your endpoint was down or paused.
- Start without a cursor, then send each response's
nextCursorwith your next call.nextCursoris always present: at the end of the feed it holds your place, so a later call with it returns only newer events.hasMoresays whether more events are waiting now.limitis 1 to 100 (default 50). - Each item is the event exactly as delivered, with the same
id, so skip any you already processed. An event joins the feed about 10 seconds after it is recorded. - A cursor older than the 30 days answers
422 resync_required: read your reservations and reservation requests for their current state, then start again without a cursor. - Calls count against the read quota. Poll it no more than once a minute.
In the sandbox, simulated reservations produce the same events (see the
+cancelled and +changed outcomes above); sandbox delivery to your
endpoints follows, announced in the changelog. The TypeScript SDK's
simulator emits the same events in process through its webhooks option,
and both serve the event feed.
Dates, money and parties
- Dates are property-local calendar dates,
YYYY-MM-DD, in the listing's IANAtimeZone. A stay is half-open: it starts oncheckInand ends the morning ofcheckOut, so 2026-12-04 to 2026-12-07 is three nights. A stay is 1 to 90 nights. - Instants such as
expiresAt,sessionExpiresAtandconfirmedAtare RFC 3339 timestamps in UTC. - Money is
{ "amountMinor": 45000, "currency": "USD" }: an integer in the currency's minor unit (cents) and an ISO 4217 code. Format it with the currency's exponent.nullmeans unknown, never zero. - Charges:
feesalready includecleaning;cleaningis shown separately for display only, so never add it to the total again.totalis the stay's price.dueNowandscheduleare payment timing, distinct from the total. - Parties count
adults(at least 1),children,infantsandpets. Send the real counts; the API refuses a party a listing cannot take with422 policy_restrictedand never drops a count to make a request pass. At present, quotes and searches refuse parties with infants or with more than one pet, and pets at a listing that does not allow them. - Absence:
nullmeans unknown; an absent field means it does not apply. - Forward compatibility: ignore response fields you do not know, and handle enum values you do not know (states, actions, failure reasons) without failing.
Errors and retries
Every response carries an x-request-id header. Log it with every call and
include it in any support request.
Errors share one envelope:
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Retry later.",
"requestId": "4bf92f3577b34da6a3ce929d0e0e4736",
"retryable": true,
"retryAfterSeconds": 12
}
}
code is stable; message is safe to log but not written for guests.
details appears on some errors, for example the invalid fields of an
invalid_input. These are all the codes, with their HTTP status:
| Code | HTTP | Meaning | Retry guidance |
|---|---|---|---|
| invalid_input | 400 | The request is invalid. | No automatic retry |
| unauthenticated | 401 | Authentication is required. | No automatic retry |
| forbidden | 403 | The caller may not perform this operation. | No automatic retry |
| not_found | 404 | The resource was not found. | No automatic retry |
| conflict | 409 | The request conflicts with an existing resource. | No automatic retry |
| invalid_state | 409 | The resource is not in a state that allows this operation. | No automatic retry |
| precondition_failed | 412 | The resource has changed since it was read. | No automatic retry |
| rate_limited | 429 | Too many requests. Retry later. | Yes; inspect the delay and read the resource first |
| unavailable | 503 | The operation is temporarily unavailable. | Yes; inspect the delay and read the resource first |
| unresolved | 504 | The outcome is not yet known. Read the resource before retrying. | Yes; inspect the delay and read the resource first |
| internal | 500 | The operation failed. | No automatic retry |
| policy_restricted | 422 | This listing does not support the requested party. | No automatic retry |
| not_configured | 503 | This capability is not enabled. | No automatic retry |
| quote_expired | 409 | Refresh the quote and accept its current terms. | No automatic retry |
| payment_not_required | 422 | This checkout is settled by your organization and takes no payment. Submit it once the guest's details are saved. | No automatic retry |
| resync_required | 422 | The cursor is older than the event feed's replay window. Read your reservations and requests for their current state, then read the feed again without a cursor. | No automatic retry |
How to retry:
- Retry only when
retryableistrue, afterretryAfterSeconds(also sent asRetry-After). Without a delay, back off exponentially from one second with jitter, up to a minute. - Retrying is safe because creates reuse your ID and checkout commands reuse the version you read: a retry returns the existing result instead of applying the write twice.
- After a timeout or network error on a write, repeat a create with the same
ID, and read the checkout (
reservation.checkout.get) before repeating a checkout command. Never start a replacement checkout because a response was lost. unresolved(504) means an outcome is not yet known; read the resource before retrying.401 unauthenticated: exchange your key for a new token once and resend (Authentication).403 forbiddenand503 not_configuredmean your application is not granted that operation or capability; contact Brightwild rather than retrying.quote_expired(409): create a new quote and checkout, and have the guest accept the new terms.
Rate limits
Each application has per-minute quotas by kind of call. Quotas refill continuously, so short bursts are fine.
| Tier | Operations | Default quota |
|---|---|---|
| Read | Site, listings, checkout and status reads | 360 per minute |
| Search | Search, calendars and quotes | 36 per minute |
| Write | Checkout create and commands | 120 per minute |
| Signal | cdp.signal.capture |
240 per minute |
Brightwild may agree different quotas for your application during onboarding. Live reads (search, calendars and quotes) also share availability capacity with Brightwild's own channels; when it is busy the API refuses quickly rather than queueing your request. Every search and calendar request counts against your quota, including one the API answers from its short-lived cache (Check availability); a quote is never cached.
Over a limit, the API answers 429 rate_limited with retryAfterSeconds and a
Retry-After header. Wait that long before trying again, slow your request
rate, and do not retry in a tight loop. Repeated creates and commands that
return an existing result do not use quota.
Partner-settled reservations
A partner-settled application collects payment from the guest on its own platform. Brightwild takes no card for its reservations: the checkout has no payment steps, and submit requests the reservation with your company recorded as the payer.
- Brightwild sets settlement on your application under your agreement; there
is no request field for it. Every checkout reports it as
settlement.kind:partnerfor your checkouts,cardfor a card-paying application's. - A new checkout is
needs_guestwithpaymentReady: true. AftersaveGuest, it isreadyandnextActionsofferssubmit.setupPaymentandverifyPaymentare never offered and answer422 payment_not_required. - The checkout keeps the price of the quote it was created from:
applyPromoanswers403 forbidden. For another price, create a new quote and checkout. - The confirmation's
chargesare the reservation's price for your channel (Partner pricing).dueNowandscheduledescribe Brightwild's own collection and do not apply to how you charge your guest. - Brightwild records each reservation's price and the amount owed under your agreement. Invoicing and settlement follow the agreement and happen outside this API.
- Charging, refunding and receipts for your guest are yours. Changes and cancellations go through Brightwild's reservations team (Cancellations and changes), and a webhook tells you of each.
The sandbox serves partner-settled checkouts with simulated outcomes.
Reservation requests
A partner-settled application can request a reservation in one call instead
of a checkout's create, guest and submit steps.
reservation.reservationRequest.create (POST /v1/reservation-requests)
takes a new UUIDv7 reservationRequestId, the quoteId, the quote's
termsVersion with termsAccepted: true, the guest, and optionally your
own partnerReference for it, such as your order number. The quote fixes
the home, dates, party and price; for another selection or price, quote
again. It answers 202 with the request:
{
"reservationRequestId": "0199d647-5c00-7000-8000-000000000001",
"version": 3,
"state": "pending",
"quoteId": "0199d647-5c00-7000-8000-000000000002",
"partnerReference": "WY-1001",
"reference": null,
"nextActions": ["poll"],
"pollAfterSeconds": 2
}
Read reservation.reservationRequest.get
(GET /v1/reservation-requests/{reservationRequestId}) after
pollAfterSeconds, or after a webhook, until it leaves
pending. No session capability is involved: your application's access
token reaches its own requests, and another application's answer
404 not_found.
state |
Meaning | What to do |
|---|---|---|
pending |
The reservation is being made. | Read the request again after pollAfterSeconds. |
unresolved |
Brightwild cannot yet tell whether the reservation was made and is reconciling it. | Never create another request for the same home and dates. Tell the guest it is being confirmed; contact support with the request ID if it persists. |
confirmed |
The reservation was made. | Read it with reservation.reservation.get by reference. |
failed |
No reservation was made. | Quote again if the guest wants to retry. failure.reason says why, when it is known (Checkout states). |
A confirmed request stays confirmed if the reservation is later cancelled;
read the reservation for its current state. Sending the same request again,
such as after a lost response, returns it as it is now; other input under
the same reservationRequestId answers 409 conflict. A quote backs one
reservation: a request on a quote another of your requests or checkouts
already uses answers 409 conflict, whatever its reservationRequestId, so
a retry under a new ID never makes a second reservation. To try again after
a failed request, quote again. A partnerReference is unique among your
application's requests: using it again on another request answers
409 conflict. Send one with every request, so you can find it after a
lost response. A card-paying application answers 503 not_configured.
reservation.reservationRequest.list (GET /v1/reservation-requests) lists
your application's requests, newest first, limit (up to 50) at a time with
a cursor for the next page. With partnerReference, it returns the request
you named with it, so a request whose response you lost can be found even if
you did not keep its reservationRequestId. Webhooks for a request name its
reservationRequestId as their checkoutId.
Partner pricing
Every price the API returns to your application is Brightwild's price for your channel, as Brightwild configures it: search totals, quotes, checkout totals and confirmation charges, taxes included. Show these amounts as they are; do not adjust them. No request parameter changes them.
The quote is the authoritative price. A search total can differ from the quote that follows it, so always quote before you take the guest to checkout, and show the quote's charges.
Cancellations and changes
At launch the API does not cancel or change confirmed reservations.
Brightwild's reservations team handles cancellations, date or party changes
and other requests for a confirmed reservation. Contact the reservations team through your Brightwild partner contact with
the reservation's confirmation reference, and your checkoutId if you have
it.
The cancellation policy the guest accepted is in the quote's
terms.policies; Brightwild applies it to cancellations. Whoever makes a
cancellation or change, a webhook (reservation.cancelled or
reservation.changed) tells your servers, and
reservation.reservation.get returns the reservation as it is now.
Operations to cancel and to change a reservation will follow as an additive
change, announced in the changelog.
Versioning and compatibility
The wire version is the OpenAPI document's info.version, and the release
manifest (release.json) binds it to a digest of the document.
- Within a wire version, changes are only additive: new operations, new optional inputs, new response fields, new enum values and new error codes. Your integration must ignore what it does not know.
- A breaking change (removing or renaming anything, tightening validation, making an input required, or changing units or meaning) ships only as a new wire version. Brightwild announces it in the changelog and directly to each enrolled partner's technical contact.
- A deprecated operation answers with
DeprecationandSunsetheaders, with at least 90 days' notice before production applications lose it. - While the wire version is a preview (
-preview.N), the timing of any breaking change is agreed with every enrolled partner before it ships.
Changelog
The changelog records every change to the contract and to this documentation.
Support
Contact your Brightwild partner contact. Include the environment, the operation, the time (UTC),
the x-request-id and, for a checkout, its checkoutId. Never send
application keys, access tokens, session capabilities or guest card details.
Onboarding checklist
- Agreement and contacts. Name a technical contact for credentials, change notices and incidents.
- Sandbox credentials. Brightwild registers your sandbox application, bound to your catalog and settings (such as settlement), and delivers its application ID and key through a secure channel agreed with your technical contact, never in plain email or chat. Put them in your secret manager.
- Build against the sandbox. Cover the whole flow: token caching, content
sync, search, quote, checkout, guest, submit and status, including
412handling, retries that reuse IDs and versions,429back-off, and every terminal state. Build your webhook endpoint too: verify signatures, answer fast, skip repeats, and read the reservation on each event. - Sandbox review. Share a test run's request IDs with Brightwild. The
review checks that keys stay server-side, tokens are cached, retries never
duplicate a checkout, status is polled with
pollAfterSeconds, prices and terms are shown exactly, and request IDs are logged. - Production promotion. Brightwild issues separate production credentials and enables production access. Reservation limits per checkout and per day may apply at first, agreed with you. The first live reservations are watched together.
- Key rotation. Keys expire (the expiry is given with the key). To rotate, ask for a replacement: Brightwild issues a new key while the old one keeps working, you deploy it, and Brightwild revokes the old key once it is no longer used. If a key may be exposed, Brightwild revokes it immediately; tokens it already obtained stop working within 15 minutes.
- Operate. Keep your technical contact current, watch for
Deprecationheaders and changelog entries, and report errors with their request IDs.