---
title: "Migrate from seats.io"
description: "Import a public seats.io event as a SeatLayer draft chart, then map charts, events, categories, holds, labels, and webhooks to SeatLayer."
---

SeatLayer can read the seating layout of a public seats.io event and turn it
into a draft SeatLayer chart. You review the draft in the Designer, publish it,
and then create SeatLayer events from it. This page covers what the import
copies, the steps in the dashboard, and how seats.io concepts map to the
SeatLayer API.

## What the import copies

The import reads the published drawing behind one seats.io event, using only
the public workspace key and event key. No seats.io secret or admin key is
needed.

| Copied | Details |
|---|---|
| Sections | Outline, label, label visibility, and rotation |
| Rows and seats | Exact seat positions and row label position. Seat labels become `Section-Row-Seat`. Rows placed outside any section go into an "Unsectioned" group |
| Seat attributes | Wheelchair (with space type), hearing, sign language, cart, lift-up armrests, semi-ambulatory, and plus-size flags; restricted view and limited legroom become notes |
| Tables | Round and rectangular tables with per-seat positions, book-as-whole, and variable occupancy |
| General admission areas | Polygon outline, capacity, and category |
| Booths | Position and size |
| Text and shapes | Text labels, rectangles, ellipses, polygons, lines, and polylines |
| Categories | Label and color |
| Floors | Each floor becomes a SeatLayer floor |

What it does not copy:

- **Bookings and holds.** Seat statuses are not read. Every seat in the draft
  is free.
- **Ticket prices.** Prices are not read. Set prices on categories in the
  Designer after import.
- **Images and icons.** They are not imported as visuals. Image positions are
  only used to place wheelchair spaces and to detect a pitch or court.
- **The visual theme.** The draft uses a SeatLayer theme.
- **Anything the converter could not read.** A table, booth, GA area, or shape
  with missing geometry is left out and listed in the import review.

The focal point is set to the center of the drawing. A pitch or court is added
only when the drawing or venue name says it is one, for example football,
basketball, or court.

## Import in the dashboard

1. Open **Charts** and choose **Import a public Seats.io event**. In the
   Designer, the same flow is under the new-chart menu as **Import Seats.io
   event…**.
2. Enter the **Public workspace key** (from Workspace Settings in seats.io) and
   the **Event key** (from the event in seats.io).
3. Choose the **Region** the seats.io workspace lives in: Europe, North
   America, South America, or Oceania.
4. Optionally enter a **Draft name**. Without one, SeatLayer uses the seats.io
   chart name.
5. Choose **Import to a draft**. It usually takes 20 to 30 seconds. You can
   close the dialog; a message offers **Open draft** when it is ready.
6. The draft opens in the Designer with an import review. Each finding is
   marked fixed, needs input, unsupported, or informational. Answer the ones
   that need input, for example ticket prices or accessibility mapping.
7. Set category prices, check labels and sections, then publish the chart.

If the import fails, check the public workspace key, event key, and region.

<Aside type="note" title="Import limits">
  An account can start 10 imports per hour. An import that does not finish in
  20 minutes is marked failed. Import is a dashboard feature: it needs a
  signed-in Designer or admin, and there is no secret-key route for it in the
  Server API.
</Aside>

## Map seats.io concepts to SeatLayer

| seats.io | SeatLayer | Where |
|---|---|---|
| Chart | Chart. Edited as a draft, then published | [Designer](/designer/charts-and-objects/) |
| Event | Event, created from a published chart with its own inventory | [`POST /v1/events`](/server-api/events/) |
| Category (key, label, color) | Chart category. Its key appears as `categoryKey` on every line item | [Pricing and offers](/platform/ticket-pricing-and-offers/) |
| Object label | Object label, such as `A-12`, used in every hold, booking, and webhook | [Inventory models](/start/inventory-models/) |
| Hold token | Hold with a `holdId`, created when the buyer selects seats and expiring after 15 minutes by default | [How seat holds work](/server-api/how-seat-holds-work/) |
| Booking objects | `POST /v1/events/:key/book` with `holdId` and your `bookingRef` | [Booking](/server-api/booking/) |
| Releasing booked objects | `POST /v1/events/:key/unbook` with the `bookingRef` that owns them | [Cancellations and box office](/server-api/cancellations-and-box-office/) |
| Objects taken off sale | Blocks, optionally released at a set time | [Blocking](/server-api/blocking/) |
| Best available | `POST /v1/events/:key/best-available` | [Holds](/server-api/holds/#let-us-pick-the-seats) |
| Channels | Sales channels on one event | [Sales channels](/platform/sales-channels/) |
| Seasons | Fixed Renewable Seasons | [Seasons](/integrations/seasons/) |
| Resale | Resale listings on booked seats | [Resale](/server-api/inventory/#resale) |
| Public workspace key | Publishable key `pk_test_…` or `pk_live_…` | [Authentication](/start/authentication/) |
| Secret key | Secret key `sk_test_…` or `sk_live_…` | [Authentication](/start/authentication/) |

Imported seat labels follow `Section-Row-Seat`. If your order records store
seats.io labels, compare them with the labels in the published SeatLayer chart
before you move live orders.

## Replace your webhook handlers

Point the handlers that react to seat changes at these SeatLayer events:

| Your system needs to know | Subscribe to |
|---|---|
| Seats were booked | `seat.booked`, with `bookingRef` and priced `items` |
| Seats became free again | `seat.released` |
| A buyer's hold ran out | `hold.expired` |
| The event sold out | `event.soldout` |
| An operator blocked seats | `seat.blocked` |
| A resale seat changed hands | `object.resale.sold` |

Every delivery is signed with HMAC SHA-256 and carries an `occurrenceId` to
deduplicate retries. See [webhook events](/webhooks/events/) and
[signature verification](/webhooks/signatures/).

## Swap the buyer SDK

Replace the seats.io chart renderer with the SeatLayer `SeatingChart`:

```js title="browser/chart.js"
import { SeatingChart } from "@seatlayer/js";

const chart = new SeatingChart({
  container: "#chart",
  event: "<YOUR_EVENT_KEY>",
  publicKey: "<YOUR_PUBLIC_KEY>",
  onSelectionChange: renderCart,
  onHold: (hold) => saveHoldId(hold),
});

await chart.render();
```

The chart holds seats as the buyer selects them. Send the `holdId` to your
server, check the price there, take payment, and book with your secret key.
React, Vue, Angular, and the mobile SDKs follow the same pattern; see
[choose an integration](/start/choose-an-integration/) and the
[SeatingChart reference](/buyer-sdk/seating-chart/).

## Cut over one event at a time

1. Import the chart, review it, set prices, and publish it.
2. Create a test-mode event with a `sk_test_…` key and run a full buy: select,
   hold, pay, book, and receive `seat.booked`.
3. Create the live event with a `sk_live_…` key.
4. For seats already sold elsewhere, book them in SeatLayer with their original
   order references using
   [box office booking](/server-api/cancellations-and-box-office/#book-inventory-directly), so they
   are not sold twice. Each seat booked this way uses a
   [credit](/billing/credits/), like any sale.
5. Switch the buyer page to the SeatLayer chart.