Skip to content

CreateBooking

POST
/wink.partner.v1.Booking/CreateBooking
curl --request POST \
--url https://example.com/wink.partner.v1.Booking/CreateBooking \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "idempotencyKey": "example", "bookingOriginUrl": "example", "rooms": [ { "propertyId": "example", "checkIn": "example", "checkOut": "example", "roomRateId": "example", "bedroomConfigurationId": "example", "occupancy": { "adults": 1, "children": [ { "age": 1, "quantity": 1 } ], "quantity": 1 }, "promotionalCodes": [ "example" ], "ancillaries": [ { "inventoryId": "example", "channelInventoryId": "example", "offerId": "example", "type": "INVENTORY_SCOPE_UNSPECIFIED", "quantity": 1, "requestedStart": "example", "requestedEnd": "example" } ], "specialRequests": "example", "guest": { "firstName": "example", "lastName": "example", "email": "example", "phone": "example" }, "externalBookingCode": "example", "externalTransactionId": "example", "externalCustomerId": "example", "externalFees": [ { "feeId": "example", "amount": "example", "type": "example", "description": "example" } ] } ], "currencyCode": "example", "languageCode": "example", "traceId": "example" }'

Create a booking on behalf of the agency

Books inventory against the agency’s balance and returns the resulting confirmations. This is the one rpc that MOVES MONEY on the way in.

IDEMPOTENT ON idempotency_key. gRPC clients retry on UNAVAILABLE by default and a retried create is a double booking and a double debit, so the key is REQUIRED rather than optional. Replaying a key returns the ORIGINAL confirmations without booking again. Reuse a key only to retry the same intent — a new booking needs a new key, and sending a used key with a different payload is FAILED_PRECONDITION rather than a silent no-op, because the alternative is an agent believing they booked something they did not.

A multi-room create produces SEVERAL bookings sharing a group_id, which is what makes them refundable together via RefundBookings.

Returns INVALID_ARGUMENT when rooms is empty, the request is malformed or idempotency_key is blank, FAILED_PRECONDITION when the key was used for a different request, and PERMISSION_DENIED when the account is not a travel agent or lacks BOOKING:WRITE.

Media typeapplication/json

Request for CreateBooking.

Shaped from the domain’s CreateAgentBookingRequest rather than from what a single-room booking looks like. rooms is REPEATED because the domain takes a list and because that is how a group booking is made — the group_id that RefundBookings operates on exists precisely because one create can produce several bookings. A one-room booking is a one-element list.

object
idempotencyKey

Idempotency key. REQUIRED — see the rpc comment for why this is not optional on a money-moving call. Any opaque string up to 128 characters that is unique per booking intent; a UUID is the obvious choice.

string
bookingOriginUrl

Which of the agency’s sites or apps the booking originated on, as an absolute URL.

REQUIRED, and an explicit field rather than something sniffed from a header. Over REST this was read off the Origin/Host of the inbound request, which gRPC has no equivalent of — and which quietly recorded whatever proxy happened to terminate the call. Stating it makes the value mean what it always claimed to.

string
rooms

What to book. At least one room; each entry may name a different property, so a multi-property itinerary is one call.

Array<object>

One room to book.

Field-for-field from AgentBookingRequest, minus nights (derivable from the dates, and a second source of truth for the same fact) and vendor_specific (a vendor-keyed metadata map, excluded for the same reason its outbound twin is).

object
propertyId

The property to book at. From a Search or Inventory result.

string
checkIn

Local dates at the property, ISO-8601 (YYYY-MM-DD).

string
checkOut
string
roomRateId

The rate to book. From a pricing result’s RoomRate.

string
bedroomConfigurationId

Which bedroom layout, when the rate offers more than one.

string
occupancy

Who is in the room, and how many identical rooms of it. Child AGES are required rather than a count: properties price children in age bands, and an unpriceable age produces a rate that cannot be booked.

object
adults

Adults in this room. Defaults to 1.

integer format: int32
children

