TicketMap SDK
The TicketMap SDK lets you sell tickets for your AnyBiz events directly on your own website. It renders an interactive seat map (pan, zoom, seat selection), keeps availability in sync, and hands the visitor off to the hosted payment page — all from a single script tag.
The widget mounts inside a Shadow DOM, so your site’s CSS cannot break the map and the map’s styles never leak into your page. The only global it exposes is window.TicketMap.
Prerequisites
Section titled “Prerequisites”Before you can integrate, your organization needs SDK access configured in the AnyBiz app under Settings → SDK:
- Site key — a publishable key (e.g.
sdk_pub_...) that identifies your organization. It is safe to include in public HTML. - Allowed origins — the list of website origins (e.g.
https://www.example.com) permitted to use the key. An empty list allows any origin; set it in production. - Return URL — the page on your website where visitors land after payment (see Checkout return flow).
You will also need:
- The SDK script (
ticketmap.min.js) — the URL is provided together with your site key. - The API base URL:
https://api.anybiz.rs/api/events/sdk. Always pass it explicitly — if omitted, the SDK assumes the API lives on your own origin.
Quick start
Section titled “Quick start”A minimal embed: authenticate with your site key, mount the map, and check out the visitor’s selection.
<div id="ticket-map"></div><button id="checkoutBtn" disabled>Checkout</button>
<script src="https://<sdk-host>/ticketmap.min.js"></script><script> (async () => { const API_BASE = 'https://api.anybiz.rs/api/events/sdk';
// 1. Exchange your publishable site key for a short-lived auth token const auth = await TicketMap.authenticate({ siteKey: 'sdk_pub_your_key', apiBaseUrl: API_BASE, });
// 2. Mount the seat map for one event display const sdk = await TicketMap.init({ container: '#ticket-map', apiBaseUrl: API_BASE, eventId: 'evt_123', displayId: 'disp_456', authToken: auth.authToken, lang: 'en', onSelectionChange: (selection) => { const btn = document.getElementById('checkoutBtn'); btn.disabled = selection.items.length === 0; btn.textContent = selection.items.length ? `Checkout (${selection.items.length}) — ${selection.total} ${selection.currency ?? ''}` : 'Checkout'; }, onError: (error) => console.error('TicketMap:', error.message), });
// 3. Reserve the selected seats and redirect to the payment page document.getElementById('checkoutBtn').addEventListener('click', () => { sdk.checkout({ customerEmail: 'buyer@example.com', // recommended: ticket delivery + payment receipt }); }); })();</script>checkout() reserves the selected seats, creates a checkout session, and redirects the browser to the hosted payment page. When payment completes, the visitor is sent back to your configured return URL.
Discovering events and displays
Section titled “Discovering events and displays”If you don’t want to hard-code eventId/displayId, list them with the same auth token:
const events = await TicketMap.listEvents({ authToken, apiBaseUrl: API_BASE });// [{ id, name, description, archived, createdAt, updatedAt }, ...]
const displays = await TicketMap.listDisplays({ authToken, eventId: events[0].id, apiBaseUrl: API_BASE,});// [{ id, eventId, startTime, status: 'draft' | 'active' | 'closed', salesStartTime, salesEndTime, ... }, ...]Only displays with seat sales open can be sold through the SDK; use status and the sales window fields to decide what to show.
How selection works
Section titled “How selection works”The SDK is local-selection first: tapping a seat only changes the visual selection in the browser. Nothing is reserved on the server until you call hold() or checkout().
Two integration styles:
- One-shot — the visitor picks seats, then your Checkout button calls
sdk.checkout(). The SDK reserves the seats and immediately redirects to payment. Simplest, and enough for most sites. - Staged cart — call
sdk.hold()to reserve the current selection on the server (default hold: 10 minutes) without paying. The visitor can keep adding seats and callinghold()again; each call merges new reservation ids into the cart. A latersdk.checkout()pays for all held reservations (and clears the cart). UsereleaseHeldReservation(id)to give a hold back.
Seat colors reflect live availability: available (gray), selected (blue), held by this visitor (orange), reserved by someone else (red), sold (dark gray). Availability refreshes automatically every 10 seconds unless you disable polling.
TicketMap.init(options) reference
Section titled “TicketMap.init(options) reference”Required:
| Option | Type | Description |
|---|---|---|
container | string | HTMLElement | CSS selector or element where the widget mounts. |
eventId | string | AnyBiz event id. |
displayId | string | Display id belonging to that event. |
authToken | string | Token from TicketMap.authenticate({ siteKey }). |
Optional:
| Option | Type | Default | Description |
|---|---|---|---|
apiBaseUrl | string | current origin + /api/events/sdk | Always set to https://api.anybiz.rs/api/events/sdk. |
lang | 'en' | 'sr' | 'en' | Language of built-in UI texts. |
labels | Partial<SdkLabels> | — | Override individual UI strings (status texts, legend, error messages). |
holdMinutes | number | 10 | Server hold duration used by hold() / one-shot checkout(). |
pollMs | number | 10000 | Availability polling interval in milliseconds. |
autoPollAvailability | boolean | true | Set false to disable polling and call refreshAvailability() yourself. |
customerName / customerEmail / customerPhone | string | — | Default buyer details sent with checkout (a checkout() call can override them). |
promoCode | string | null | — | Default promo code applied at checkout. |
reservationHolderName / reservationHolderEmail / reservationHolderPhone | string | — | Optional holder details stored on seat holds made by hold(). |
Callbacks:
| Callback | Fires |
|---|---|
onSeatClick(detail) | Only when the visitor taps a seat. detail is { seatId, action: 'select' | 'deselect', seat, selection }. |
onSelectionChange(selection) | After every availability sync — initial load, polling, and seat taps. Receives a selection snapshot. |
onSeatSelect(selection) | Same payload and timing as onSelectionChange (kept for compatibility; use either). |
onCheckoutStart(checkoutId) | When a checkout session is created, before the payment redirect. |
onCheckoutResult(status) | With the initial checkout status returned by the API. |
onError(error) | On any SDK error (failed request, invalid selection, …). |
Drive your cart UI from onSelectionChange; use onSeatClick when you need tap-only behavior — for example calling sdk.hold() immediately on each tap.
Selection snapshot
Section titled “Selection snapshot”onSelectionChange / onSeatSelect / getSelectionSnapshot() provide:
{ seatIds: string[]; items: Array<{ seatId: string; sectionId: string; sectionTitle: string | null; seatRow: string; seatNumber: string; effectivePrice: string; // decimal string, e.g. "1200.00" currency: string; availability: 'available' | 'reserved' | 'sold'; reservationId: string | null; // reservation group id when the seat is held heldByMe: boolean; // true when held by this visitor's session reservedUntil: string | null; // ISO hold expiry }>; total: string; // sum of effectivePrice, e.g. "3600.00" currency: string | null;}Instance methods
Section titled “Instance methods”TicketMap.init() resolves to an instance with:
| Method | Description |
|---|---|
checkout(options?) | If holds exist: pays for the held reservations. Otherwise: reserves the current selection, then redirects to payment. options can override customerName, customerEmail, customerPhone, promoCode per call. |
hold(options?) | Reserves the current selection on the server and adds the reservation ids to the cart. Returns all held ids. options can override holdMinutes and holder details. |
releaseHeldReservation(id) | Releases one held reservation group. |
getHeldReservationIds() | Reservation ids currently in the cart. |
refreshAvailability() | Reloads seat availability on demand. |
getSelectionSnapshot() / getSelectedItems() | Current selection (snapshot / items only). |
zoomIn(step?) / zoomOut(step?) / fitToScreen() | Map controls, e.g. for your own toolbar buttons. |
destroy() | Unmounts the widget and stops polling. Call before re-initializing in the same container. |
Checkout return flow
Section titled “Checkout return flow”Payment happens on the hosted payment page (MSU), not on your site:
checkout()redirects the browser to the payment page.- After payment, the gateway calls the AnyBiz API, which verifies the transaction and finalizes the checkout (tickets are issued on success).
- The API then redirects the visitor to the Return URL from your SDK settings — for successful, declined, and cancelled payments alike — appending the result as query parameters:
responseCode— gateway response code (00= approved,99= declined)responseMsg— human-readable gateway messagemerchantPaymentId— payment referencecheckoutId— the checkout session idmsuAccepted—1when the callback was matched and processed,0otherwise; this is not a payment-success flag — readresponseCodefor the outcome
The same parameters are also duplicated in the URL fragment (after #), as a fallback for intermediaries that strip query strings.
Build a payment-result page at that URL that reads the parameters and shows success or failure. Payment verification has already happened server-side by the time the visitor arrives — the parameters are for display, not for deciding whether to hand over goods.
To re-check a result from the browser (for example if the visitor refreshes the page), call POST /checkout/status with the SDK session token and checkoutId; it returns the verified status: created, pending, paid, failed, or cancelled.
Authentication and token lifetime
Section titled “Authentication and token lifetime”TicketMap.authenticate({ siteKey })exchanges the publishable site key for an auth token valid for 30 minutes. The request’s browser origin must be in your key’s allowed-origins list.TicketMap.init()exchanges the auth token for a per-visitor session token, also valid for 30 minutes, scoped to one event display.- On expiry, requests fail with an error surfaced through
onError— re-runauthenticate()andinit()to recover. For long-lived pages, re-authenticate before callinginit()again rather than caching tokens across visits.
Localization
Section titled “Localization”Built-in texts (status line, legend, hints, error messages) ship in English (lang: 'en') and Serbian (lang: 'sr'). Any string can be overridden:
await TicketMap.init({ // ... lang: 'sr', labels: { statusReady: 'Izaberite svoja mesta', errorSelectAtLeastOneSeat: 'Prvo izaberite mesto na mapi', },});API endpoints
Section titled “API endpoints”The SDK is a thin client over the public SDK endpoint family under https://api.anybiz.rs/api/events/sdk. All are POST with JSON bodies:
| Endpoint | Purpose |
|---|---|
/auth | Exchange site key for an auth token. |
/events, /displays | List sellable events / displays. |
/session | Create a per-visitor session for one display. |
/init | Load the seat map payload. |
/availability | Live seat availability. |
/reserve, /release | Hold / release seat reservations (ids are reservation group ids). |
/checkout | Create a checkout session and payment redirect URL. |
/checkout/status | Verified payment status for a checkout. |
Request and response schemas for each endpoint are in the API Reference sidebar — you only need them if you are building a custom client instead of using the SDK.
Calling the API directly (without the SDK)
Section titled “Calling the API directly (without the SDK)”For server-side integrations — syncing events into your CMS, rendering your own event list — call the same endpoints over plain HTTP. Authentication is a site-key exchange; no user account, API key header, or cookie is involved.
1. Exchange the site key for an auth token:
curl -X POST https://api.anybiz.rs/api/events/sdk/auth \ -H "Content-Type: application/json" \ -d '{"siteKey":"sdk_pub_your_key","origin":"https://www.example.com"}'{ "authToken": "eyJqdGkiOi...", "expiresAt": "2026-09-14T12:30:00.000Z" }The origin rules follow your key’s allowed-origins list: if the list is empty, any origin passes and you may omit the field; if the list is set, you must send one of the listed origins or the request fails with 403. Browsers send their own origin automatically via the SDK.
2. List events with the token:
curl -X POST https://api.anybiz.rs/api/events/sdk/events \ -H "Content-Type: application/json" \ -d '{"authToken":"<token-from-step-1>"}'[ { "id": "evt_123", "name": "Summer Concert", "description": null, "archived": false, "createdAt": "2026-06-01T10:00:00.000Z", "updatedAt": "2026-06-10T09:00:00.000Z" }]3. List displays for an event:
curl -X POST https://api.anybiz.rs/api/events/sdk/displays \ -H "Content-Type: application/json" \ -d '{"authToken":"<token>","eventId":"evt_123"}'The auth token expires after 30 minutes — re-run step 1 when you get an expiry error instead of caching the token long-term.
The remaining endpoints (/session, /init, /availability, /reserve, /checkout) require a per-visitor session token scoped to one event display and are designed to be driven by the SDK in the buyer’s browser. If you are building a fully custom checkout client, follow the schemas in the API Reference.
Troubleshooting
Section titled “Troubleshooting”TicketMap request failed (403)onauthenticate— your page’s origin is not in the site key’s allowed-origins list, or the key is wrong.- Map renders but every seat is red/gray — seats are reserved or sold, or the display’s sales window is closed. Check the display’s
salesStartTime/salesEndTimeand status. Select at least one seat before checkout—checkout()was called with no selection and no held reservations.- Nothing appears in the container — the container element must exist before
init()runs, and must have a width; the map sizes itself to the container.