Website integration

The recipe for running a partner website on the Sway Public API.

This page is the recommended recipe for connecting a crew's website to Sway: the site keeps its own design and hosting, and Sway becomes its source for the roster, events, tickets, forms and ambassador programme.


1. Call the API from your server

Keep the secret key on your server (API routes, server-side rendering, edge functions), never in a page.

  • Store it as a secret (SWAY_API_KEY), never in the repository or the client bundle.
  • Give your pages your own routes (/api/site-data) that call Sway and return only what the page shows.
  • Check the key and its limits at start-up with GET /v1/me.
  • Limits counted per visitor (booking requests, newsletter sign-ups, checkout, the ambassador routes) count the visitor your server names in the body as client_ip, and your key as a whole with twenty times the room. Send it: without it, only the whole key is counted, so one visitor who keeps trying can use up the room of your whole site.
  • A fully static site with no server can use a publishable key (sway_pk_) from the browser instead, for public reads only, listing the site's addresses on the key. See Two kinds of key.
If a secret key leaks (a public repository, a bundle, a log), Suspend it at once in Crew admin → API, then create a replacement and revoke the old key with the keys that replaced it.

2. Let caching carry the traffic

Sway caches every read per crew and serves repeats for free, so your site's traffic does not become API traffic. Do your part:

  • Send If-None-Match. Keep the ETag of each answer; an unchanged resource answers 304, with no body and no cost.
  • Keep your own copy for as long as Sway does, and serve it when the API is slow or unreachable:
DataEndpointsSway keeps it
Crew, roster, promoters, venues/v1/crew, /v1/artists, /v1/promoters, /v1/venues5 minutes
Events, line-ups, days, timetables/v1/events, /v1/events/{id}, /days, /timetable1 minute
Ticket tiers, availability/v1/events/{id}/ticket-tiers, /availability30 seconds
Genres/v1/genres1 hour
  • Ask for what you show. ?fields= trims answers, ?expand=venue,lineup,ticket_tiers on a promoter's feed replaces three requests per event, ?ids= fetches 50 known events at once, and updated_since syncs only what changed.
  • Listen instead of polling. With webhooks, refresh an event when event.updated arrives and add it on event.published, instead of re-reading every page on a timer. Send those refreshes with Cache-Control: no-cache, so that Sway reads them fresh rather than from a copy made before the change.

A site built this way makes a few API requests a minute whatever its traffic, and keeps working through a short outage.


3. Sell tickets

Payment always happens on Sway's checkout page. Two ways to get there:

A link. Each ticket tier carries a checkout_url. Render your own "Get tickets" button pointing at it. Nothing else to build.

A checkout opened by your site. With write:checkout, your server calls POST /v1/checkout-sessions with the tiers and quantities the visitor chose on your page, and redirects to the url it returns. Before that:

  • check a coupon or an access code with POST /v1/coupons/validate or POST /v1/access-codes/validate, to show the discount or unlock hidden tiers before the visitor pays;
  • keep the checkout_id from the answer to follow the checkout (GET /v1/checkout-sessions/{id}) or end it (POST /v1/checkout-sessions/{id}/expire) when the visitor leaves.

Then order.paid arrives on your webhook with "via_your_site": true.

Show availability from the tier's status (on_sale, scheduled, sold_out, off_sale) and GET /v1/events/{id}/availability. The API never exposes stock counts or buyer data.


4. Collect from your forms

Your formEndpointScope
Booking enquiryPOST /v1/booking-requestswrite:bookings
Newsletter sign-upPOST /v1/newsletter-signupwrite:newsletter
Newsletter unsubscribePOST /v1/newsletter-unsubscribewrite:newsletter
A Sway form rendered by your sitePOST /v1/forms/{slug}/submitwrite:forms

Validate on your server first, and keep your own bot protection (a captcha, a honeypot) in front of each form: Sway sees your server, not the visitor. Pass the visitor's address where the endpoint asks for it (client_ip), so that Sway's own limits apply per visitor rather than to your whole site. Booking requests are limited to 10 a minute per visitor and 300 a day per crew.

Booking requests land in Crew admin → API → Booking requests; newsletter sign-ups and form answers in the promoter's CRM and the form's answers. Send Idempotency-Key on every one of these calls, so that a retry never records a submission twice.


5. Keep placeholders as a fallback

Keep your site's static content (sample artists, sample events) as a fallback layer rather than deleting it:

  • If the API is unreachable and your cache is empty, render the placeholders instead of an empty page.
  • If the API returns fewer items than the design needs, pad the grid.
  • Merge order: API data, then your site's own selection, then placeholders.

The site stays presentable at all times, including before the first key is configured.


6. Run the ambassador portal on your server

When the promoter turns on sign-up from your website, the join page, the sign-in page and the member's own page are yours to render, and the programme's leaderboard, rewards and quests are yours to show (read:ambassadors). One rule: the browser holds a cookie of yours, never a Sway credential.

  • Keep the member session (amb_st_...) on your server, in a sealed first-party cookie or a session store. Sway refuses that header from a browser anyway.
  • Run your own bot protection on the join and sign-in forms. Sway renders neither.
  • Trade the ?amb_token=... on your landing page for a session on your server, on first load, and drop it from the URL.
  • Never put a discount_id in a page. Capture ?amb=CODE, keep the code for the visit, and resolve it on your server when you open the checkout.
  • The member list, ledgers and statistics (read:ambassador-members) are personal data for the promoter's own tools, never for a public page.

Checklist

  • SWAY_API_KEY stored as a server-side secret (or a publishable key limited to your websites)
  • GET /v1/me used as a start-up check
  • If-None-Match sent, answers kept, stale copy served on errors
  • fields, expand, ids and updated_since used where they save requests
  • A webhook for event.published and event.updated instead of polling
  • Ticket buttons use checkout_url, or your server opens POST /v1/checkout-sessions
  • Forms posted from your server, with your own bot protection and an Idempotency-Key
  • Placeholder content kept as a fallback
  • Ambassador sessions kept on your server, ?amb=CODE resolved at checkout