Sign in to see your venue's slug and publishable key in every sample.
Your account has no venue yet, so the samples keep their placeholders. Sign out
Signed in as · . Create a publishable key in the console and reload to see it here. Sign out
Signed in as · . The samples show your venue's publishable key. Sign out
Developer docs
Vouchers
Sell gift vouchers online, look one up by its code without a key, and let guests pay a tab, a deposit or tickets with one.
A venue sells gift vouchers through these routes, and guests spend them at the table or online. Three kinds exist: an amount (spent in parts until the balance is used up), a percentage off one bill, and one menu item. The venue sets what it sells, the price and how long a voucher stays valid; the console issues vouchers by hand on every plan, and selling them online needs Premium.
Two routes below need your key, like every /api/venues/** route: the offers and the checkout. The third, the lookup by code, takes no key: whoever holds a voucher's code holds the voucher. Paying with a voucher needs no route of its own — the tab, deposit and ticket payments take an optional voucher code (Paying with a voucher).
The code is a credential
A voucher code is 20 characters, shown in five groups (K7QM-2XRP-9DTA-HV3N-8W4F). The last character is a check symbol, so a typo is caught before any request; the SDKs check it for you. Anyone who has the code can see the voucher's value and spend it, exactly like a gift card. So:
- Never log it, and never put it in a URL of your own — only the lookup below carries it in its path, and BookDinePlay strips it from its own logs and traces.
- Send it in the request body everywhere else: the tab payment, the deposit payment and the ticket checkout all take it as
voucherCode. - Do not show a code you did not receive from the guest. The code exists only once the voucher is paid; it reaches the buyer or the recipient by e-mail, never through the checkout response.
- Expect one answer for "no". A mistyped, unknown, unpaid or other venue's code is the same
404voucher-not-found, so codes cannot be probed.
The reference (GV-…) a checkout returns is not a credential: it identifies the purchase for support and can be logged.
Endpoints
GET /api/vouchers/{code}
Keyless. code is the voucher code in any form a guest might type or scan: the display form (K7QM-2XRP-9DTA-HV3N-8W4F), the 20 symbols without dashes, or either in lower case. A full guest-page URL is not accepted here: take its last segment. Send it percent-encoded as one path segment, and keep it out of your own logs — it is a bearer credential. Not plan-gated: a voucher issued while the venue's plan allowed it keeps working after a downgrade.
Response 200 OK
| Field | Type | Meaning |
|---|---|---|
venueSlug, venueName |
string | The venue that issued the voucher |
displayCode |
string | The code in its display form |
kind |
string | Amount, Percentage or Item |
status |
string | Active, PartlyUsed, UsedUp, Expired or Voided |
currency |
string | ISO 4217 currency of every money field |
balance |
number or null | What an Amount voucher can still pay; null for the other kinds |
faceValue |
number or null | The value an Amount voucher was issued with |
percentage, maxDiscount |
number or null | A Percentage voucher's discount and its optional cap in money |
itemName |
string or null | An Item voucher's menu item |
expiresOn |
string | The last valid day, venue-local, yyyy-MM-dd |
terms |
string or null | The venue's voucher terms as they were at issue |
recipientName, giftMessage |
string or null | What the buyer wrote for the recipient |
guestUrl |
string | The guest page of this voucher |
qrSvg |
string | An SVG QR code of guestUrl |
The answer never carries the voucher's history or the buyer's name or e-mail address. balance is what can be spent right now: value held by a payment in progress is not counted.
Errors. 404 with the problem type https://bookdineplay.com/docs/api/errors/voucher-not-found for every code that does not resolve to an issued voucher — mistyped, unknown, or a sale that was never paid. An expired or voided voucher is not an error: it answers 200 with that status. The answer is the same for all of them on purpose, so codes cannot be probed. 429 with a Retry-After header when one client asks more than 30 times a minute — this is the one rate-limited route of the API, because there is no key to count against (Rate limits).
curl
curl "https://api.bookdineplay.com/api/vouchers/K7QM-2XRP-9DTA-HV3N-8W4F"JavaScript
const voucher = await client.voucher(code); // sends no key; null when unknown or mistyped (no request at all for a mistyped code)C#
var voucher = await client.GetVoucherAsync(code, cancellationToken); // sends no key; null when unknownGET /api/venues/{venueSlug}/vouchers/offers
What the venue sells online right now. Answers on every plan: when vouchers cannot be bought online, onlineSaleAvailable is false, offers is empty and unavailableReason says why. Titles, terms and the validity sentence follow the request's Accept-Language.
Response 200 OK
| Field | Type | Meaning |
|---|---|---|
venueSlug, venueName, currency |
string | The venue and the currency of every price |
onlineSaleAvailable |
boolean | Whether a checkout can be started |
unavailableReason |
string or null | not-offered, validity-not-set, validity-too-short, payments-off, not-in-plan or payouts-not-connected |
offers |
array | One entry per product: productId, kind, title, price (null when the buyer picks the amount), customAmount, minAmount, maxAmount, itemName |
terms |
string or null | The venue's voucher terms |
validityDescription |
string or null | How long a voucher bought today is valid |
Show a guest the same sentence for every unavailableReason, with at most not-offered getting its own: the other reasons are the venue's setup, not something a guest can act on. The voucher shop does exactly that.
curl
curl "https://api.bookdineplay.com/api/venues/your-venue/vouchers/offers" \
-H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
-H "Origin: https://www.your-venue.example"JavaScript
const offers = await client.voucherOffers();
if (!offers.onlineSaleAvailable) showNotAvailable(offers.unavailableReason);C#
var offers = await client.GetVoucherOffersAsync("your-venue", cancellationToken); // null when the venue is unknownPOST /api/venues/{venueSlug}/vouchers/checkout
Buy one voucher: opens a Stripe Checkout session on the venue's own connected Stripe account and returns the URL to send the buyer to. Needs the Premium plan, online payments switched on and a payout account that can sell. The voucher's code does not exist yet: it is minted once the payment is confirmed and reaches the buyer, or the recipient, by e-mail. Send an Idempotency-Key header so a retried request replays the same checkout instead of opening a second one; see Idempotency problems. The .NET SDK and the voucher shop send one for you; with client.checkoutVoucher pass { idempotencyKey } yourself — without it the JavaScript client sends none and a retry can open a second checkout.
Request body (Content-Type: application/json)
| Field | Required | Meaning |
|---|---|---|
productId |
yes | An offer's productId |
amount |
custom amounts | The value to buy, within the offer's minAmount and maxAmount |
buyerName, buyerEmail |
yes | Who pays |
recipientName, recipientEmail, giftMessage |
no | Who the voucher is for; the message is at most 500 characters |
deliverOn |
no | yyyy-MM-dd, venue-local, today up to a year ahead: the day the recipient's e-mail goes out. Needs recipientEmail |
language |
no | en or de; defaults to the request's Accept-Language, then the venue's own language |
successUrl, cancelUrl |
no | Where Checkout sends the buyer. Must be https and on this API key's origin allowlist or the platform's own guest app; omitted uses the built-in thank-you page |
Response 201 Created: reference (GV-…, display only), checkoutUrl (send the buyer here) and expiresAt (when the Checkout session stops accepting payment).
A paid voucher is valid for at least a year from the day it is issued. A sale whose payment never arrives creates no voucher; it is deleted 30 days after its checkout expired.
Errors
| Status | When |
|---|---|
400 |
A field fails validation (errors names it), a return URL is refused, or the venue's validity rule gives a paid voucher less than a year (voucher-validity-too-short) |
400 |
errors["Idempotency-Key"]: the key is longer than 200 characters |
403 |
feature-not-in-plan: online sale needs Premium |
404 |
No venue with that slug |
409 |
voucher-sale-not-available (online sale off, product not sold online, payments off, or no payout account that can sell), or voucher-validity-not-set |
409 |
idempotency-key-reused or checkout-in-progress |
curl
curl -X POST "https://api.bookdineplay.com/api/venues/your-venue/vouchers/checkout" \
-H "X-BookDinePlay-Key: bdp_pk_your_publishable_key" \
-H "Origin: https://www.your-venue.example" \
-H "Idempotency-Key: 4f9c2a1e8b7d4c3f9e0a1b2c3d4e5f60" \
-H "Content-Type: application/json" \
-d '{ "productId": "3f2a1b4c5d6e4f708192a3b4c5d6e7f8", "buyerName": "Ben Buyer", "buyerEmail": "ben@example.com", "recipientName": "Ada" }'JavaScript
const checkout = await client.checkoutVoucher(
{ productId, buyerName: 'Ben Buyer', buyerEmail: 'ben@example.com', recipientName: 'Ada' },
{ idempotencyKey: attemptKey } // one crypto.randomUUID() per attempt, reused when you retry it
);
window.location.assign(checkout.checkoutUrl);C#
var checkout = await client.StartVoucherCheckoutAsync("your-venue",
new StartVoucherCheckoutRequest { ProductId = productId, BuyerName = "Ben Buyer", BuyerEmail = "ben@example.com", RecipientName = "Ada" },
idempotencyKey: attemptKey, cancellationToken);Paying with a voucher
A guest spends a voucher online by adding its code to a payment they are making anyway. The three payments take an optional voucherCode in their body; leaving it out changes nothing.
| Payment | Field | Kinds accepted | What comes back |
|---|---|---|---|
A table tab, /api/qr/{token}/payment |
voucherCode |
Amount, Percentage |
voucherAmount, paidInFull |
A reservation deposit, /api/venues/{venueSlug}/payment-intents |
voucherCode |
Amount |
voucherAmount, cardAmount |
Event tickets, /api/venues/{venueSlug}/events/{eventSlug}/tickets/checkout |
voucherCode |
Amount |
voucherAmount, amountDue |
The same rules hold for all three:
- The voucher pays first, the card pays the rest. The value is held while the guest is on Stripe's page, so it cannot be spent twice; if the guest gives up, the hold lapses on its own.
- The card pays at least Stripe's minimum — €0.50 in EUR; the minimum is Stripe's own per currency. Stripe refuses smaller card payments. When the rest would be between €0.01 and €0.49, the voucher pays a little less so the card pays exactly €0.50, and the voucher keeps the difference: a €50 voucher on a €50.30 tab pays €49.80. When the whole amount is €0.50 or less and the voucher does not cover it, the voucher cannot be used for it:
409withreasonbelow-card-minimum, on all three payments. - A voucher that covers everything opens no payment page. The payment completes at once, and
checkoutUrlis the success page — so a client that simply followscheckoutUrlworks either way. CheckpaidInFull,cardAmountoramountDueto skip the redirect. - A refund gives the voucher part back to the voucher. Only the card part goes back to the card. An expired voucher is extended by 30 days from the refund, so the guest can still use what came back. A voucher the venue has voided stays void: the refund is recorded on it, but the venue settles that value with the guest directly.
- A percentage voucher is a discount on the whole bill, applied before any amount voucher and capped at what is due. It is used once.
- No plan gate. Paying with a voucher and refunding it work on every plan. A payment's own conditions still apply — a tab needs ordering, a deposit needs deposits, tickets need ticket sales — and the card part needs online payments, as without a voucher.
- The same problems everywhere. A code is refused with one of the gift voucher problems, whichever payment it was given to.
Item vouchers are redeemed at the table by staff, who pick the line on the bill they pay for.
Next steps
- JavaScript vouchers — the voucher shop, the balance check and the voucher fields in the widgets.
- .NET SDK — the typed voucher calls.
- Errors — every voucher problem and what to show.