Skip to main content

SeatLayer Python SDK

CI PyPI Python License: MIT

Official Python server SDK for the SeatLayer reserved-seating API.

Server-side only. This package authenticates with your secret key. Never run it anywhere a ticket buyer can reach — browser surfaces get short-lived, origin-bound tokens that you mint here.

Install

pip install seatlayer

Requires Python 3.10 or newer. No runtime dependencies.

Quick start

import os
from seatlayer import SeatLayer

seatlayer = SeatLayer(os.environ["SEATLAYER_SECRET_KEY"])

# 1. Provision a venue from the public template catalog as a new draft chart.
chart = seatlayer.templates.instantiate_template("tpl_arena")["meta"]
seatlayer.charts.publish(chart["id"])

# 2. Create an event on it.
event = seatlayer.events.create(chart_id=chart["id"], name="Spring Gala")["meta"]

# 3. Sell four seats over the phone.
held = seatlayer.inventory.hold_best_available(event["key"], qty=4)
# … take payment against held["items"], which carry authoritative prices …
seatlayer.inventory.book(event["key"], hold_id=held["holdId"], booking_ref="order-8842")

Nullable event-create fields distinguish omission from an explicit reset: passing, for example, venue=None sends JSON null; leaving venue out sends no field.

Test vs live

Keys carry their own mode. sk_test_… keys can only touch test-mode events and sk_live_… only live ones; crossing them returns 403 mode_mismatch, surfaced as SeatLayerAuthError with is_mode_mismatch.

seatlayer = SeatLayer(os.environ["SEATLAYER_SECRET_KEY"])
if os.environ.get("ENV") == "production" and seatlayer.mode != "live":
    raise RuntimeError("Refusing to boot production against test-mode seating data.")

The two selling flows

Buyer picks seats in the browser. Your frontend holds them; your backend confirms the price and books. Never price from what the browser sent you — retrieve_hold is authoritative.

hold = seatlayer.inventory.retrieve_hold(event_key, hold_id)
total = sum(item["unitPrice"] for item in hold["items"])
# … charge `total` in hold["currency"] …
seatlayer.inventory.book(event_key, hold_id=hold_id, booking_ref=charge.id)

Your backend picks the seats. Phone orders, box office, comps.

# Payment already taken — book outright, so nothing is stranded if a second call fails.
seatlayer.inventory.book_best_available(event_key, qty=2, booking_ref="phone-1183")

# Or name the seats yourself.
seatlayer.inventory.box_office_book(event_key, labels=["A-1", "A-2"], booking_ref="comp-14")

Private and partner sales

Channels split event inventory into explicit allocations. A channel id is reporting/routing metadata, not browser authority. Authenticate the buyer in your backend and mint a short-lived token restricted to the event, origin, and allowed allocations:

access = seatlayer.channels.create_buyer_access_session(
    event_key,
    channel_ids=["chn_partner_a"],
    include_public=False,
    allowed_origin="https://tickets.example",
)
# Return access["token"] to the in-memory buyerAccessTokenProvider only.

Never log or persist the returned bse_… bearer. Allocation setup, previews, pause/archive controls, audit-safe session listing, and channel reports are on seatlayer.channels.

Listing and pagination

list() returns one page plus a nextCursor. When you want everything, list_all() pages for you and yields as it goes — a generator rather than a list, because the point of paginating is to not hold an unbounded result set in memory.

# One page, your own paging.
page = seatlayer.events.list(limit=50)
page["events"]
page.get("nextCursor")   # absent once exhausted

# Or let the SDK walk it.
for event in seatlayer.events.list_all():
    sync(event)

Listing events includes live availability counts by default, which costs the server one round-trip per event. list_all() turns them off automatically — walking a whole catalogue is exactly when you don't want that — and you can control it explicitly:

seatlayer.events.list(limit=50, counts=False)

Keeping a hold alive

When an order takes longer than the checkout window — an invoice, a phone sale — extend rather than release and re-hold. Releasing first hands the seats to whoever is racing for them in between.

from seatlayer import SeatLayerConflictError

try:
    seatlayer.inventory.extend_hold(event_key, hold_id, ttl_ms=10 * 60_000)
except SeatLayerConflictError:
    # Gone, expired, or at its renewal cap — the buyer has to re-pick.
    ...

