API · Public events feed
The public events feed exposes the same events your customers see on your shop, as JSON, on the same URL — just request application/json. It’s designed for embedding your event lineup on your own website, in a partner site, or in a third-party tool.
No authentication, CORS-enabled, cacheable.
If you’re looking for the authenticated, account-wide API instead — drafts, orders, customers, write access — see the admin API.
Quick example
curl -H "Accept: application/json" \
https://your-subdomain.usetix.io/events
From a browser:
fetch("https://your-subdomain.usetix.io/events.json")
.then(r => r.json())
.then(data => console.log(data.events))
Endpoints
GET /events
Returns published, upcoming events for the shop. Past and unpublished events are never included.
curl -H "Accept: application/json" \
https://your-subdomain.usetix.io/events
Response:
{
"events": [
{
"slug": "spring-showcase",
"title": "Spring Showcase",
"description": "<p>Our annual seasonal event.</p>",
"url": "https://your-subdomain.usetix.io/events/spring-showcase",
"starts_at": "2026-05-01T19:00:00Z",
"ends_at": "2026-05-01T23:00:00Z",
"sales_starts_at": null,
"sales_ends_at": "2026-05-01T19:00:00Z",
"time_zone": "Berlin",
"minimum_age": 18,
"video_url": null,
"sold_out": false,
"image_url": "https://your-subdomain.usetix.io/rails/active_storage/blobs/...",
"background_image_url": null,
"gallery_image_urls": [],
"venue": {
"name": "The Venue",
"address": "Hauptstraße 1",
"postal_code": "10115",
"city": "Berlin",
"country": "DE",
"latitude": null,
"longitude": null,
"time_zone": "Berlin"
},
"cheapest_available_price": {
"amount": "15.00",
"currency": "EUR"
}
}
]
}
GET /events/:slug
Returns a single event with its tickets, performers, and FAQ. Returns 404 Not Found if the slug doesn’t exist or the event isn’t published.
curl -H "Accept: application/json" \
https://your-subdomain.usetix.io/events/spring-showcase
Query parameters:
| Parameter | Description |
|---|---|
code |
Access code for hidden tickets. Repeat for multiple: ?code=AAA&code=BBB. Without a matching code, hidden tickets are excluded from the response. |
Response: all the event fields, plus:
{
"slug": "spring-showcase",
"title": "Spring Showcase",
"...": "...",
"tickets": [
{
"title": "Early Bird",
"kind": "StandardTicket",
"color": "#10b981",
"features": ["Free coat check"],
"price": { "amount": "15.00", "currency": "EUR" },
"available": true,
"sold_out": false,
"low_stock": false,
"max_per_order": 4
}
],
"performers": [
{
"name": "DJ Example",
"performer_type": "person",
"role": "headliner",
"genre": "Techno",
"starts_at": "2026-05-01T22:00:00Z",
"ends_at": "2026-05-02T01:00:00Z",
"bio": "<p>Berlin-based...</p>",
"image_url": "https://your-subdomain.usetix.io/rails/active_storage/blobs/...",
"links": {
"website": "https://example.com",
"instagram": "https://instagram.com/example",
"spotify": null,
"soundcloud": null,
"tiktok": null,
"youtube": null
}
}
],
"faq_items": [
{ "question": "Is there an age limit?", "answer": "18 and over." }
]
}
Event fields
| Field | Type | Notes |
|---|---|---|
slug |
string | URL slug. Use it as the path parameter on GET /events/:slug. |
title |
string | Event title. |
description |
string | HTML. May contain rich-text formatting. Empty string if no description. |
url |
string | Canonical shop URL for the event. |
starts_at |
string | ISO 8601 UTC. |
ends_at |
string | ISO 8601 UTC. |
sales_starts_at |
string | null | ISO 8601 UTC. null if sales are open immediately. |
sales_ends_at |
string | ISO 8601 UTC. |
time_zone |
string | IANA / Rails time zone name (e.g. "Berlin") — useful for displaying local times. |
minimum_age |
integer | null | |
video_url |
string | null | YouTube URL if set. |
sold_out |
boolean | true when no tickets remain. |
image_url |
string | null | Absolute URL to the event image. |
background_image_url |
string | null | Absolute URL to the background image. |
gallery_image_urls |
string[] | Absolute URLs to gallery images, in order. |
venue |
object | See below. |
cheapest_available_price |
object | null | The lowest-priced ticket still available. null when sold out or no tickets configured. |
venue
| Field | Type | Notes |
|---|---|---|
name |
string | |
address |
string | Street address. |
postal_code |
string | null | |
city |
string | |
country |
string | ISO 3166-1 alpha-2. |
latitude |
number | null | Decimal degrees. |
longitude |
number | null | Decimal degrees. |
time_zone |
string | IANA / Rails time zone name. |
tickets[] (show only)
| Field | Type | Notes |
|---|---|---|
title |
string | |
kind |
string | "StandardTicket" or "GroupTicket". |
color |
string | Hex color (e.g. "#10b981") used in the shop. |
features |
string[] | Bullet-point list of perks. |
price |
object | { amount, currency }. See Money fields. |
available |
boolean | true when stock and event capacity allow purchase. |
sold_out |
boolean | Inverse of available. |
low_stock |
boolean | true when ≤ 15 left in stock. Stays false for unlimited-stock tickets. |
max_per_order |
integer | null | Per-order purchase cap. null when unlimited. |
performers[] (show only)
| Field | Type | Notes |
|---|---|---|
name |
string | |
performer_type |
string | "person" or "performing_group". |
role |
string | "headliner", "support", or "guest". |
genre |
string | null | |
starts_at |
string | null | Set time, ISO 8601 UTC. |
ends_at |
string | null | |
bio |
string | HTML. |
image_url |
string | null | |
links |
object | Social links — website, instagram, spotify, soundcloud, tiktok, youtube. Each is a string or null. |
faq_items[] (show only)
| Field | Type | Notes |
|---|---|---|
question |
string | |
answer |
string | Plain text. |
What’s not in the feed
Deliberately omitted, because it’s either internal or for the authenticated admin API:
- Database IDs (use
slugas the stable identifier) - Drafts and unpublished events
- Hidden tickets without a matching access code
- Past events (on the index)
- Capacity, sales totals, ticket stock counts (use
low_stockandsold_outinstead) - Ticket access codes
- Customer or order data
Authentication
None. The feed only exposes data your shop is already publishing. No tokens, no headers required beyond Accept: application/json.
CORS
All responses include:
Access-Control-Allow-Origin: *
You can fetch the feed from any origin in the browser without a proxy.
Caching
The feed is built to be cached by browsers, CDNs, and your own infrastructure.
Cache-Control: public, max-age=60— both endpoints. Shared caches (CDN, reverse proxy) can serve the response for up to a minute.ETag—GET /events/:slugreturns an ETag based on the event’s last-modified timestamp. Send it back asIf-None-Matchon subsequent requests; if nothing has changed, you get a304 Not Modifiedwith no body.
A typical embed setup polls every minute or two — well within the cache window — and gets near-instant responses.
Custom domains
If your shop runs on a custom domain (e.g. tickets.yourbrand.com), the feed is available at the same path on that domain:
https://tickets.yourbrand.com/events.json
https://tickets.yourbrand.com/events/spring-showcase.json
Embedding example
A minimal “next event” widget for any HTML page:
<div id="next-event"></div>
<script>
fetch("https://your-subdomain.usetix.io/events.json")
.then(r => r.json())
.then(data => {
const next = data.events[0];
if (!next) return;
document.getElementById("next-event").innerHTML = `
<a href="${next.url}">
<img src="${next.image_url}" alt="">
<h3>${next.title}</h3>
<time datetime="${next.starts_at}">${new Date(next.starts_at).toLocaleString()}</time>
<p>${next.venue.name}, ${next.venue.city}</p>
${next.sold_out ? "<span>Sold out</span>" : `<span>From ${next.cheapest_available_price.currency} ${next.cheapest_available_price.amount}</span>`}
</a>
`;
});
</script>
Changes and stability
The feed is intentionally narrow — additive changes (new fields) may ship without notice. We won’t remove fields without a deprecation window. If something breaks for you, let us know.