StayBindDevelopers
Reference

Errors & reliability

Booking API error codes, custom-OTA push semantics, the idempotency contract, the self-healing sweeps, and rate limits.

Booking API errors

Errors return a non-2xx status and a tRPC error body; the stable fields are error.data.code and error.data.httpStatus.

codeHTTPMeaningWhat to do
UNAUTHORIZED401missing/invalid API keycheck the x-api-key header and the key's environment
BAD_REQUEST400invalid input or a domain rule rejected itfix the input; the message says what failed
NOT_FOUND404the resource isn't in this orgverify the id and the key's org
CONFLICT409the nights are no longer availablere-query availability and offer an alternative

CONFLICT is a normal outcome, the ledger refusing to double-book, not a server fault. Always handle it.

Custom-OTA push results

When StayBind calls your endpoints, your HTTP status decides what happens next:

Your responseStayBind treats it asEffect
2xxsuccessdone
5xx / network errorretryableback off and retry; the nightly full sync re-pushes anyway
4xxpermanent rejectionstop retrying, flag the connection for the operator (usually a bad mapping)

When StayBind receives your reservation webhook, it always answers 200, see the reliability contract.

Idempotency

Idempotency is the backbone of safe at-least-once delivery. Two ids carry the contract on a reservation:

  • providerResId is the per-delivery key. StayBind ingests each distinct value exactly once; re-sending the same one (a webhook retry, an overlapping reconcile pull) is a no-op. Make it unique per delivery/revision.
  • providerBookingId is the stable booking id, constant across revisions. A modified or cancelled delivery uses it to find and update the existing booking instead of creating a new one. Without it, revisions look like new bookings.

On your side, apply outbound availability/rate/restriction pushes as upserts keyed by date (never increments), so order and duplicates don't matter.

Payment captures are likewise idempotent on the payment id, a re-delivered capture confirms the booking once.

The self-healing sweeps

You get these for free; they're why a dropped request never loses a booking:

SweepCadenceWhat it catches
Reconcile pull~every 15 mina reservation whose webhook was missed (pulled from your GET /reservations)
Nightly full synconce daily, off-peakavailability drift (re-pushes the forward horizon to every channel)
Acknowledge after commitper deliverya delivery is acked only after it's durably written, so a crash mid-ingest re-surfaces it rather than dropping it

Conflicts and overbooking

The ledger's database constraint makes overlapping bookings impossible to commit. Two outcomes follow:

  • A booking attempt for taken nights returns CONFLICT (Booking API) or is reported in the webhook's conflicts array (custom OTA).
  • A true cross-channel overbooking (two OTAs genuinely sold the same night within the propagation window) is terminal, the booking is real on the OTA, so StayBind records it and alerts a human to relocate or refund, rather than silently dropping it.

Rate limits

  • Outbound (StayBind → your OTA): throttled per account so a busy operator never floods your API; bursts are coalesced. Make your endpoints accept batched updates arrays.
  • Inbound (you → StayBind): post freely, idempotency makes retries safe. Use sane client-side back-off on 5xx, and prefer batching multiple reservations into one webhook call over many tiny calls.

On this page