Map reservations
How StayBind normalizes your reservation payload into its canonical model, the default shape, writing a custom mapper, and how a connection's credentials are stored.
StayBind reduces every reservation, from any source, to one CanonicalReservation. For a custom OTA, a small mapReservation function does that translation. You have two options: send the default shape and it works as-is, or send your own shape and StayBind configures a mapper for it.
The default shape
Out of the box, the mapper reads these fields from each raw reservation:
| Your field | Maps to | Notes |
|---|---|---|
id or reservationId | providerResId | per-delivery idempotency key, ingested once |
roomTypeId or roomType | providerRoomTypeId | resolved to a StayBind unit via the listing mapping |
status | status | new | modified | cancelled (anything else → new) |
checkIn, checkOut | dateRange | YYYY-MM-DD, check-out exclusive |
channel | externalChannel | optional label for the downstream source |
totalMinor | total | optional, integer paise, currency INR |
guest.name | guest.name | required (defaults to "Guest" if absent) |
guest.email, guest.phone | guest.email, guest.phone | optional |
guest.adults, guest.children | guest.adults, guest.children | default 1 / 0 |
Carry a stable booking id for modify and cancel
The default mapper only sets providerResId. If you send modified or
cancelled revisions, your payload must also carry a stable booking id
that is constant across revisions, and the mapper must set it as
providerBookingId. Without it, a modification looks like a brand-new booking.
Mention this when you set up your connection so the mapper includes it.
Writing a custom mapper
If your payload differs, the mapper is a single pure function: raw payload in, canonical reservation out. This is the only per-partner code, everything else (HTTP, retries, signature checks, ingestion) is shared.
type MapReservation = (raw: unknown) => CanonicalReservation;const mapReservation: MapReservation = (raw) => {
const r = raw as MyOtaReservation;
return {
provider: "partner",
externalChannel: "my-ota",
providerResId: r.event_id, // unique per delivery
providerBookingId: r.booking_ref, // stable across revisions
providerRoomTypeId: r.room_code,
status: r.cancelled ? "cancelled" : r.amended ? "modified" : "new",
dateRange: { checkIn: r.arrival, checkOut: r.departure },
guest: {
name: r.customer.full_name,
email: r.customer.email,
phone: r.customer.mobile,
adults: r.pax.adults ?? 1,
children: r.pax.children ?? 0,
},
total: { amount: r.total_paise, currency: "INR" },
raw, // keep the original for the audit trail
};
};The full set of canonical fields (including the optional ackRef, ratePlanId, and bookedAt) is documented in the canonical model.
Registering the adapter
On the StayBind server, your OTA is registered as a PartnerChannelAdapter with your mapper (and, recommended, a signature verifier):
new PartnerChannelAdapter({
mapReservation,
// verify the inbound webhook against the connection's webhookSecret
verifySignature: (event, secret) => event.signature === secret,
});This is platform-side wiring; as the OTA you provide the payload shape (or confirm you'll send the default), and the StayBind team or a self-hosting operator registers it.
Credentials
A connection stores only a pointer (secretRef); the actual secrets live in StayBind's secret store, never in the database. Your connection needs three values:
Prop
Type
In the default deployment these are read from an environment variable named CHANNEL_SECRET_<REF> holding a JSON object:
{ "baseUrl": "https://api.my-ota.com", "apiKey": "…", "webhookSecret": "…" }The secret store is an interface, so a production deployment can swap the env backend for a vault (KMS, Doppler, Vault) without changing any call site. The sync engine only ever sees the resolved credentials.
Putting it together
partner connection in the Channels screen; StayBind issues the connectionId.baseUrl, apiKey, webhookSecret, and your reservation payload shape; they're stored against the connection.Next: Sync availability out.
Receive bookings
Post your OTA's reservations to a StayBind channel webhook. New, modified, and cancelled deliveries keep the ledger in sync, idempotently and with self-healing retries.
Sync availability out
How StayBind pushes availability, rate, and restriction changes to your OTA so it mirrors the ledger, with delta pushes, a nightly full sync, and rate limiting.