---
title: "Platform SDK quickstart"
description: "Create a test inventory event, embed the picker, and commit inventory from your own backend."
---

This guide covers SeatLayer Platform/SDK infrastructure: the buyer SDK creates
a short hold, then your server turns that hold into a permanent inventory
booking. Your application continues to own payment, commercial Orders, tickets,
delivery, refunds, and check-in.

<Aside type="note" title="Start in test mode">
  Use a `pk_test_…` / `sk_test_…` key pair. Sandbox events behave like live events, but bookings spend no credits. Going live is a credential swap after the flow works.
</Aside>

<Steps>
  <Step title="Create an event">
    Publish a chart in the Designer, then create a sandbox event from your server.

    ```js title="server/create-event.js" {5}
    const response = await fetch("https://api.seatlayer.io/v1/events", {
      method: "POST",
      headers: {
        "content-type": "application/json",
        authorization: `Bearer ${process.env.SEATLAYER_SECRET_KEY}`,
      },
      body: JSON.stringify({
        chartId: "ch_grand_theatre",
        name: "Dress rehearsal",
      }),
    });

    const { meta } = await response.json();
    console.log(meta.key); // ev_9f3a
    ```
  </Step>

  <Step title="Install the buyer SDK">
    Use the CDN directly or add the JavaScript package to your app.

    <PackageManagers pkg="@seatlayer/js@0" />
  </Step>

  <Step title="Authorize Public-sale browser access">
    Keep the `pk_test_…` public half of the key pair you used to create the
    event, and register your buyer site's exact HTTPS origin under **Embed
    domains** in the dashboard. The public key is safe to publish in browser
    code; the matching `sk_test_…` is not.

    On first render the SDK sends the event key, public key, and browser-provided
    origin directly to SeatLayer. SeatLayer verifies their account and test/live
    mode, creates a short-lived Public-sale-only session, and returns its
    in-memory bearer together with the chart and compact inventory status. There
    is no customer-backend token round trip on this path.

    A public key cannot reveal presale, login, partner, or channel inventory.
    Those audiences still use an authenticated
    [`buyerAccessTokenProvider`](/server-api/buyer-access-sessions); an explicit
    provider or token always takes precedence over `publicKey`.
  </Step>

  <Step title="Embed the picker">
    Choose the integration that matches your frontend. All variants return the same `holdId` checkout handoff.

    <Tabs syncKey="seatlayer-sdk">
      <TabItem label="Script tag">
        ```html title="checkout.html"
        <div id="picker" style="height: 640px"></div>
        <script src="https://cdn.seatlayer.io/seatlayer-js@0/seatlayer-buyer.js"></script>
        <script>
          const picker = new seatlayer.SeatPicker({
            container: "#picker",
            event: "ev_9f3a",
            publicKey: "pk_test_…",
            onCheckout: async (_, __, handoff) => {
              await fetch("/api/checkout", {
                method: "POST",
                headers: { "content-type": "application/json" },
                body: JSON.stringify({ holdId: handoff.holdId }),
              });
            },
          });
          picker.render();
        </script>
        ```
      </TabItem>
      <TabItem label="JavaScript">
        ```js title="checkout.js"
        import { SeatPicker } from "@seatlayer/js";

        const picker = new SeatPicker({
          container: "#picker",
          event: "ev_9f3a",
          publicKey: "pk_test_…",
          onCheckout: async (_, __, handoff) => {
            await fetch("/api/checkout", {
              method: "POST",
              headers: { "content-type": "application/json" },
              body: JSON.stringify({ holdId: handoff.holdId }),
            });
          },
        });

        await picker.render();
        ```
      </TabItem>
      <TabItem label="React">
        ```tsx title="Checkout.tsx"
        import { SeatPicker } from "@seatlayer/react";

        export function Checkout() {
          return (
            <SeatPicker
              event="ev_9f3a"
              publicKey="pk_test_…"
              style={{ height: 640 }}
              onCheckout={async (_, __, handoff) => {
                await fetch("/api/checkout", {
                  method: "POST",
                  headers: { "content-type": "application/json" },
                  body: JSON.stringify({ holdId: handoff.holdId }),
                });
              }}
            />
          );
        }
        ```
      </TabItem>
    </Tabs>
  </Step>

  <Step title="Book inventory from your server">
    Resolve authoritative inventory and configured prices from the hold,
    complete your payment logic, then book inventory with the secret key. After
    success, create and deliver tickets from your own platform.

    ```js title="server/checkout.js" {3,15,24}
    const eventKey = "ev_9f3a";
    const headers = {
      authorization: `Bearer ${process.env.SEATLAYER_SECRET_KEY}`,
      "content-type": "application/json",
    };

    const holdResponse = await fetch(
      `https://api.seatlayer.io/v1/events/${eventKey}/holds/${holdId}`,
      { headers },
    );
    if (!holdResponse.ok) throw new Error("Hold is no longer active");
    const hold = await holdResponse.json();

    // Price from trusted hold items—not values posted by the browser.
    const order = await createPaidOrder(hold.items);

    const bookResponse = await fetch(
      `https://api.seatlayer.io/v1/events/${eventKey}/book`,
      {
        method: "POST",
        headers,
        body: JSON.stringify({
          holdId,
          bookingRef: order.id,
        }),
      },
    );
    if (!bookResponse.ok) await handleBookingFailure(bookResponse, order);
    ```
  </Step>
</Steps>

<Aside type="caution" title="Never expose the secret key">
  Browser code may receive a publishable `pk_…` for ordinary Public bootstrap,
  but never an `sk_…`. SeatLayer keeps the resulting `bse_…` in the SDK's memory;
  a private-audience provider must do the same rather than using storage or a
  URL. Keep every `sk_test_…` and `sk_live_…` in a server-side secret manager.
</Aside>

## Next

Use the [complete checkout example](/examples/complete-checkout) as an implementation template, then read [integration best practices](/integrations/best-practices) before going live.

If you would rather have SeatLayer run checkout, tickets and the door instead of building them yourself, follow [your first managed event](/start/first-event/) for the direct-organizer route.