Skip to main content
Parkstack

Developers

Guides

The common jobs, step by step. Every address and field is in the reference.

Selling tickets, start to finish

  1. 1.Read GET /tickets. While onSaleOnline is false, offer nothing to buy. Show each price as the total the guest pays, with “inc. VAT” where includesVat says so, and show daysLine and reentryLine when they are set.
  2. 2.For a ticket with dated: true, show a month from GET /tickets/availability?month=YYYY-MM. Only days with status available can be chosen; say “Sold out”, “Closed” or “Too late” for the others, and mention places left only when fewLeft is set.
  3. 3.Before the button, say who the guest is buying from (seller on GET /park) and link the park’s Ticket terms.
  4. 4.Send POST /checkout from your server. If it is refused, show its message to the guest as it is. Otherwise keep orderNumber and linkToken, and send the guest to url, Stripe’s payment page.
  5. 5.After paying, Stripe sends the guest to your returnUrl with ?order=…&t=… added, or to Parkstack’s confirmation page if you sent none. Read GET /orders/{orderNumber}?t=…: while it is PENDING, ask again every few seconds; once PAID, show a QR code for every ticket. Never offer to pay again. The guest is also emailed their tickets.
JavaScript
// On your server. PARKSTACK_SITE_KEY is the park's key (or the demo key while you build).
const API = "https://api.parkstack.net/site/v1";
const headers = { Authorization: `Bearer ${process.env.PARKSTACK_SITE_KEY}`, "Content-Type": "application/json" };

async function call(path, init) {
  const response = await fetch(`${API}${path}`, { headers, ...init });
  const answer = await response.json();
  if (!response.ok) throw new Error(answer.message); // written for the guest: show it
  return answer;
}

// 1. What is on sale, and (for dated tickets) which days can be booked.
const tickets = await call("/tickets");
if (!tickets.onSaleOnline) { /* show nothing to buy */ }
const month = await call("/tickets/availability?month=2026-11");
const bookable = month.days.filter((day) => day.status === "available");

// 2. The guest chose two dated tickets for a day. Start paying.
const checkout = await call("/checkout", {
  method: "POST",
  body: JSON.stringify({
    email: "guest@example.com",
    lines: [{ ticketId: tickets.items[0].id, quantity: 2, visitDate: bookable[0].date }],
    returnUrl: "https://www.yourpark.co.uk/tickets/thank-you", // one of the key's addresses
  }),
});
// Send the guest to checkout.url (Stripe's page). Keep checkout.orderNumber and checkout.linkToken.

// 3. Back on /tickets/thank-you?order=…&t=…, read the order until it is paid.
const params = new URL(request.url).searchParams;
const order = await call(`/orders/${params.get("order")}?t=${encodeURIComponent(params.get("t"))}`);
// order.status "PENDING": ask again in a few seconds. "PAID": draw each order.tickets[].qrPayload as a QR code.

Building a phone app

Ask the park’s owner for a key made for a phone app: its limit applies to each phone, so it keeps working when many guests use the app at once. The app calls the API itself.

  1. 1.Keep the last answers on the phone and show them with no signal, with when they were last updated.
  2. 2.For checkout, leave out returnUrl and cancelUrl. Open url in the system’s in-app browser (SFSafariViewController or ASWebAuthenticationSession on iOS, Custom Tabs on Android), not an embedded web view, so Apple Pay and Google Pay work.
  3. 3.When the guest comes back to the app, read the order with orderNumber and linkToken until it is PAID, then keep the tickets on the phone so they open at the gate with no signal.
  4. 4.Guests pay on Stripe’s page for tickets used at the park, so do not use in-app purchase for them.

The instructions for AI tools have the same advice, ready to give to an AI coding tool.

Keeping a site up to date

The park changes things in Parkstack and expects to see them on its website soon after. Every answer says how long to keep it (Cache-Control): a minute for park content, never for checkout and orders. Fetch again after that, and a published change appears within about a minute.

JavaScript
// Next.js (App Router): keep each answer for a minute, then fetch it again in the background.
const response = await fetch(`${API}/attractions`, {
  headers,
  next: { revalidate: 60 },
});

Do not copy the park’s words into your code or a database you keep: read them from the API, so what the park publishes is what guests see. A site built once and uploaded as files must be rebuilt regularly, at least daily, or opening hours and closures go out of date. updatedAt on each entry is handy for a sitemap.

Renamed and removed pages

When the park renames an attraction, event or page, its address changes and Parkstack remembers the old one. Use Parkstack’s own addresses (such as /explore/attractions/{slug}), and when a page is not found, ask GET /redirects before showing your 404 page.

JavaScript
// In your 404 handling: ask Parkstack whether the page moved.
const response = await fetch(`${API}/redirects?path=${encodeURIComponent(pathname)}`, { headers });
if (response.ok) {
  const { to } = await response.json();
  return redirect(to, 308); // permanent, so search engines follow it
}
// Otherwise show your "page not found" page.

What has changed

Version 1 only grows: fields, addresses and values are added, never renamed or removed.

  • 29 October 2026: Fixed lists of values, example answers and guides

    • Fields that only ever hold a few values now list them: type on attractions, food and drink and shops and facilities, status.code, terrain, wheelchairAccess.code, fonts[].role, dayMode on tickets and status on orders (which can also be FAILED). They were documented as plain text; the values are the same.
    • Every address in the reference has an example answer and a JavaScript example.
    • More fields have descriptions.
  • 29 October 2026: Phone apps

    • Keys can be made for a phone app. Their per-minute limit applies to each phone.
    • POST /checkout also answers orderNumber and linkToken, so an app can read GET /orders/{orderNumber} without a return address.
  • 29 October 2026: Version 1

    • The park, opening hours, attractions, food and drink, shops and facilities, events, questions, pages, closures and redirects.
    • Tickets, the days they can be booked, checkout and orders.
    • A public demo key for Riverside, a demonstration park whose checkout takes no payment.