Children in this room, by age. Ages are REQUIRED, not optional detail: properties price children in age bands, and an unpriceable age produces a rate the guest cannot actually book.

Array<object>

A number of children of the same age.

object
age

Age at check-in, in years. Zero is valid — infants are priced differently from older children, not ignored.

integer format: int32
quantity

How many children of this age. Defaults to 1.

integer format: int32
quantity

How many identical rooms of this configuration. Defaults to 1.

integer format: int32
promotionalCodes

Promotional codes to apply. Silently ignored when they do not apply to this rate, which is the same behaviour the booking engine has.

Array<string>
ancillaries

Extras to add to this room — spa treatments, restaurant sittings, activities, add-ons, and the in-room ancillaries sold with the rate itself.

Array<object>

One extra to add to a room.

Every field is quoted back from something the Inventory surface already handed you — there is nothing to construct and nothing to look up. Where you read them from depends on which kind of extra it is:

PROPERTY-LEVEL (restaurant, spa, meeting room, activity, attraction, place, add-on) inventory_id AncillaryInventory.inventory_id — the restaurant or spa itself channel_inventory_id AncillaryInventory.channel_inventory_identifier offer_id AncillaryOffer.id — the sitting, the 1-hour massage type AncillaryInventory.type

IN-ROOM (INVENTORY_SCOPE_ROOM_TYPE_ANCILLARY) inventory_id RoomRate.room_rate_id — the RATE, not a room type channel_inventory_id leave blank — this is the common case and not an error offer_id RoomRate.room_type_ancillaries[].id type INVENTORY_SCOPE_ROOM_TYPE_ANCILLARY

The in-room case is the one to read twice: its inventory_id is a RATE because the gate for these is keyed by the master rate rather than by an entity. Sending a room type id there matches nothing, and it does so SILENTLY rather than failing.

object
inventoryId

The thing being sold from — see the table above. Never a room type.

string
channelInventoryId

Your channel’s gate onto it. Empty when the item reached you through Wink Network rather than one of your own sales channels, which is the common case and not an error.

string
offerId

The priced row you are buying.

string
type

Which kind of extra this is. INVENTORY_SCOPE_ANNOUNCEMENT is informational only and is rejected here.

string
Allowed values: INVENTORY_SCOPE_UNSPECIFIED INVENTORY_SCOPE_ADD_ON INVENTORY_SCOPE_RESTAURANT INVENTORY_SCOPE_SPA INVENTORY_SCOPE_MEETING_ROOM INVENTORY_SCOPE_ACTIVITY INVENTORY_SCOPE_ATTRACTION INVENTORY_SCOPE_PLACE INVENTORY_SCOPE_ANNOUNCEMENT INVENTORY_SCOPE_ROOM_TYPE_ANCILLARY
quantity

How many. Bounded by the offer’s min_pax and max_pax when it sets them; defaults to 1.

integer format: int32
requestedStart

OPTIONAL, and only meaningful for appointment-style extras — a spa slot, a restaurant sitting, an activity departure — or where the offer’s pricing_type is per hour or per day and the span is what is being priced.

In-room extras and add-ons OMIT them: they are bundled with the stay, and sending times makes the platform treat them as appointments and issue calendar invites nobody asked for.

These are REQUESTED, never confirmed. The property is not committing to them by accepting the booking. ISO-8601 local date-time at the property.

string
requestedEnd
string
specialRequests

Free text for the property.

string
guest

Who is staying in this room.

object
firstName
string
lastName
string
email
string
phone
string
externalBookingCode

The agency’s own references, carried onto the booking and echoed back on reporting. These are the reason an agent booking is its own request type: an agency reconciling Wink against its own system needs its identifiers to survive the round trip.

string
externalTransactionId
string
externalCustomerId
string
externalFees

Fees the agency is adding on top of the rate. Amounts are in MINOR UNITS of display_currency.

Array<object>

A fee the agency is adding on top of the rate.

object
feeId
string
amount

