Docs

Seat map embed

venuery.js puts the seat map on your own checkout page. Buyers zoom, pick seats, tables or general admission, and see prices you choose. Every pick is held for them straight away, so nobody can take a seat they're paying for.

Add it to a page

checkout.html
<div id="seats"></div>

<script src="https://www.venuery.com/embed/v1.js"></script>
<script>
  const chart = new venuery.SeatingChart({
    container: "#seats",
    event: "2027-05-28",            // event id or your key
    publicKey: "pk_test_…",
    pricing: { club: "$45", reserved: "$25", lawn: "$12" },
    onSelectionChanged: (selection) => {
      document.querySelector("#pay").disabled = !selection.hold;
    },
    onHoldFailed: (e) => alert("Sorry, someone just took that seat."),
  }).render();
</script>

The map is an iframe served by venuery, so it works with any framework. In React, create the chart in an effect and call destroy() in its cleanup.

Book when they pay

When the buyer checks out, send chart.holdId to your server along with the order. Once payment succeeds, book the hold with your secret key. If it expired meanwhile, booking fails with 409 and nothing is sold.

server.js
const res = await fetch("https://www.venuery.com/v1/events/2027-05-28/book", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.VENUERY_SECRET_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": order.id,
  },
  body: JSON.stringify({ hold: holdId, order_ref: order.id, metadata: { email: order.email } }),
});
if (res.status === 409) return refundAndApologize(order);
const booking = await res.json(); // { objects: [...], ga: [...], order_ref }

Need longer to take payment? Extend the hold from your server with PATCH /v1/holds/:id (up to two hours from when it was made). To print tickets, fetch the seats' section, row, seat and entrance from /objects.

Options

OptionTypeDescription
containerelement or selectorWhere the seat map goes. Required.
eventstringThe event's id or your key. Required.
publicKeystringA publishable key (pk_live_… or pk_test_…). Required. Secret keys are refused.
heightnumberHeight in pixels (default 560). The map fills the container's width.
maxSelectedObjectsnumberThe most places a buyer can pick (default: the event's max_tickets_per_order).
pricingobjectPrices to show in tooltips, by category key, formatted your way: { club: "$45", reserved: "$25" }.
showCountdownbooleanShow how long the hold has left (default true).
holdTtlSecondsnumberHow long to hold picked seats (default 900, at most 7200).
channelsstring[]Sales channels this page sells (seats kept for other channels show as taken). Add "NO_CHANNEL" to sell unassigned seats too.
titlestringThe iframe's accessible title (default “Seat map”).

Callbacks

onReady(chart)The map has loaded.
onSelectionChanged(selection)After every change to the buyer's places: { objects, ga, hold }.
onObjectSelected(object)A seat or table was picked (and held). object: { id, label, displayed, category, kind, accessibility }.
onObjectDeselected(object)A seat or table was let go.
onHoldFailed({ objects, ga, reason, conflicts })Places couldn't be held, usually because someone else just took them. They're deselected; reason says why.
onHoldExpired({ hold })The hold ran out. The selection is cleared and the seats are back on sale.
selection
{
  "objects": [
    { "id": "s_4k9q2m8f1x7c", "label": "204-C-12", "displayed": { "section": "204", "row": "C", "seat": "12" },
      "category": "reserved", "kind": "seat", "accessibility": [] }
  ],
  "ga": [{ "pool": "p_lawn", "label": "Lawn", "category": "lawn", "quantity": 2 }],
  "hold": { "id": "hld_x7c4b0d8f3k2m9q1", "expires_at": "2027-05-20T18:15:00.000Z" }
}

Methods

render()Adds the map to the container. Returns the chart.
selectObjects(ids)Picks (and holds) seats or tables, by id or label.
deselectObjects(ids)Lets them go.
clearSelection()Releases the hold and clears the selection.
holdIdThe hold to book on your server, or null.
destroy()Removes the map.

How holds behave

  • Each buyer has one hold per event. Picking more seats adds to it; letting go removes them.
  • The hold survives a page reload in the same tab, so a refresh doesn't lose anyone's seats.
  • The map refreshes availability every few seconds and greys out seats as others take them.
  • Held seats show as unavailable to everyone else, on the map and in the API.

Event manager (staff)

The same map with staff tools, for your back office: live statuses with order references, label search, selecting whole sections or drawing a lasso around seats (the Lasso button, or Shift-drag; Alt-drag takes seats out), booking and releasing under an order reference, editing an order (swap, add or take out its seats in one change), taking seats off sale (or into your own statuses) and back, and point-of-sale selling. It never sees a secret key: your server mints a short-lived token for one event and the modes the staff member may use.

server.js
const res = await fetch("https://www.venuery.com/v1/event_manager_tokens", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.VENUERY_SECRET_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ event: "2027-05-28", modes: ["view", "book", "block"], user: staff.email }),
});
const { token } = await res.json(); // works for an hour (ttl_seconds: up to 12 hours)
back office page
<div id="manager"></div>
<script src="https://www.venuery.com/embed/v1.js"></script>
<script>
  const manager = new venuery.EventManager({
    container: "#manager",
    token,                    // from your server
    mode: "book",             // view, sell, book or block
    height: 700,
    // book, release, block, unblock: c.objects; move (an order edited): c.added and c.removed
    onChange: (c) => console.log(c.action, c.objects ?? c.added, c.order_ref),
  }).render();
  // tokens are short-lived: swap in a fresh one without reloading
  // manager.setToken(newToken);
