Skip to main content

Using a Coupon at Checkout

The checkout payload does not change when a coupon is used — you only add two keys to it. This is the same for every product that goes through the unified booking endpoints, not hotels only.

The Keys

KeyTypeWhereRequiredDescription
coupon_codeStringTop levelOptionalThe coupon the user wants to use. Omit it to book at full price.
user_idIntegerTop levelConditionalThe signed-in user's ID. Required for any coupon the user claimed.
{
"user_id": 3312,
"coupon_code": "SONGKRAN10"
}

Top level, not inside products. Putting coupon_code or user_id inside a products item has no effect — the coupon is silently ignored and the customer pays full price.

Where to Send Them

Add the same two keys to both calls of the booking flow. Everything else stays exactly as documented for the product — for hotels, Hotel Checkout and Hotel Checkout Confirm.

StepEndpointSend the keys?
1. CheckoutPOST /api/v1/unified-booking/checkoutYes
2. ConfirmPOST /api/v1/unified-booking/checkout/confirmYes — the same code again
3. PaymentPayment Request APINo — nothing coupon-specific

Which Products Support It

The two keys are identical for every product type. Only product_type inside products changes.

product_typeProductCoupon support
pd11HotelFull
pd4More ExperiencesFull
pd6Airport TransferFull
pd7Airport VIP AssistanceFull
pd8TourFull
pd9FlightLimited — see below

What Comes Back

The data array is unchanged. The discount appears in the checkout response's meta:

meta fieldMeaning
coupon_codeEcho of the code that was applied.
discount_amountThe saving, capped at sub_total.
sub_totalCart total before the discount.
grand_totalsub_total − discount_amount. The bank fee is calculated on this.
bank_fee_priceFee for the payment method, charged on the discounted amount.
payablegrand_total + bank_fee_price — what the customer pays.
{
"meta": {
"currency_code": "THB",
"sub_total": 5000,
"coupon_code": "SONGKRAN10",
"discount_amount": 500,
"grand_total": 4500,
"bank_fee_price": 135,
"payable": 4635
}
}

The discount is applied before the bank fee, so a coupon lowers the fee too. On confirm, the returned grand_total is already the final payable amount — use it for the Payment Request API.

Coupon Status Along the Way

StageCoupon status
CheckoutUnchanged — validated and priced only, nothing is written.
Confirmreserved, held against the new booking.
Payment successredeemed. Final.
Payment failed / cancelled, or unpaid for 30 minutesReleased back to claimed.

When the Coupon Is Rejected

An unusable coupon rejects the whole checkout — you do not get a booking at full price. The response is the standard envelope with HTTP 200:

{
"result": 0,
"message": "Grand total does not meet the minimum purchase amount."
}
MessageWhat to do
Coupon code not found.The code does not exist, or is not usable by this user or partner.
This coupon does not belong to the requested user.Send the owner's user_id, or drop the code.
This coupon is not validWrong product type, or user_id missing on a members-only coupon.
The coupon has expired.Ask the user to pick another.
The claim duration for this coupon has expired.Claimed but not used in time.
Grand total does not meet the minimum purchase amount.Cart is below the coupon's minimum spend.
You already have a pending booking with this coupon.Finish or cancel that booking first.
The usage limit per user for this campaign has been reached.The user already redeemed their allowance.
The campaign usage limit has been reached.The coupon's global limit is exhausted.

To let the booking go through anyway, retry the same request without coupon_code.

Notes

  • One coupon per booking.
  • For hotels the property is resolved from hotel_code, so hotel- and destination-restricted coupons need no extra fields.
  • Amounts follow your X-Currency header. Read meta.discount_amount and meta.payable from the response instead of calculating the discount yourself — the coupon is priced against the live rate.
  • Call Applicable Coupons before checkout to offer only the coupons that fit the cart, so a rejection here becomes rare.