Vouchers
Operator endpoints for gift vouchers: configure what guests can buy, issue
complimentary or paid vouchers yourself, and look up and redeem codes at the
till. All routes are scoped to a venue you can access; issuing and redeeming
require the operator role (client). Guest voucher
purchase runs through the widget endpoints.
Endpoints
Section titled “Endpoints”| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/my-venues/:venueId/voucher-settings |
Read voucher configuration | operator |
| PUT | /api/my-venues/:venueId/voucher-settings |
Update voucher configuration | operator |
| GET | /api/my-venues/:venueId/vouchers |
List issued vouchers | operator |
| POST | /api/my-venues/:venueId/voucher-intent |
Start payment for a paid voucher | operator |
| POST | /api/my-venues/:venueId/vouchers |
Issue a voucher (comp or paid) | operator |
| GET | /api/my-venues/:venueId/vouchers/lookup |
Preview a voucher by code | operator |
| POST | /api/my-venues/:venueId/vouchers/redeem |
Redeem a voucher code | operator |
| GET | /api/my-venues/:venueId/vouchers/:id/payment |
A voucher’s payment and refund/cancel eligibility | operator |
| GET | /api/my-venues/:venueId/vouchers/:id/history |
A voucher’s lifecycle timeline | operator |
| POST | /api/my-venues/:venueId/vouchers/:id/refund |
Refund a paid voucher’s payment and void it | operator |
| POST | /api/my-venues/:venueId/vouchers/:id/cancel |
Cancel a voucher without a refund | operator |
| POST | /api/my-venues/:venueId/vouchers/:id/resend |
Re-send a voucher to its recipient or purchaser | operator |
| POST | /api/my-venues/:venueId/vouchers/:id/resend-receipt |
Re-send the purchase receipt to the purchaser | operator |
| POST | /api/my-venues/:venueId/vouchers/:id/reassign-purchaser |
Change who bought a voucher | operator |
Configure vouchers
Section titled “Configure vouchers”GET returns the current settings merged with defaults; PUT upserts them.
Amounts are in pence, fixed_amounts is an array of preset values (or null
for none), and min_amount_pence must not exceed max_amount_pence.
curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/voucher-settings" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "expiration_months": 12, "min_amount_pence": 2500, "max_amount_pence": 50000, "fixed_amounts": [2500, 5000, 10000], "allow_custom": true, "min_qty": 1, "max_qty": 10, "terms": "Valid for 12 months. Not redeemable for cash." }'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/voucher-settings", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ enabled: true, expiration_months: 12, min_amount_pence: 2500, max_amount_pence: 50000, fixed_amounts: [2500, 5000, 10000], allow_custom: true, min_qty: 1, max_qty: 10, terms: "Valid for 12 months. Not redeemable for cash.", }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/voucher-settings");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "enabled" => true, "expiration_months" => 12, "min_amount_pence" => 2500, "max_amount_pence" => 50000, "fixed_amounts" => [2500, 5000, 10000], "allow_custom" => true, "min_qty" => 1, "max_qty" => 10, "terms" => "Valid for 12 months. Not redeemable for cash.", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true }Reading the settings back:
curl "https://thesidedoor.co/api/my-venues/VENUE_ID/voucher-settings" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/voucher-settings", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/voucher-settings");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "data": { "venue_id": "ven_123", "enabled": 1, "expiration_months": 12, "min_amount_pence": 2500, "max_amount_pence": 50000, "fixed_amounts": [2500, 5000, 10000], "allow_custom": 1, "min_qty": 1, "max_qty": 10, "terms": "Valid for 12 months. Not redeemable for cash." }}List issued vouchers
Section titled “List issued vouchers”Returns vouchers for the venue and every other venue in its group, newest first. The result is paginated (default 25 per page) and filterable:
?status=- only vouchers in that status (e.g.active,redeemed,expired).?q=- free-text search matching a voucher code or the recipient’s or purchaser’s name or email.?venue_id=- narrow to a single venue in the group.?page=/?limit=(or?offset=) - page through the results. To read the full set, keep paging whilehas_moreistrue.
The response wraps the page in data alongside total, limit, offset and
has_more. It also includes stats (a per-status count and balance for the
whole group, not just this page) and venues (the distinct venues in the group
that have vouchers, handy for building a venue filter).
curl "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers?status=active&page=1&limit=25" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers?status=active&page=1&limit=25", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers?status=active&page=1&limit=25");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "data": [ { "id": "vch_1", "code": "A1B2-C3D4-E5F6", "amount_pence": 5000, "status": "active", "expires_at": "2027-07-03", "venue_id": "ven_123", "venue_name": "The Attic" } ], "total": 1, "limit": 25, "offset": 0, "has_more": false, "stats": { "active": { "n": 1, "bal": 5000 }, "redeemed": { "n": 0, "bal": 0 }, "expired": { "n": 0, "bal": 0 } }, "venues": [{ "id": "ven_123", "name": "The Attic" }]}Issue a complimentary voucher
Section titled “Issue a complimentary voucher”Issue a comp voucher directly - no payment. The comp/paid selector is the
mode field; anything other than "paid" is treated as "comp".
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "amount_pence": 5000, "mode": "comp", "recipient_name": "Sam Lee", "recipient_email": "sam@example.com", "message": "Thanks for being a great guest!" }'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ amount_pence: 5000, mode: "comp", recipient_name: "Sam Lee", recipient_email: "sam@example.com", message: "Thanks for being a great guest!", }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "amount_pence" => 5000, "mode" => "comp", "recipient_name" => "Sam Lee", "recipient_email" => "sam@example.com", "message" => "Thanks for being a great guest!", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "vch_2", "code": "G7H8-J9K0-L1M2", "emailed": true }Issue a paid voucher
Section titled “Issue a paid voucher”For a paid voucher, first start payment with
POST /api/my-venues/:venueId/voucher-intent (amount_pence, min 50). Sidedoor
handles the secure card step; once payment succeeds, issue the voucher with
mode: "paid" and the returned payment_intent_id.
# 1. Create the PaymentIntentcurl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/voucher-intent" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"amount_pence":5000}'
# 2. After payment succeeds, issue the vouchercurl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"amount_pence":5000,"mode":"paid","payment_intent_id":"pi_...","recipient_email":"sam@example.com"}'const intent = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/voucher-intent", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ amount_pence: 5000 }), },).then((r) => r.json());
// ...once Sidedoor's secure card step completes the payment, then:const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ amount_pence: 5000, mode: "paid", payment_intent_id: intent.intent_id, recipient_email: "sam@example.com", }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/voucher-intent");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["amount_pence" => 5000]),]);$intent = json_decode(curl_exec($ch), true);// ...once the card step completes the payment, then POST /vouchers with mode "paid".{ "intent_id": "pi_...", "amount_pence": 5000 }Look up a voucher
Section titled “Look up a voucher”Preview a code before redeeming - check the balance, status and whether it is
redeemable at this venue. Codes are accepted grouped (XXXX-XXXX-XXXX) or as a
plain 12-character string. This endpoint is rate-limited (429 + Retry-After).
curl "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/lookup?code=A1B2-C3D4-E5F6" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/lookup?code=A1B2-C3D4-E5F6", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/lookup?code=A1B2-C3D4-E5F6");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "found": true, "voucher": { "id": "vch_1", "code": "A1B2-C3D4-E5F6", "amount_pence": 5000, "status": "active", "expires_at": "2027-07-03", "venue_name": "The Attic" }, "redeemable": true, "reason": "ok"}Redeem a voucher
Section titled “Redeem a voucher”Mark a voucher as redeemed. Send the code and an optional note. The response
carries a reason: ok on success, or one of not_found, wrong_venue,
refunded, cancelled, already_redeemed or expired on failure (with
success: false).
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/redeem" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"code":"A1B2-C3D4-E5F6","note":"Redeemed against table 12"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/redeem", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ code: "A1B2-C3D4-E5F6", note: "Redeemed against table 12", }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/redeem");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "code" => "A1B2-C3D4-E5F6", "note" => "Redeemed against table 12", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "reason": "ok", "voucher": { "id": "vch_1", "code": "A1B2-C3D4-E5F6", "amount_pence": 5000, "status": "redeemed" }}Manage one voucher
Section titled “Manage one voucher”These actions work on a single issued voucher, by its id (not its code).
GET .../vouchers/:id/payment reads what was paid, the payment policy locked
in at purchase (advisory only - staff can override it), and whether a refund
or cancellation is currently allowed.
curl "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/payment" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/payment", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/payment");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "payment": { "amount_pence": 5000, "amount_paid_pence": 5000, "paid": true, "status": "active", "stripe_ref": "pi_..." }, "policy": { "refund_type": "full", "cancel_type": "anytime" }, "can_refund": true, "can_cancel": true, "refund_amount_pence": 5000}GET .../vouchers/:id/history returns its lifecycle as an entries array
(created, redeemed, refunded, cancelled or expired, each with an at
timestamp and, where relevant, an actor).
Refund or cancel
Section titled “Refund or cancel”POST .../vouchers/:id/refund refunds the full amount paid via Stripe and
voids the voucher so it can no longer be redeemed - only an active, paid
voucher can be refunded. POST .../vouchers/:id/cancel voids a voucher
(paid or complimentary) without a refund.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/refund" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/refund", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/refund");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "success": true, "status": "refunded", "refunded_pence": 5000 }cancel returns { "success": true, "status": "cancelled" } the same way.
Resend
Section titled “Resend”POST .../vouchers/:id/resend re-sends the voucher by email, to the
recipient (default) or the purchaser - handy when the original send
bounced or went to the wrong inbox. POST .../vouchers/:id/resend-receipt
re-sends the purchase receipt to the purchaser.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/resend" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target":"recipient"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/resend", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ target: "recipient" }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/resend");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["target" => "recipient"]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "emailed": true, "channels": ["email"] }resend-receipt takes no body and returns { "success": true }.
Reassign the purchaser
Section titled “Reassign the purchaser”POST .../vouchers/:id/reassign-purchaser changes who bought a voucher -
pass a user_id for one of your own guests, or guest_name for a walk-in
with no account. The new purchaser must belong to your own roster; a bare
call with neither field is rejected.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/reassign-purchaser" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"user_id":"usr_def"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/reassign-purchaser", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "usr_def" }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/vouchers/vch_1/reassign-purchaser");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["user_id" => "usr_def"]),]);$data = json_decode(curl_exec($ch), true);{ "success": true }