</script>
ModeWhat staff can do
viewSee every seat's status, with order references for selected seats; find seats by label (204-C-12, or 204-C-* for a row). Always included.
sellPoint of sale: the buyer seat map without a per-order limit. Picks are held; book them from your server with manager.holdId.
bookBook seats and GA places under an order reference (channel seats too), or release booked ones. Open an order to edit it: select its seats to take out and free seats to add, then apply them together (POST /v1/events/{id}/move), so a swap never loses the old seats without getting the new ones.
blockTake seats and GA places off sale, as not_for_sale or your own status, and put them back.

Every change is in the status log under the token's user, sends the usual webhooks, and reaches your page as onChange. The token travels in the frame's URL fragment, so it never reaches a server log.

Embedded designer

Let your customers draw their own charts on your pages, white label: sections and rows, tables, general admission, the field or stage, sightlines and floors, the same designer venues use on venuery. Your server mints a token for one chart (Growth plan; any plan with test keys); drafts save as they draw, and with the publish mode they can publish versions your events use.

server.js
const res = await fetch("https://www.venuery.com/v1/designer_tokens", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.VENUERY_SECRET_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ chart: "cht_…", modes: ["edit", "publish"], user: customer.email }),
});
const { token } = await res.json();
your page
<div id="designer"></div>
<script src="https://www.venuery.com/embed/v1.js"></script>
<script>
  new venuery.Designer({
    container: "#designer",
    token,
    height: 760,
    onPublish: ({ version }) => console.log("published version", version),
  }).render();
</script>

Your ticket shop on your site

For venuery Ticketing organizers: put your events and season tickets on your own website. Fans pick seats in the frame and check out in the whole window (card payments can’t run inside a frame), then land on their order. Your shop’s address is on your events page: https://www.venuery.com/o/your-team.

tickets.html
<div id="tickets"></div>
<script src="https://www.venuery.com/embed/v1.js"></script>
<script>
  new venuery.Storefront({
    container: "#tickets",
    org: "wilmington-ghost-crabs",   // or event: "evt_…" for one game's seat map
    height: 760,
  }).render();
</script>

Prefer your own address instead (tickets.yourteam.com)? Set it under Settings → Your own web address and point it at venuery: the whole shop runs there.

Without the script

You can also frame https://www.venuery.com/embed/seatmap?event=…&key=pk_… yourself and talk to it with postMessage. It sends venuery:ready, venuery:selectionChanged, venuery:holdFailed and venuery:holdExpired, and accepts venuery:config, venuery:select, venuery:deselect and venuery:clear. Check that messages come from https://www.venuery.com.

Try it: the demo page takes ?event=…&key=pk_test_… and logs every callback.