Skip to content

Add ticket widgets to any website

Copy one snippet from the dashboard, paste it into an organizer site, and send buyers to your hosted event pages.

5-second quick start

Paste this anywhere inside <body>. It works in any browser, any site builder.

Listings widget

<div
  class="tickets-widget"
  data-widget-type="listings"
  data-organizer="demo"
  data-limit="3"
  data-primary-color="#F2CC5D"
></div>
<script async src="https://gigshack-tickets-staging.up.railway.app/v1/loader.js"></script>

Heads up: The snippet uses synthetic demo values (organizer demo). For your real site, copy the snippet from the dashboard instead — it has your real organizer slug and brand color.

Build your own snippet

Pick a widget type, fill in the fields, and copy the live-updating snippet. The preview on the right is a real widget rendering with your settings.

6

Live preview

Snippet

<div
  class="tickets-widget"
  data-widget-type="listings"
  data-organizer="demo"
  data-limit="6"
  data-primary-color="#F2CC5D"
  data-font-family="Inter, system-ui, sans-serif"
></div>
<script async src="https://gigshack-tickets-staging.up.railway.app/v1/loader.js"></script>

Live demos

These demos use rolling synthetic events, so dates stay in the future without creating database rows.

Buy button

A compact call-to-action for a specific event. The button opens the hosted event page in a new tab.

Snippet

<div
  class="tickets-widget"
  data-widget-type="buy"
  data-event-id="evt_DEMO"
  data-label="Buy tickets"
  data-primary-color="#F2CC5D"
></div>
<script async src="https://gigshack-tickets-staging.up.railway.app/v1/loader.js"></script>

Event listings

A responsive list of upcoming event cards for one organizer. Each card opens the hosted event page in a new tab.

Snippet

<div
  class="tickets-widget"
  data-widget-type="listings"
  data-organizer="demo"
  data-limit="3"
  data-primary-color="#F2CC5D"
></div>
<script async src="https://gigshack-tickets-staging.up.railway.app/v1/loader.js"></script>

How it works

1

Copy a snippet from the dashboard

Event pages show a Buy button snippet. The events list page shows a Listings snippet per organizer. Both include your real event IDs, brand color, and loader URL.

2

Paste into your website

Use an HTML/embed block in WordPress, Squarespace, Webflow, or any plain HTML page. See the platform guide below for site-builder-specific steps.

3

Buyer clicks → hosted checkout

The widget links to your hosted event page in a new tab. Basket, payment, and ticket delivery continue through the normal flow — nothing changes for you.

Configuration attributes

Every attribute is read from the <div> and validated by the loader. Invalid colors or fonts fall back to safe defaults.

All attributes

FieldTypeDescription
data-widget-typerequiredenum

`buy` or `listings`. Required.

data-event-idrequiredstring

Public event ID generated by the dashboard (format: `evt_XXXX`). Required for Buy.

data-organizerrequiredstring

Organizer slug from the dashboard. Required for Listings.

data-labelstring

Button label. Defaults to `Buy tickets`.

data-limitinteger 1-24

Number of event cards to show. Defaults to 6.

data-categorystring

Optional event category filter (e.g. `music`, `comedy`).

data-primary-colorhex color

Button/card accent color. Must match `^#[0-9a-fA-F]{6}$`. Defaults to `#F2CC5D`.

data-font-familyfont stack

CSS font family. Must match `^[a-zA-Z0-9 ,'-]{1,100}$`. Defaults to `Inter, system-ui, sans-serif`.