MINOR UNITS of the enclosing request’s display_currency.

string format: int64
type
string
description
string
currencyCode

ISO 4217 currency to quote totals back in, and ISO 639-1 language for anything human-readable. BOTH ARE REQUIRED — the domain rejects a blank either way, so omitting one is INVALID_ARGUMENT.

CAREFUL: this is NOT the same currency_code as AgentBooking’s, despite the shared name. This one is what the agency wants to SEE; that one is what the property SETTLES in, and the two differ whenever a booking is sold in one currency and paid out in another. Every monetary field on the response is in the settlement currency regardless of what is asked for here — see AgentBooking.currency_code.

string
languageCode
string
traceId

The agency’s own correlation id for this attempt, carried into the platform’s logs. Distinct from idempotency_key: this one identifies the REQUEST for tracing, that one identifies the INTENT for deduplication, and reusing a trace id is harmless where reusing a key is not.

string

The RPC completed with gRPC status OK.

Media typeapplication/json

Response for CreateBooking.

object
bookings

One booking per room requested, in the order requested. A multi-room create produces several bookings sharing a group_id — that is what makes them refundable together.

Repeated rather than singular because the domain returns BookingConfirmations, which is a LIST. A single-booking response would have quietly dropped every room after the first.

Array<object>

One booking, as the agency that sold it needs to see it.

A deliberate projection, not a mirror of the internal model — see the note at the top of this file for what is excluded and why.

object
bookingId

Wink’s identifier for this booking.

string
bookingCode

The human-facing reference shown to the traveller, e.g. on the confirmation email.

string
groupId

Present when this booking was made as part of a group; RefundBookings operates on this value.

string
status

Where the booking is in its lifecycle.

LIFECYCLE ONLY — this does NOT report refunds. BOOKING_STATUS_PARTIALLY_REFUNDED and BOOKING_STATUS_FULLY_REFUNDED are retained for bookings written before the lifecycle and refund axes were separated, and are never emitted for anything refunded after that. They were always lossy: a refund settling used to overwrite a cancellation here, so only one of the two facts survived. Read refund (field 14) for whether money came back and how much; a refunded booking that was not cancelled now correctly reports BOOKING_STATUS_PAYMENT_SUCCEEDED here.

string
Allowed values: BOOKING_STATUS_UNSPECIFIED BOOKING_STATUS_INITIALIZED BOOKING_STATUS_TEST_PAYMENT_SUCCEEDED BOOKING_STATUS_PAYMENT_SUCCEEDED BOOKING_STATUS_PAYMENT_FAILED BOOKING_STATUS_FAILED BOOKING_STATUS_CANCELLED
creation

How the booking came to exist.

string
Allowed values: BOOKING_CREATION_TYPE_UNSPECIFIED BOOKING_CREATION_TYPE_NORMAL BOOKING_CREATION_TYPE_TEST BOOKING_CREATION_TYPE_FAILED
createdAt

When the booking was created. ISO-8601 instant, UTC.

string
currencyCode

ISO 4217 currency every monetary field on this message is denominated in. This is the SOURCE currency — the one the property settles in — which is not necessarily the one the traveller was shown. See the note on net_total_sales_amount.

string
property

The property and stay.

object
propertyId
string
name
string
address

A postal address.

object
address1

Street line 1.

string
address2

Street line 2, when the property supplies one.

string
state

State, province or region.

string
postalCode

Postal or ZIP code.

string
city

City name.

string
countryCode

ISO 3166-1 alpha-2 country code, e.g. “TH”.

string
country

Country name in the requested language.

string
fullAddress

The full address pre-formatted for display, newline-free.

string
phone

Direct contact for the property, for when the agency needs to call about a booking.

string
email
string
timeZone

IANA zone, e.g. “Asia/Bangkok”. Check-in and check-out below are LOCAL to the property, so without this an agency cannot tell whether a same-day cancellation is still same-day.

string
stay

The stay itself.

object
checkIn

Local dates at the property, ISO-8601 (YYYY-MM-DD).

