Two ways to integrate: a 2-line embed that drops a Buy-tickets button onto any HTML page, or a full Stripe-style REST API with bearer keys, idempotency, and HMAC-signed webhooks.
Whether you only need a Buy-tickets button on a marketing page or a full programmatic integration, this is the path. Should take about 5 minutes.
Go to sbtix.net/demo/signup/ and create your account. From the dashboard, click + Create event and set up your event — name, date, venue, ticket tiers, prices. After publishing you'll see the event card on your dashboard; click it to copy the event slug (looks like konpa-all-stars-night-xyz4). You'll plug this slug into your partner site in step 3.
From your dashboard, click API & Webhooks in the top nav (or go directly to dashboard/api/). Click + New publishable key for the embed widget — this one is safe to ship in front-end code. If you'll be calling the REST API from your backend too, also issue a + New secret key (keep this one server-side).
.env file immediately — you can't retrieve them later, only revoke + reissue.On any page where you want a Buy-tickets button, paste exactly this:
<!-- 1. Load the embed (place once anywhere on the page) --> <script src="https://sbtix.net/v1/embed.js" defer></script> <!-- 2. Any element with this attribute opens the buy modal --> <button data-sbtix-buy="YOUR_EVENT_SLUG">Buy tickets</button>
Replace YOUR_EVENT_SLUG with the slug from step 1. Click the button — sbTix opens a modal iframe right on your page, the visitor picks tiers and pays, and your page never reloads. That's it. No further coding required.
If you want to fire your own analytics, redirect to a thank-you page, or write to your CRM when a sale completes:
window.addEventListener("sbtix:order_complete", e => { // e.detail.order → { id, tickets[], total, buyer, … } // e.detail.event → { slug, name, date, venue } gtag("event", "purchase", { value: e.detail.order.total }); location.href = "/thank-you?order=" + e.detail.order.id; });
Front-end events only fire if a visitor reaches the confirmation screen. For reliability (refunds initiated from inside sbTix, server-side reconciliation), register a webhook URL from the API & Webhooks page. sbTix POSTs to that URL with an X-Sbtix-Signature header you verify with HMAC-SHA256 — see the webhooks section.
If you want to programmatically create events, list orders, issue refunds, or build a custom checkout UI that pings sbTix server-side, use the REST API with your secret key.
curl https://sbtix.net/v1/events \
-H "Authorization: Bearer sbtix_sk_…"
The fastest integration. One script + one attribute = working Buy-tickets button. Visitors stay on your page; the buy flow renders in a sandboxed iframe modal.
<script src="https://sbtix.net/v1/embed.js" defer></script> <button data-sbtix-buy="konpa-all-stars-night-xyz4" data-sbtix-key="sbtix_pk_…">Buy tickets</button>
| Method / Event | Description |
|---|---|
sbtix.open(slug, opts?) | Opens the buy modal. opts.key = publishable key. |
sbtix.close() | Closes the modal programmatically. |
window event sbtix:open | Modal opened. |
window event sbtix:ready | Iframe loaded the event data. |
sbtix:order_complete | Order paid + confirmation shown. e.detail = { order, event } |
sbtix:close | Modal closed. |
Every REST API request needs an API key in the Authorization header.
Authorization: Bearer sbtix_sk_e51c9d6c…
| Key type | Prefix | What it can do | Where it's safe |
|---|---|---|---|
| Secret | sbtix_sk_ | Read + write everything for your org — events, orders, refunds, etc. | Server-side only. Never ship in front-end code or commit to git. |
| Publishable | sbtix_pk_ | Read public event data only (for the embed widget). | Safe to embed in HTML. |
Issue + revoke keys from dashboard/api/.
Base URL: https://sbtix.net/v1 · JSON only · UTF-8 · idempotent POSTs supported via Idempotency-Key.
/v1/events
limit (max 100, default 20), archived=1, deleted=1./v1/events
curl -X POST https://sbtix.net/v1/events \ -H "Authorization: Bearer sbtix_sk_…" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "name": "Konpa All-Stars Night", "date": "2026-06-15", "date_label": "Saturday, June 15 · 9 PM", "venue": "Brick City Lounge, Newark NJ", "tiers": [ { "name": "General Admission", "price": 25, "capacity": 200 }, { "name": "VIP", "price": 75, "capacity": 30 } ], "fees": { "pct": 1, "flat": 0.5, "mode": "buyer" } }'
/v1/events/{slug}
/v1/events/{slug}
name, date, date_label, venue, description, image, tiers, fees, no_refund, sales_suspended./v1/events/{slug}
?permanent=1 for a hard delete./v1/orders?event={slug}
/v1/orders
curl -X POST https://sbtix.net/v1/orders \ -H "Authorization: Bearer sbtix_sk_…" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "event": "konpa-all-stars-night-xyz4", "buyer": { "name": "Yves Fabien", "email": "[email protected]" }, "items": [{ "tierIndex": 0, "qty": 2 }] }'
/v1/orders/{id}?event={slug}
/v1/orders/{id}/refund
{ "event": "<slug>", "tids": ["TICKET_ID", …] }. Omit tids to refund every live ticket. Returns the updated order with status: "partially_refunded" or "refunded".Every POST accepts an Idempotency-Key header. Send the same key with the same body twice in 24 hours and sbTix returns the cached response — no duplicate orders, even if your server retries on a network blip.
-H "Idempotency-Key: bd9c…uuid"
The replayed response carries X-Sbtix-Idempotent-Replayed: true so you can tell it apart in your logs.
sbTix POSTs JSON to your URL whenever something interesting happens. Register endpoints + grab the signing secret from dashboard/api/.
| Event | When it fires |
|---|---|
order.paid | Paid order created (any channel — embed, hosted, API). |
order.rsvp | Free-event RSVP claimed. |
order.comp | Organizer issued a comp ticket. |
order.refunded | Any refund — full or partial. |
event.created / updated / deleted / archived | Event lifecycle changes. |
* | Catch-all — receive every event type. |
{
"id": "evt_4z9c…",
"type": "order.paid",
"created": 1717340821,
"livemode": false,
"data": { // the order, in v1 public shape
"id": "ORD-XYZAB123",
"event": "konpa-all-stars-night-xyz4",
"buyer": { "name": "Yves Fabien", "email": "[email protected]" },
"tickets": [{ "id": "X7W3…", "tier": "GA", "price": 25 }],
"total": 51.25,
"status": "paid"
}
}
Every webhook carries X-Sbtix-Signature: t=<timestamp>,v1=<hex>. Compute HMAC-SHA256 of {timestamp}.{raw-body} using your endpoint's signing secret and compare.
const crypto = require("crypto"); app.post("/webhooks/sbtix", express.raw({type:"application/json"}), (req, res) => { const sig = req.headers["x-sbtix-signature"]; const [t, v1] = sig.split(",").map(p => p.split("=")[1]); const expected = crypto.createHmac("sha256", WEBHOOK_SECRET) .update(t + "." + req.body) .digest("hex"); if (expected !== v1) return res.sendStatus(400); const event = JSON.parse(req.body); // event.type, event.data.… res.sendStatus(200); });
All errors return JSON of the shape { "error": { "type": "…", "message": "…" } }.
| HTTP | Type | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed body / missing param. |
| 401 | authentication_required / invalid_api_key | Missing or unknown key. |
| 403 | insufficient_scope | Publishable key tried to call a secret-only endpoint. |
| 403 | sales_suspended / event_past | Order rejected — sales paused or event already happened. |
| 404 | not_found | Event / order doesn't exist or isn't yours. |
| 409 | conflict | Past event delete blocked — archive instead. |
| 410 | event_closed | Event was deleted. |
| 429 | rate_limited | Slow down. Retry-After header gives seconds. |
Rate limits default to 120 req/min per secret key and 240/min per publishable key. Every response includes RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset headers.