Embedding the control room

Your secret key never reaches a browser. Mint a scoped token instead.

session = seatlayer.sessions.create_manage_session(
    event_key,
    allowed_origin="https://box-office.yourplatform.com",
    capabilities=["event:view", "event:block"],
    expires_in_seconds=3600,
)

capabilities is required by this SDK even though the raw API safely defaults an omitted list to view-only (event:view). Keeping the argument required makes browser authority visible at every call site. Grant the smallest set the page needs.

The full set, all opt-in:

Capability Grants
event:view Read the seat map and its live states
event:block Block and unblock seats
event:cancel Unbook paid seats and issue gateway refunds — destructive, moves money
event:reports Read sales and availability reports
event:channels:view Read sales channels and their allocations
event:channels:manage Create, pause and archive channels; rotate access links
event:orders:read Read SeatLayer-managed orders
event:refund Refund a SeatLayer-managed order
event:tickets:send Send SeatLayer-managed tickets
event:door:view Read the door list
event:door:checkin Check tickets in and out
event:boxoffice Use the managed box-office surface

The two event:channels:* capabilities are not in the default — a token minted before sales channels existed must not silently acquire channel authority — so ask for them explicitly if the page manages channels.

The same pattern embeds the Designer in your own UI:

chart = seatlayer.charts.create(name="Riverside Theatre")["meta"]
designer = seatlayer.sessions.create_designer_session(
    workspace_id=workspace_id,
    chart_id=chart["id"],
    allowed_origin="https://app.yourplatform.com",
    authority="edit",
)
# The API response is an envelope; hand designer["session"]["token"] to the embed.

For safe-mode embeds, pass mode="safe" with safe_mode_options; feature policy is passed with features and the returned settings live under designer["session"].

Webhooks

Subscription responses use the API envelopes exactly: list() returns {"subs": [...]}, create() returns {"sub": ..., "secret": ...} (the secret is shown once), and update() returns {"sub": ...}. Supported event names are seat.booked, seat.released, seat.blocked, hold.expired, hold.created, hold.extended, event.created, and event.soldout.

Verify every delivery against the raw body. Re-serialising it changes the bytes and verification will fail.

from flask import request
from seatlayer import verify_webhook, WebhookVerificationError

@app.post("/webhooks/seatlayer")
def seatlayer_webhook():
    try:
        event = verify_webhook(
            request.get_data(),                                 # raw bytes, not request.json
            request.headers.get("X-SeatLayer-Signature"),
            os.environ["SEATLAYER_WEBHOOK_SECRET"],
        )
    except WebhookVerificationError:
        return "", 400

    # The signed body carries `at`, but nothing enforces a freshness window, so
    # a captured delivery stays valid indefinitely. Deduplicate on occurrenceId —
    # this is your replay protection, not an optimisation.
    if already_processed(event["occurrenceId"]):
        return "", 200

    handle(event)
    return "", 200

Errors

from seatlayer import SeatLayerAuthError, SeatLayerConflictError, SeatLayerRateLimitError

try:
    seatlayer.inventory.hold_best_available(event_key, qty=6)
except SeatLayerConflictError as error:
    if error.is_sold_out:
        return show_alternative_dates()      # a business outcome, not a bug
    raise
except SeatLayerRateLimitError as error:
    return retry_after(error.retry_after_seconds)
except SeatLayerAuthError as error:
    if error.is_mode_mismatch:
        raise RuntimeError("Test key pointed at a live event (or the reverse.)") from error
    raise

Every error carries status, code, body, and request_id — quote the request id in support requests.

Reliability

Retries and idempotency. Reads retry 408, 429 and 5xx responses with backoff. Only chart create, chart copy, template instantiation, event create and workspace create opt into mutation retries: the SDK generates one Idempotency-Key and reuses it for every attempt. All other mutations are single-attempt, even if you supply a key.

Booking safety. Direct and box-office bookings have the server's exact-selection plus booking_ref safeguard, but the SDK still sends them once. Holds, best-available operations, show-once secret creation and raw mutations are also single-attempt; reconcile an unknown outcome before trying again.