string
checkOut
string
nights

Nights between them, precomputed so nobody has to reimplement the date arithmetic.

integer format: int32
roomTypeName

What was booked.

string
ratePlanName
string
quantity

How many rooms of the above, and who is in each.

integer format: int32
adults
integer format: int32
childAges

One entry per child, carrying that child’s age at check-in. Ages drive pricing, so an empty list means no children rather than unknown ages.

Array<integer>
booker

Who booked it, and who is staying. Frequently the same person, and deliberately still two fields: an agency books on behalf of a traveller far more often than not.

object
firstName
string
lastName
string
email
string
phone
string
guest

A person on the booking. Deliberately not the platform’s user model: an agency needs to contact somebody about a stay, not to know their account.

object
firstName
string
lastName
string
email
string
phone
string
earnings

What the agency earns and what the sale was worth.

object
netTotalSalesAmount

Net sale value in currency_code, summed across SALE-type beneficiaries.

The SOURCE currency, meaning the one the property settles in. A booking sold in one currency and settled in another carries an exchange spread, and that spread is intentional platform behaviour — do not reconcile this against what the traveller was charged and expect equality.

string format: int64
netAmount

Net amount attributable to the booking after fees, in currency_code.

string format: int64
commissionPercent

The agency’s commission on this booking, as a percentage at booking time. Percentages rather than amounts because that is how they are agreed and how they survive a partial refund.

string
agencyFeePercent

The agency’s own agency-fee percentage at booking time.

string
commissionPercentAfterRefund

Commission percentage restated after any refund. Equal to commission_percent until something is refunded, and the value to reconcile against once something is.

string
cancellation

Present once cancelled.

object
cancelledAt

ISO-8601 instant, UTC.

string
reason

Free text supplied by whoever cancelled.

string
source

Who cancelled. An agency cancelling on the traveller’s behalf and a property cancelling on the agency are different conversations.

string
Allowed values: CANCELLATION_SOURCE_UNSPECIFIED CANCELLATION_SOURCE_PROPERTY CANCELLATION_SOURCE_GUEST CANCELLATION_SOURCE_ADMIN CANCELLATION_SOURCE_AGENT CANCELLATION_SOURCE_ACQUIRER CANCELLATION_SOURCE_SALES_CHANNEL CANCELLATION_SOURCE_SYSTEM
type

The classification the cancellation was filed under, which is not the same thing as reason: this is the closed list the platform reports on, that is what a human typed.

string
Allowed values: CANCELLATION_TYPE_UNSPECIFIED CANCELLATION_TYPE_DUPLICATE CANCELLATION_TYPE_CANCELLATION CANCELLATION_TYPE_NO_SHOW CANCELLATION_TYPE_CC_INVALID CANCELLATION_TYPE_CC_INSUFFICIENT CANCELLATION_TYPE_DISCRETIONARY
refund

Present once any refund exists, whether from a cancellation or an immediate refund.

object
amount

Amount returned, in currency_code minor units. This is the SOURCE amount — what the property settles — matching every other monetary field on this message.

string format: int64
requestedAt

When the refund was requested, and when it actually settled. processed_at is empty while the status is PENDING or REQUIRES_ACTION, which is the honest answer: it has not been processed yet.

string
processedAt
string
status

Where a refund has got to. Mirrors the domain’s BookingRefundStatus.

  • REFUND_STATUS_PENDING: Requested and settling. NOT a failure, and NOT to be retried — retrying a PENDING refund is how you refund twice.
  • REFUND_STATUS_CANCELED: Spelled with one L, matching the payment processor and the domain enum.
  • REFUND_STATUS_REQUIRES_ACTION: Needs intervention before it can settle; not terminal.
string
Allowed values: REFUND_STATUS_UNSPECIFIED REFUND_STATUS_PENDING REFUND_STATUS_SUCCEEDED REFUND_STATUS_CANCELED REFUND_STATUS_REQUIRES_ACTION REFUND_STATUS_FAILED
reason