Validation rules

  • Colors must match ^#[0-9a-fA-F]6$ (six-digit hex with #). Invalid colors fall back to #F2CC5D.
  • Font families must match ^[a-zA-Z0-9 ,'-]{1,100}$ (no quotes, no semicolons). Invalid values fall back to Inter, system-ui, sans-serif.
  • Limit is clamped to 1-24 even if a higher value is sent.
  • Event IDs and organizer slugs are URL-encoded before being passed to the API.

Platform guides

Pick your site builder for step-by-step paste instructions.

Pick your platform

Click a platform for step-by-step paste instructions.

WordPress
  1. In the post or page editor, add a new Custom HTML block where you want the widget to appear.
  2. Paste the full snippet (the `<div>` and the `<script>`) into the block.
  3. Click Preview to verify the widget renders correctly.
  4. Click Update / Publish to make the page live.

Heads up: If your theme or a security plugin strips `<script>` tags from the block content, add the loader once in your theme footer: go to Appearance → Theme File Editor → Theme Footer (`footer.php`), then just before `</body>` paste only the `<script async src="…/v1/loader.js"></script>` line. Keep the `<div class="tickets-widget" …></div>` inside the Custom HTML block.

Squarespace
  1. Edit the page, click + Add Block, and choose Code.
  2. Paste the full snippet into the code block.
  3. Click Apply then Save.

Heads up: If Squarespace rejects the `<script>` tag, use the iframe embed alternative and put the `<iframe>` in a Code block. Iframe URL: `https://your-domain.example/tickets/embed/:slug`.

Webflow
  1. In the Webflow Designer, drag an Embed element onto the page where the widget should appear.
  2. Paste the full snippet into the embed code editor.
  3. Click Save & Close, then publish the site.

Heads up: If Webflow's HTML sanitiser strips the `<script>`, add the loader to Project Settings → Custom Code → Footer Code (just the script tag) and keep the `<div>` inside the Embed element.

Wix
  1. Click + Add → More → HTML iframe.
  2. Set the Source URL to `https://your-domain.example/tickets/embed/:slug` for the event you want to embed.
  3. Set the width to 100% (or a fixed pixel value) and height to 380.
  4. Click Apply then Publish.

Heads up: Wix's HTML embed element strips `<script>` tags, so the JS widget will not work on Wix. The iframe embed alternative is the recommended approach. Wix sites that need the listings grid should use one iframe per event, or use Wix Velo to fetch the public widget API directly.

iframe embed alternative

If your site builder strips <script> tags (most notably Wix), use the iframe embed alternative. It renders a pre-styled ticket card at a fixed URL.

iframe embed

<iframe
  src="https://your-domain.example/tickets/embed/friday-sessions"
  width="100%"
  height="380"
  style="border:0; max-width: 480px;"
  loading="lazy"
  title="Buy tickets"
></iframe>

The iframe returns a standalone HTML page with the event details, available ticket types, and a "Buy tickets" button that links to the hosted event page. It is responsive up to 480px wide.

Advanced: styling and lifecycle

Shadow DOM isolation

The widget renders inside an open Shadow DOM attached to your <div>. Host page CSS cannot reach inside, and widget CSS cannot leak out. The only way to customize appearance is through the `data-*` attributes.

Lifecycle

The loader runs on page load, on DOMContentLoaded, on `load`, and via a MutationObserver. Widgets added dynamically (e.g. via SPA route changes) are picked up automatically. You can also force a re-scan manually with window.TicketsWidgets.initAll().

CSS custom property

The widget sets --tickets-primary inside its Shadow DOM, used for the button background and CTA pill. This is internal — you cannot override it from outside the Shadow DOM.

Caching

The loader script is served with Cache-Control: public, max-age=300, stale-while-revalidate=3600. The first page load downloads the script; subsequent loads use the cached version for 5 minutes and silently revalidate in the background for up to an hour.

Public API response fields

The loader fetches from the public widget API. Each event returned has this shape:

PublicWidgetEvent

FieldTypeDescription
public_idrequiredstring

Public event ID in `evt_XXXX` format.

namerequiredstring

Event name.

slugrequiredstring

URL slug of the hosted event page.

summarystring | null

Short event description.

start_timerequiredISO 8601 string

Event start time in UTC.

end_timerequiredISO 8601 string

Event end time in UTC.

image_urlstring | null

Absolute URL of the event image, or null.

categoryrequiredstring

Event category (e.g. `music`, `comedy`, `theatre`).

age_restrictionrequiredstring

e.g. `all_ages`, `18_plus`.

event_formatrequiredstring

e.g. `in_person`, `online`.

venue_namestring | null

Venue name.

venue_citystring | null

Venue city.

urlrequiredstring

Absolute URL to the hosted event page. Widget links go here.

min_priceinteger | null

Lowest public ticket type price in the smallest currency unit (pence, cents). `null` if no public tickets.

currencystring | null

ISO 4217 currency code (e.g. `GBP`).

availability_statusrequiredstring

Computed sale status: `on_sale`, `upcoming`, `sold_out`, `closed`.

Troubleshooting

SymptomCauseFix
Buy button shows "Event is not available."Event is unpublished, sold out, sale window is in the future, or the public ID is wrong.Verify the event is **published** in the dashboard, then copy the exact `data-event-id` from the dashboard snippet. The ID is case-sensitive.
Listings widget shows "No upcoming events are available."No upcoming published events for the organizer.Verify the organizer has at least one published event with a `start_time` in the future. The widget filters out past and unpublished events.
Listings widget shows "Events are not available." (generic error)Wrong organizer slug, or the network request failed.Verify `data-organizer` matches the organizer slug in the dashboard URL. Check the browser dev tools Network tab for the `/api/public/widgets/events` request.
Widget renders nothing at allThe `<script>` tag was stripped by the editor.Use the iframe embed alternative, or move the script tag to your site footer (e.g. WordPress `footer.php`, Webflow Project Settings → Custom Code → Footer Code).
Widget renders but links go to a 404`WIDGET_ORIGIN` / `PUBLIC_ORIGIN` is misconfigured.Verify the env var matches the public-facing domain where the event page is hosted. The widget computes links as `WIDGET_ORIGIN + /tickets/events/:slug`.
Wrong colors or fonts in the widgetInvalid hex color or font family value.Colors must be six-digit hex with `#` (e.g. `#F2CC5D`). Font families must match `^[a-zA-Z0-9 ,'-]{1,100}$` — no quotes, no semicolons.
"Too Many Requests" / 429Rate limit exceeded (240 requests/min per IP for the public widget API).Reduce request frequency, cache the loader, or contact support if you need a higher limit.
Console error: `evt_DEMO` event not found in productionThe demo event only exists in the public widget API on the docs page, not in your dashboard.Use a real `data-event-id` from the dashboard in production snippets. `evt_DEMO` is only for the docs page at `/developers/widgets`.

Security and public data

No secrets

Widget snippets do not contain API keys. Public endpoints return only active organizer, published event data.

No credentials

The loader fetches with credentials: 'omit'. The browser does not send cookies. No write operations are exposed via the public API.

Server-side filtering

Listings exclude past events and unpublished events at the database query level, not just the response level. Private ticket types are excluded from pricing.

Link safety

All clickable links use rel="noopener noreferrer" and target="_blank" for security and performance.

Next steps

Use the API

Read event data programmatically with API keys. See the API docs for full reference, response examples, and a working playground.

API docs →

Quick start

Two-track 5-minute walkthrough: embed widgets OR use the API. Great for first-time integrators.

Quick start →