Branding & widgets
Branding controls the look of a venue’s widgets and branded emails; widget
settings enable the embeddable flows and mint the public_id used in the embed
URL. Every endpoint is under /api/my-venues/:venueId/… and is operator
(client role, venue-scoped) - send an Authorization: Bearer token from the
Authentication flow. Image uploads are base64 data
URLs in a data field (multipart is rejected at the edge). For the operator-side
walkthrough see Widgets; to embed the resulting snippet see
the embed reference.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/my-venues/:venueId/branding |
Get branding | operator |
| PUT | /api/my-venues/:venueId/branding |
Update branding colours/font | operator |
| POST | /api/my-venues/:venueId/branding-logo |
Upload brand logo | operator |
| GET | /api/my-venues/:venueId/widget-settings |
List widget settings + theme | operator |
| PUT | /api/my-venues/:venueId/widget-settings/:type |
Enable/disable a widget | operator |
| PUT | /api/my-venues/:venueId/widget-theme |
Update widget presentation | operator |
| POST | /api/my-venues/:venueId/widget-logo |
Upload a widget logo | operator |
| GET | /api/my-venues/:venueId/widget-templates |
Design gallery + the venue’s active design | operator |
| PUT | /api/my-venues/:venueId/widget-theme/design |
Set the active design (classic, a template, or a saved builder design) | operator |
| GET | /api/my-venues/:venueId/widget-theme/builder |
Read the stored builder design, for editing | operator |
| POST | /api/my-venues/:venueId/widget-theme/image |
Upload an image for a builder block | operator |
| GET | /api/my-venues/:venueId/email-footers |
List email footers | operator |
| POST | /api/my-venues/:venueId/email-footers |
Create an email footer | operator |
| PUT | /api/my-venues/:venueId/email-footers/:id |
Update an email footer | operator |
| DELETE | /api/my-venues/:venueId/email-footers/:id |
Delete an email footer | operator |
Branding
Section titled “Branding”GET /api/my-venues/:venueId/branding returns the brand record (or defaults).
PUT updates the colours and font: theme (preset name, e.g. classic),
accent_color, bg_color, text_color (hex) and font_key, one of serif,
sans, mono, elegant, rounded, slab, editorial, cormorant or oswald
(defaults to sans). The same PUT also sets the structural look (see
Structural look below). The logo is set separately via
branding-logo, not here.
Set branding colours
Section titled “Set branding colours”curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/branding" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"theme":"midnight","accent_color":"#C0A060","bg_color":"#0A0806","text_color":"#F5F1E8","font_key":"serif"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/branding", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ theme: "midnight", accent_color: "#C0A060", bg_color: "#0A0806", text_color: "#F5F1E8", font_key: "serif", }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/branding");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "theme" => "midnight", "accent_color" => "#C0A060", "bg_color" => "#0A0806", "text_color" => "#F5F1E8", "font_key" => "serif", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true }Structural look
Section titled “Structural look”The same PUT /api/my-venues/:venueId/branding also accepts a structural-look
layer that shapes the widgets beyond colour and font. Each field is validated
against a fixed set of values; anything else falls back to the default. All five
are also returned by GET /branding.
| Field | Allowed values | Default |
|---|---|---|
style_preset |
classic, minimal, luxury, bold, playful, modern |
classic |
button_shape |
square, rounded, pill |
rounded |
radius_scale |
sharp, soft, medium, round |
medium |
shadow_style |
none, soft, strong |
soft |
spacing_scale |
compact, normal, roomy |
normal |
curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/branding" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"style_preset":"luxury","button_shape":"pill","radius_scale":"round","shadow_style":"strong","spacing_scale":"roomy"}'Upload a brand logo
Section titled “Upload a brand logo”POST /api/my-venues/:venueId/branding-logo stores a logo (max 2 MB) on the brand
record and returns the stored url. To set the venue’s own image (the hero photo
used across listings and the widget), use the venue logo uploader documented under
Upload a logo in the venues
reference, not this endpoint.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/branding-logo" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"data":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/branding-logo", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ data: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...", }), },);const { url } = await res.json();$dataUrl = "data:image/png;base64," . base64_encode(file_get_contents("logo.png"));$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/branding-logo");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["data" => $dataUrl]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "url": "/assets/branding/VENUE_ID.png" }Widget settings
Section titled “Widget settings”GET /api/my-venues/:venueId/widget-settings returns per-type settings keyed by
widget type (bookings, guestlist, events, vouchers, combo, waitlist,
experiences), a global theme object and vouchers_enabled. It lazily mints a
stable public_id for every type, so each always has a shareable id.
curl "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-settings" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-settings", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const { data, theme, vouchers_enabled } = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/widget-settings");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "data": { "bookings": { "widget_type": "bookings", "enabled": 0, "public_id": "3f2a9c7e-..." }, "guestlist": { "widget_type": "guestlist", "enabled": 0, "public_id": "5c8f..." }, "events": { "widget_type": "events", "enabled": 0, "public_id": "8b1d..." }, "vouchers": { "widget_type": "vouchers", "enabled": 0, "public_id": "a4c9..." }, "combo": { "widget_type": "combo", "enabled": 0, "public_id": "d7e2..." }, "waitlist": { "widget_type": "waitlist", "enabled": 0, "public_id": "b6a1..." }, "experiences": { "widget_type": "experiences", "enabled": 0, "public_id": "e9f4..." } }, "theme": { "accent_color": "#C0A060", "width": "medium", "theme_mode": "detailed" }, "vouchers_enabled": false}Enable a widget
Section titled “Enable a widget”PUT /api/my-venues/:venueId/widget-settings/:type where :type is one of
bookings, guestlist, events, vouchers, combo, waitlist or
experiences. Send { enabled: true }; you may also
pass width (narrow/medium/wide) and custom_css, which write through to
the global theme. The response returns the stable public_id - embed the widget
at https://thesidedoor.co/widget/<public_id>. Enabling vouchers requires gift
vouchers to be switched on first, otherwise you get a 400.
curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-settings/bookings" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"enabled":true,"width":"medium"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-settings/bookings", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ enabled: true, width: "medium" }), },);const { public_id } = await res.json();$ch = curl_init( "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-settings/bookings");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, "width" => "medium"]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "public_id": "3f2a9c7e-1b8d-4c2a-9f11-6a0e5d3b2c47" }Update the widget theme
Section titled “Update the widget theme”PUT /api/my-venues/:venueId/widget-theme sets global presentation shared by all
widgets: width (narrow/medium/wide), theme_mode (minimal/detailed),
custom_css (max 20000 chars) and the show_map, show_about, show_tandc,
show_info_card, show_contact, show_socials toggles (show_socials shows the
footer social-icon row). It deliberately leaves colours, logo and font untouched
(those are on branding).
curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"width":"wide","theme_mode":"detailed","show_map":true,"show_about":true,"show_tandc":true,"show_info_card":true,"show_contact":false,"show_socials":true}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ width: "wide", theme_mode: "detailed", show_map: true, show_about: true, show_tandc: true, show_info_card: true, show_contact: false, show_socials: true, }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "width" => "wide", "theme_mode" => "detailed", "show_map" => true, "show_about" => true, "show_tandc" => true, "show_info_card" => true, "show_contact" => false, "show_socials" => true, ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true }The design gallery
Section titled “The design gallery”GET /api/my-venues/:venueId/widget-templates returns the code-defined design
template gallery (id, name, blurb for each) plus the venue’s current
active selection (source: none, template or builder, with
template_id when source is template) and whether a builder design has
ever been saved (has_builder).
curl "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-templates" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-templates", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/widget-templates");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "templates": [{ "id": "minimal-light", "name": "Minimal Light", "blurb": "Clean and airy" }], "has_builder": false, "active": { "source": "template", "template_id": "minimal-light" }, "content": {}}Set the active design
Section titled “Set the active design”PUT /api/my-venues/:venueId/widget-theme/design chooses what a venue’s
widgets actually render. Set source to:
none- the classic shell (colours and font from Branding).template- one of the gallery templates fromGET /widget-templates; pass itstemplate_id.builder- a block layout you compose yourself. Pass an orderedblocksarray (and optionalpage); Sidedoor renders it server-side and stores the result. Sendactivate: falseto save the blocks as a draft without making them live yet, or omitblocksentirely to activate the builder design you already have saved.
curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme/design" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"source":"template","template_id":"minimal-light"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme/design", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ source: "template", template_id: "minimal-light" }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme/design");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["source" => "template", "template_id" => "minimal-light"]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "source": "template", "template_id": "minimal-light" }Read the builder design
Section titled “Read the builder design”GET /api/my-venues/:venueId/widget-theme/builder returns the venue’s saved
builder document for re-editing: design_json (the block list), and the
layout_html / layout_css last rendered from it. All three are null when
nothing has been saved yet.
curl "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme/builder" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme/builder", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme/builder");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "design_json": "{\"blocks\":[...]}", "layout_html": "<div>...</div>", "layout_css": ".sd-widget { ... }" }Upload a builder image
Section titled “Upload a builder image”POST /api/my-venues/:venueId/widget-theme/image uploads an image for a
builder block (JPG, PNG, GIF or WebP, under 3 MB) and returns a hosted url
to reference from the block.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme/image" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"data":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme/image", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ data: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." }), },);const { url } = await res.json();$dataUrl = "data:image/png;base64," . base64_encode(file_get_contents("block.png"));$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/widget-theme/image");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["data" => $dataUrl]),]);$data = json_decode(curl_exec($ch), true);{ "ok": true, "url": "/assets/widget-images/VENUE_ID/a1b2c3d4e5f6a7b8.png" }Upload a widget logo
Section titled “Upload a widget logo”POST /api/my-venues/:venueId/widget-logo stores a per-type widget logo (max
2 MB); pass data (base64 data URL) and type (defaults to bookings). It
returns the url only - save it where you need it.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-logo" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"data":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...","type":"bookings"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/widget-logo", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ data: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...", type: "bookings", }), },);const { url } = await res.json();$dataUrl = "data:image/png;base64," . base64_encode(file_get_contents("wlogo.png"));$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/widget-logo");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["data" => $dataUrl, "type" => "bookings"]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "url": "/assets/widget-logos/VENUE_ID/bookings.png" }Email footers
Section titled “Email footers”A footer is a reusable block of text - booking terms, deposit terms, and so on - a venue can append to the bottom of its notification emails. See Email footers for how they’re picked per notification in the portal.
GET /api/my-venues/:venueId/email-footers lists a venue’s footers, each
with used_by - the notification categories currently using it.
curl "https://thesidedoor.co/api/my-venues/VENUE_ID/email-footers" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/email-footers", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` } },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/email-footers");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "data": [ { "id": "ftr_1", "name": "Booking terms", "body": "**Deposits are non-refundable within 48 hours.**", "used_by": ["reservation_confirmation"] } ]}Create, update and delete a footer
Section titled “Create, update and delete a footer”POST /api/my-venues/:venueId/email-footers takes name and body (plain
text with a little formatting - see Email
footers for the supported syntax) and
returns { success: true, id }.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/email-footers" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Booking terms","body":"Deposits are non-refundable within 48 hours."}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/email-footers", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Booking terms", body: "Deposits are non-refundable within 48 hours.", }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/email-footers");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Booking terms", "body" => "Deposits are non-refundable within 48 hours.", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "ftr_1" }Update with PUT /api/my-venues/:venueId/email-footers/:id (same name and
body fields), or remove it with DELETE /api/my-venues/:venueId/email-footers/:id.
Deleting a footer that’s in use clears it from every notification that picked
it - those emails go back to no footer rather than failing to send.