Skip to content

Ticketing & Events

The ticketing vertical lets a cultural organisation (a festival, a concert series, an association) publish a programme of dated events and sell tickets for them — and lets a developer embed that catalogue and buy flow into the organisation’s own website (e.g. amei-musique-en-vacances.viite.ai). It reuses the platform’s billing and Stripe foundations: ticket revenue settles directly to the organiser’s Stripe account via Stripe Connect.

Event ──< Session ──< TicketTariff the catalogue an organisation publishes
Order ──< OrderItem a buyer's purchase
  • Event — e.g. XXXIème Festival “Musique en Vacances”. Has a slug (unique per organisation, the public read key), a status (draftpublishedarchived), and an optional date window.
  • Session — a dated show within an event (e.g. Samedi 18 juillet 18h00 – Récital de piano).
  • TicketTariff — a priced, inventoried ticket type under a session. Three kinds:
    • fixed — a set price (e.g. €10).
    • pay_what_you_want — the buyer chooses, optionally above a floor (minAmountCents).
    • free — registration only; no payment, the order is confirmed instantly. A tariff may cap inventory (capacity); available/soldOut are derived. VAT is recorded per tariff (vatRateBps, e.g. 2000 = 20%).
  • Order — created by an anonymous buyer (no platform account). Inventory is reserved when the order is created; a paid order is confirmed when Stripe Checkout completes, a free order immediately. Money is always stored in minor units (cents).

1. Connect a Stripe account (organiser, once)

Section titled “1. Connect a Stripe account (organiser, once)”

Ticket payments use Stripe Connect — funds settle to the organiser’s own account. From the dashboard Events page, the organiser clicks Connect Stripe, which runs Stripe’s onboarding. Until the connected account reports chargesEnabled, paid tickets cannot be sold (free RSVP tariffs still work). Programmatically:

const { url } = await platform.events.connectOnboard({ returnUrl: 'https://dashboard/events' });
// redirect the organiser to `url`; poll status afterwards:
const status = await platform.events.connectStatus(); // { connected, chargesEnabled, ... }

In the dashboard, or via the authenticated SDK (platform.events.*):

const event = await platform.events.createEvent({ title: 'Musique en Vacances', status: 'published' });
const session = await platform.events.createSession(event.id, {
title: 'Samedi 18 juillet 18h00 – Récital de piano',
startsAt: '2026-07-18T18:00:00Z',
});
await platform.events.createTariff(session.id, {
name: 'Tarif Prévente', kind: 'fixed', priceAmountCents: 1000, capacity: 200,
});

Only published events are visible on the public surface.

3. Render the catalogue on your own site (public, API-key gated)

Section titled “3. Render the catalogue on your own site (public, API-key gated)”

Mint an organisation-scoped API key (dashboard → Settings → API keys, see API keys) and keep it server-side. Then read the published catalogue:

import { PlatformApiClient } from '@abeauvois/platform-sdk';
const site = new PlatformApiClient({ baseUrl, getToken: () => process.env.VIITE_API_KEY });
const events = await site.events.listPublicEvents(); // list
const detail = await site.events.getPublicEvent('musique-en-vacances'); // sessions + tariffs
// detail.sessions[].tariffs[] carry { id, name, kind, priceAmountCents, available, soldOut, ... }

The public DTO is a whitelist — internal ids, ownership, and inventory counters never leak; only available/soldOut are exposed.

The buy endpoint is anonymous (gated only by the organisation API key), mirroring quote-request submission. Pass the buyer’s email, the tariff lines, and your own success/cancel URLs:

const { order, checkoutUrl } = await site.ticketing.createOrder({
eventSlug: 'musique-en-vacances',
buyerEmail: buyer.email,
items: [{ tariffId, quantity: 2 }],
successUrl: 'https://your-site/tickets/ok',
cancelUrl: 'https://your-site/tickets',
});
if (checkoutUrl) {
// Paid order → redirect the buyer to Stripe Checkout (charge settles to the organiser).
window.location.href = checkoutUrl;
} else {
// Free order → already confirmed (order.status === 'confirmed').
}

Inventory is reserved the moment the order is created and released automatically if anything fails (organiser not yet payable, Stripe error) or the checkout is abandoned. A confirmed order can be polled with site.ticketing.getOrder(order.id).

Pass source/linkSlug (e.g. the ?s=/utm_source value your site captured from a tracked link) alongside the order to get a fully source-attributed paid event — no separate integration needed:

const { order, checkoutUrl } = await site.ticketing.createOrder({
eventSlug: 'musique-en-vacances',
buyerEmail: buyer.email,
items: [{ tariffId, quantity: 2 }],
successUrl: 'https://your-site/tickets/ok',
cancelUrl: 'https://your-site/tickets',
source: trackedSource,
linkSlug: trackedLinkSlug,
});

Both fields are persisted on the order and echoed through Stripe Checkout metadata; on checkout.session.completed the webhook emits an analytics paid event carrying the same source/linkSlug, so /analytics’s per-source report includes paid conversions — unlike a plain HelloAsso payment link, whose webhook never relays query params back into paid-event metadata.

VariablePurpose
STRIPE_SECRET_KEYShared Stripe secret (reused from subscriptions).
STRIPE_TICKETING_WEBHOOK_SECRETSigning secret of the ticketing webhook endpoint (/api/ticketing/webhooks/stripe).
TICKETING_APPLICATION_FEE_BPSOptional platform commission per sale, in basis points (0 = none).

The webhook keeps orders and Connect status in sync: checkout.session.completed confirms an order (and commits its inventory), checkout.session.expired releases an abandoned one, and account.updated tracks when an organiser becomes payable.