Launching soon.Get early access
Veridien Docs
API Reference

Offers & Promo Codes

The catalog endpoints answer "what can I book in each room type". These answer "what offers apply to this stay", which is what a booking engine needs to merchandise: public names, descriptions, images, what a package includes, and the price for the party in question.

A rate plan, a package and a promotion are all the same kind of object. They differ by kind and by how their price derives from a parent plan, which is why a package distributes and prices exactly like any other rate.


GET /rate-plans

Scope: availability:read

Every offer the property has published for the booking engine, priced for the requested stay and party.

ParameterRequiredNotes
check_inyesYYYY-MM-DD.
check_outyesYYYY-MM-DD, exclusive.
adultsnoDefaults to 2.
childrennoDefaults to 0.
room_type_idnoRestrict to one room type.
curl "$VRDN_BASE/rate-plans?check_in=2026-08-01&check_out=2026-08-04&adults=2" \
  -H "Authorization: Bearer $VRDN_KEY"
{
  "check_in": "2026-08-01",
  "check_out": "2026-08-04",
  "data": [
    {
      "rate_plan_id": "5c1e8f4a-2b7d-4a9c-8e6f-1d3b9a5c7e02",
      "room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
      "room_type_name": "Ocean Suite",
      "public_name": "Half Board Escape",
      "kind": "package",
      "description": "Breakfast and dinner included daily.",
      "image_url": "https://cdn.example.com/half-board.jpg",
      "inclusions": [
        { "label": "Breakfast", "frequency": "perGuestPerNight", "included_quantity": 1 },
        { "label": "Dinner", "frequency": "perGuestPerNight", "included_quantity": 1 }
      ],
      "min_los": 2,
      "max_los": null,
      "currency": "USD",
      "total": "1341.60",
      "room_subtotal": "1020.00",
      "taxes_total": "321.60",
      "base_occupancy": 2,
      "nightly_rates": [{ "date": "2026-08-01", "day_of_week": 6, "rate": "340.00", "source": "interval" }]
    }
  ]
}

An offer only appears when all of the following hold. Anything that fails is simply absent from the response; there is no partial or "unavailable" entry.

  • The plan is active and the property has enabled it for the booking engine.
  • The stay falls inside the offer's booking window and stay window.
  • The stay length satisfies the offer's minimum and maximum, and every night falls on an allowed day of the week.
  • The room type can hold the party.
  • The plan is not gated behind a promotion code.

Offers behind a promotion code never appear here. They are reachable only through a successful code validation, described below.

inclusions lists what a package bundles, with the frequency each item recurs at: perStay, perNight, perGuest or perGuestPerNight. They are descriptive. The price a package sells for is the single total; the property allocates that total across the inclusions internally so each component meets the right tax treatment, but the guest pays one number.


POST /promo-codes/validate

Scope: availability:read

FieldRequiredNotes
codeyesCase and surrounding whitespace are ignored.
check_inyesYYYY-MM-DD.
check_outyesYYYY-MM-DD, exclusive.
room_type_idnoIf supplied and the code's offer is on a different room type, the response is the uniform valid: false.
adultsnoDefaults to 2.
childrennoDefaults to 0.
child_agesnoDefaults to []. When non-empty, its length is the child count and overrides children.

Party size is checked against the room type's maximum occupancy, so an understated party can validate here and then fail at POST /holds.

A code unlocks a rate plan. It does not apply a discount of its own: the saving is already built into the plan the code reveals, so the price you get back is the price, with nothing further to compute.

curl -X POST "$VRDN_BASE/promo-codes/validate" \
  -H "Authorization: Bearer $VRDN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "SUMMER26",
    "check_in": "2026-08-01",
    "check_out": "2026-08-04",
    "adults": 2
  }'

A code that applies returns the offer it unlocks, in the same shape as a /rate-plans entry.

{
  "valid": true,
  "code": "SUMMER26",
  "rate_plan": {
    "rate_plan_id": "e2a9d7c4-3f6b-4c1e-9a8d-7b5f0c2e4a19",
    "room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
    "room_type_name": "Ocean Suite",
    "public_name": "Early Bird",
    "kind": "promotion",
    "description": "Book 60 days ahead and save.",
    "image_url": "https://cdn.example.com/early-bird.jpg",
    "inclusions": [],
    "min_los": 3,
    "max_los": null,
    "currency": "USD",
    "total": "1140.36",
    "room_subtotal": "867.00",
    "taxes_total": "273.36",
    "nightly_rates": [{ "date": "2026-08-01", "day_of_week": 6, "rate": "289.00", "source": "interval" }]
  }
}

A code that does not apply returns one response, whatever the cause.

{
  "valid": false,
  "reason": "That code is not valid for these dates."
}

The failure response is deliberately identical for a code that does not exist, one that has expired, one that has been fully redeemed, and one whose offer does not cover the requested dates. Distinguishing them would let a caller discover which codes are real, so the specific cause is never disclosed.

Code matching ignores case and surrounding whitespace, so summer26 and SUMMER26 are the same code.

Code validation carries a tighter limit than the rest of the API, on top of the per-key and per-IP limits described in Authentication. Repeated failures return 429. Validate a code when the guest submits it, rather than on every keystroke.


Pass the code to the booking call. The server resolves it again from scratch and re-prices the stay.

curl -X POST "$VRDN_BASE/holds" \
  -H "Authorization: Bearer $VRDN_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "guest_id": "c4e7b9d2-6a1f-4e3c-9b8d-2f5a7c0e1d46",
    "room_type_id": "9b2f6c3e-8d1a-4f5b-a7c9-4e0d2b8f1a63",
    "rate_plan_id": "e2a9d7c4-3f6b-4c1e-9a8d-7b5f0c2e4a19",
    "check_in": "2026-08-01",
    "check_out": "2026-08-04",
    "adults": 2,
    "promo_code": "SUMMER26"
  }'

Two consequences worth designing for:

  • A validation response is advisory. It reflects the moment it was issued. If the code expires, is deactivated, or reaches its redemption limit before the guest completes payment, the booking fails rather than honouring the earlier quote. Handle that failure in your checkout flow.
  • Never send a price. The server prices every booking itself and ignores any amount a client supplies. A quote is for display.

Redemption is counted when a reservation is confirmed, not when a code is validated, and it is released if the reservation is cancelled. A code limited to a fixed number of redemptions cannot be oversold by simultaneous bookings.