Why it was issued, from the closed list, plus whatever free text accompanied it.

string
Allowed values: REFUND_REASON_UNSPECIFIED REFUND_REASON_DUPLICATE REFUND_REASON_FRAUDULENT REFUND_REASON_REQUESTED_BY_CUSTOMER REFUND_REASON_GUEST_COMPLAINT REFUND_REASON_GUEST_BOOKED_INCORRECT_DATE REFUND_REASON_GUEST_SICK REFUND_REASON_OVERBOOKING REFUND_REASON_EMERGENCY REFUND_REASON_FLIGHT_CANCELLED REFUND_REASON_DEATH_IN_FAMILY REFUND_REASON_OTHER REFUND_REASON_EXPIRED_UNCAPTURED_CHARGE REFUND_REASON_AVAILABILITY_CHECK_FAILED
description
string
receiptUrl

Processor-hosted receipt for the traveller, when one exists. Published because an agency fielding “where is my money” needs something to forward; the processor’s refund id and fee do not appear here for the same reason no other payment plumbing does.

string
comment

Free text captured at booking time.

string
specialRequests
string
earlyCheckInCharge

Charges that apply outside the room rate — early check-in, late check-out and anything the property adds. has_additional_charges is the cheap check; additional_charges_summary is the human-readable detail when it is true.

string format: int64
lateCheckOutCharge
string format: int64
hasAdditionalCharges
boolean
additionalChargesSummary
string
paymentType

How the traveller paid.

string
Allowed values: PAYMENT_TYPE_UNSPECIFIED PAYMENT_TYPE_STRIPE PAYMENT_TYPE_AGENT
customizationId

The white-label configuration this booking was made through. The id only — the configuration itself is the agency’s own setup and is managed elsewhere, not published here.

string
statusHistory

Status changes in order, oldest first. Enough to answer “when was this cancelled” without a support ticket.

Array<object>

One lifecycle transition.

object
status

Where a booking is in its lifecycle.

Mirrors the domain enum exactly. PAYMENT_SUCCEEDED is the “booked and paid” state — there is no separate CONFIRMED, and no COMPLETED: a stay that has happened is still PAYMENT_SUCCEEDED.

  • BOOKING_STATUS_INITIALIZED: In flight. Never returned by this API — see GetBooking.
  • BOOKING_STATUS_TEST_PAYMENT_SUCCEEDED: Paid against a test payment method. Returned so an agency can tell its own test bookings apart from real ones rather than discovering the difference in a reconciliation.
  • BOOKING_STATUS_PAYMENT_SUCCEEDED: Booked and paid. The normal terminal state for a stay that goes ahead.
  • BOOKING_STATUS_FAILED: Never returned: failed bookings are excluded from every response on this service.
string
Allowed values: BOOKING_STATUS_UNSPECIFIED BOOKING_STATUS_INITIALIZED BOOKING_STATUS_TEST_PAYMENT_SUCCEEDED BOOKING_STATUS_PAYMENT_SUCCEEDED BOOKING_STATUS_PAYMENT_FAILED BOOKING_STATUS_FAILED BOOKING_STATUS_CANCELLED
changedAt
string
idempotentReplay

True when this response replayed an earlier call rather than creating anything. Nothing was charged.

boolean
Example
{
"bookings": [
{
"status": "BOOKING_STATUS_UNSPECIFIED",
"creation": "BOOKING_CREATION_TYPE_UNSPECIFIED",
"cancellation": {
"source": "CANCELLATION_SOURCE_UNSPECIFIED",
"type": "CANCELLATION_TYPE_UNSPECIFIED"
},
"refund": {
"status": "REFUND_STATUS_UNSPECIFIED",
"reason": "REFUND_REASON_UNSPECIFIED"
},
"paymentType": "PAYMENT_TYPE_UNSPECIFIED",
"statusHistory": [
{
"status": "BOOKING_STATUS_UNSPECIFIED"
}
]
}
]
}