Docs

Moving from seats.io

Most of what you built on seats.io maps one to one. Bring your charts across with their labels, point your calls at venuery, swap the renderer, then move live events with their bookings. You can do it one venue, or one event, at a time.

1. Bring your charts

In the dashboard, open a venue and use From seats.io: enter the chart key, your region and your seats.io secret key (used once, to read the chart, and never stored), or upload the drawing's JSON. Through the API, send the drawing as the chart's document:

# the drawing, from seats.io (use your region: eu, na, sa, oc)
curl https://api-eu.seatsio.net/charts/CHART_KEY/version/published -u SEATSIO_SECRET: > drawing.json

curl https://www.venuery.com/v1/charts -u sk_test_…: \
  -d '{"venue":"ven_…","name":"Main stand","document":'"$(cat drawing.json)"'}'

Sections, rows, seats, tables, booths, general admission areas, categories, accessibility flags, zones, floors, focal points, shapes and text come across. Background images don't: upload them in the designer to trace over. Seats keep their seats.io labels, so labels you've stored still work. Check the chart in the designer, then publish it.

2. Map labels to ids (optional)

Every call here takes labels, so you can keep using them. Labels can change when a venue renames a row, though, and ids never do. To store ids instead, fetch the label map of a published version:

curl "https://www.venuery.com/v1/charts/cht_…/versions/1?expand=labels" -u sk_test_…:

{ "labels": { "A-1-12": "s_4k9q2m8f1x7c", "T1": "t_8f1x7c4k9q2m", "Lawn": "p_lawn", … } }

3. Point your calls at venuery

Authentication works the same way: HTTP Basic with the secret key as the username is accepted, as well as bearer tokens. Change the base URL to https://www.venuery.com/v1 and map the calls:

seats.iovenuery
POST /charts (then draw)POST /v1/charts, with the drawing as document
POST /charts/{key}/version/draft/actions/publishPOST /v1/charts/{id}/publish
POST /events {chartKey, eventKey}POST /v1/events {chart, key}
POST /events (several)POST /v1/events/bulk
GET /events/{eventKey}GET /v1/events/{key}
POST /hold-tokensPOST /v1/holds (the hold is the token and its seats)
POST /hold-tokens/{token} {expiresInMinutes}PATCH /v1/holds/{id} {ttl_seconds}
POST /events/{key}/actions/hold {objects, holdToken}PATCH /v1/holds/{id} {add: {objects}}
POST /events/{key}/actions/book {objects, holdToken, orderId}POST /v1/events/{key}/book {hold or objects, order_ref}
POST /events/{key}/actions/release {objects}POST /v1/events/{key}/release {objects or order_ref}
POST /events/{key}/actions/change-object-status {status}POST /v1/events/{key}/block {status}, and /unblock
POST /events/{key}/actions/mark-as-not-for-salePOST /v1/events/{key}/block (status not_for_sale)
POST /events/actions/change-object-status (batch)POST /v1/status_changes/batch
POST /events/{key}/actions/book-bestPOST /v1/events/{key}/best_available {action: "book"}
GET /events/{key}/objects/{label}GET /v1/events/{key}/objects?labels=…
GET /reports/events/{key}/byStatusGET /v1/events/{key}/availability
WebhooksPOST /v1/webhook_endpoints

A few things work differently, mostly in your favour:

  • Holds are objects. Create one with the seats and get its id, rather than making a token and then holding seats with it. Extend it any time, up to two hours from creation.
  • POSTs can be retried safely. Send an Idempotency-Key; a retried booking replays its first response rather than failing because the seats are already booked.
  • Errors list the seats. A 409 names every conflicting seat in conflicts, with its status.
  • Release by order. release with an order_ref frees everything in that order, GA places included.
  • Events pin a chart version. Publishing a new version never moves seats under events already on sale.
seats.io termvenuery
workspace secret keysecret key (sk_live_…); test keys (sk_test_…) replace the sandbox workspace
workspace public keypublishable key (pk_live_…)
holdTokenhold id (hld_…)
orderIdorder_ref
extraDatametadata
free / reservedByToken / bookedavailable / held / booked
forSale: falsestatus not_for_sale
custom statuscustom status (lowercase, letters, digits and _)
object label (A-1-12)label (A-1-12) or stable id (s_…)

4. Swap the renderer

venuery.js takes the same shape of configuration. The biggest difference: every pick is held straight away, so there's no session to manage.

seats.io renderervenuery.js
new seatsio.SeatingChart({ divId, workspaceKey, event })new venuery.SeatingChart({ container, publicKey, event })
pricing: [{ category, price }]pricing: { categoryKey: "$25" }
session: "continue"always on: picks are held as they're made
maxSelectedObjectsmaxSelectedObjects
onObjectSelected / onObjectDeselectedonObjectSelected / onObjectDeselected
onHoldFailed, onHoldTokenExpiredonHoldFailed, onHoldExpired
chart.holdTokenchart.holdId
chart.selectedObjectsthe selection passed to onSelectionChanged

5. Move live events

Create the event here on the imported chart, then bring its state across from a seats.io event report. Booked seats are booked here under the same orderId with their extraData; custom statuses become blocked statuses; seats not for sale stay off sale. Do a dry run first:

# the event's state, from seats.io (keep only the fields the import reads)
curl https://api-eu.seatsio.net/reports/events/EVENT_KEY/byStatus -u SEATSIO_SECRET: \
  | jq 'map_values(map(select(.status != "free" or .forSale == false or .objectType == "GeneralAdmissionArea")
      | {label, status, orderId, extraData, forSale, objectType, numBooked, numNotForSale}))' > report.json

curl https://www.venuery.com/v1/events/EVENT_KEY/import -u sk_test_…: \
  -d '{"dry_run":true,"report":'"$(cat report.json)"'}'

The answer counts what would be booked and blocked and lists labels the chart doesn't have. When it looks right, send it again without dry_run. The import is all or nothing, and refuses seats already taken here, so it's safe to retry.

  • Statuses meaning someone owns the seat default to booked and resale. If you used custom statuses for sales (say sold), pass booked_statuses.
  • seats.io reports a GA area's total booked, not each order's share, so by default it becomes one booking. To keep orders apart, pass ga_bookings with each order's quantity from your own records.
  • Held seats aren't imported: holds are short-lived, so finish or cancel checkouts on seats.io before you switch.
  • Imported bookings aren't billed again and don't send webhooks.
  • Very large reports: keep each request under 4 MB by splitting by status or by order.
A safe order of steps: import charts and check them; build against test keys; move one quiet event (pause sales on seats.io, import, switch the renderer, resume); then move the rest. Use availability to compare counts with seats.io before you switch.