SeatLayer(
    os.environ["SEATLAYER_SECRET_KEY"],
    max_retries=3,   # attempts for reads and the five replay-safe creates
    timeout=30.0,    # seconds, per attempt
)

Escape hatch

For surface this SDK does not wrap yet. Raw reads retain retries; raw mutations are single-attempt because the SDK cannot prove that an unknown operation supports exact replay:

seatlayer.request("POST", "/v1/events/ev_1/some-new-route", body={...})

API surface

Resource Methods
charts list list_all create retrieve update delete copy archive unarchive publish
templates instantiate_template
events list list_all create retrieve retrieve_configuration_binding update_configuration_binding update delete update_poster delete_poster update_chart close reopen archive list_ticket_releases update_ticket_releases close_ticket_release retrieve_hold_ttl update_hold_ttl retrieve_report retrieve_log
inventory hold hold_best_available book_best_available extend_hold retrieve_hold release book box_office_book unbook list_bookings retrieve_booking block unblock unblock_all retrieve_availability update_availability
channels list_channels create_channel update_channel update_assignments list_allocation retrieve_access_preview retrieve_report pause unpause archive create_buyer_access_session list_buyer_access_sessions revoke_buyer_access_session create_access_link list_access_links rotate_access_link revoke_access_link
sessions create_manage_session revoke_manage_session create_designer_session revoke_designer_session
webhooks list create update delete list_deliveries
workspaces list create retrieve update

Full reference: SeatLayer server API events

Deliberately not in this SDK

Some API surface is intentionally unwrapped, not merely pending:

  • Hosted-checkout orders and refunds. Reading or refunding a SeatLayer-hosted-checkout sale is not a server-SDK capability. Those records only exist for organisations using hosted checkout; if you run your own commerce store you refund in that store, through your own gateway.
  • Connecting or assigning payment gateways. Connecting one is a dashboard flow, so shipping only the assignment half across seven SDKs would hand you a method that cannot yet succeed.
  • Realtime seat updates. Live seat state reaches the browser through the widget's own socket. There is no server-side subscribe; a secret-key caller gets authoritative state from events.retrieve_report() and inventory.retrieve_availability().

None of these are reachable through request() as a supported path either — they are excluded from the public manifest, not just from the wrapper.

Related resources

Other SeatLayer SDKs

Surface Package
Browser (vanilla) @seatlayer/js
React @seatlayer/react
React Native @seatlayer/react-native
iOS seatlayer-ios
Android seatlayer-android
Flutter seatlayer
Node.js (server) @seatlayer/server
PHP (server) seatlayer/seatlayer-php
Java (server) io.seatlayer:seatlayer-java
Go (server) github.com/seatlayer/seatlayer-go
Ruby (server) seatlayer
.NET (server) SeatLayer

Development

pip install -e ".[dev]"
ruff check src tests && mypy && pytest -q

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

seatlayer-0.6.0.tar.gz (36.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

seatlayer-0.6.0-py3-none-any.whl (26.6 kB view details)

Uploaded Python 3

File details

Details for the file seatlayer-0.6.0.tar.gz.

File metadata

  • Download URL: seatlayer-0.6.0.tar.gz
  • Upload date:
  • Size: 36.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for seatlayer-0.6.0.tar.gz
Algorithm Hash digest
SHA256 747ef724ebd0d51fb0bca1424787f6e210e8130919bd93fe80bc85d70f95b529
MD5 2245d165110082b59b7d047c5d2f576c
BLAKE2b-256 910dc02fb314f1d649153c7a1b07523c20171a31be5e41833e8dadcd9aa6ed84

See more details on using hashes here.

Provenance

The following attestation bundles were made for seatlayer-0.6.0.tar.gz:

Publisher: release.yml on seatlayer/seatlayer-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file seatlayer-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: seatlayer-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 26.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for seatlayer-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 06199277db6b08a2436badefca5539204a13f79c433bee24df1643e38fca17fe
MD5 9d5579d2d9547098e684a69e5f26e397
BLAKE2b-256 3b850b10e7ed66663f9ad752b41860db510d3628d26ce6cbd0aaac89c518495a

See more details on using hashes here.

Provenance

The following attestation bundles were made for seatlayer-0.6.0-py3-none-any.whl:

Publisher: release.yml on seatlayer/seatlayer-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page