The server SDKs wrap the server API with the parts every backend integration ends up writing anyway: idempotency keys that survive retries, backoff that honours our rate limits, errors typed by what you actually branch on, and constant-time webhook verification.
Install
npm install @seatlayer/serverRequires Node 20.19.4 or newer. No runtime dependencies.
import { SeatLayer } from '@seatlayer/server';
const seatlayer = new SeatLayer(process.env.SEATLAYER_SECRET_KEY!);pip install seatlayerRequires Python 3.10 or newer. No runtime dependencies.
import os
from seatlayer import SeatLayer
seatlayer = SeatLayer(os.environ["SEATLAYER_SECRET_KEY"])composer require seatlayer/seatlayer-phpRequires PHP 8.1 or newer with ext-curl. No Composer dependencies.
use SeatLayer\SeatLayer;
$seatlayer = new SeatLayer(getenv('SEATLAYER_SECRET_KEY'));<dependency>
<groupId>io.seatlayer</groupId>
<artifactId>seatlayer-java</artifactId>
<version>0.1.0</version>
</dependency>Requires Java 17 or newer. Zero runtime dependencies — JDK HTTP and crypto only.
import io.seatlayer.SeatLayer;
SeatLayer seatlayer = new SeatLayer(System.getenv("SEATLAYER_SECRET_KEY"));go get github.com/seatlayer/seatlayer-goRequires Go 1.23 or newer. No dependencies — standard library only.
import "github.com/seatlayer/seatlayer-go"
client, err := seatlayer.New(os.Getenv("SEATLAYER_SECRET_KEY"))gem install seatlayerRequires Ruby 3.0 or newer. No runtime dependencies.
require "seatlayer"
client = SeatLayer::Client.new(ENV.fetch("SEATLAYER_SECRET_KEY"))dotnet add package SeatLayerRequires .NET 8 or newer. No package dependencies.
using SeatLayer;
var client = new SeatLayerClient(Environment.GetEnvironmentVariable("SEATLAYER_SECRET_KEY")!);Your first sale
The shape below is the whole product in five calls: provision a venue, put an event on it, and sell four seats without a browser in the loop.
// 1. Provision a venue for a new organiser from one of your templates.
const { meta: chart } = await seatlayer.charts.copy('c_template_arena');
await seatlayer.charts.publish(chart.id);
// 2. Create an event on it.
const { meta: event } = await seatlayer.events.create({
chartId: chart.id,
name: 'Spring Gala',
});
// 3. Sell four seats over the phone.
const held = await seatlayer.inventory.holdBestAvailable(event.key, { qty: 4 });
// … take payment against held.items, which carry authoritative prices …
await seatlayer.inventory.book(event.key, {
holdId: held.holdId,
bookingRef: 'order-8842',
});# 1. Provision a venue for a new organiser from one of your templates.
chart = seatlayer.charts.copy("c_template_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"
)// 1. Provision a venue for a new organiser from one of your templates.
$chart = $seatlayer->charts->copy('c_template_arena')['meta'];
$seatlayer->charts->publish($chart['id']);
// 2. Create an event on it.
$event = $seatlayer->events->create($chart['id'], name: 'Spring Gala')['meta'];
// 3. Sell four seats over the phone.
$held = $seatlayer->inventory->holdBestAvailable($event['key'], qty: 4);
// … take payment against $held['items'], which carry authoritative prices …
$seatlayer->inventory->book($event['key'], holdId: $held['holdId'], bookingRef: 'order-8842');// 1. Provision a venue for a new organiser from one of your templates.
var chart = (Map<String, Object>) seatlayer.charts().copy("c_template_arena").get("meta");
seatlayer.charts().publish((String) chart.get("id"));
// 2. Create an event on it.
var event = (Map<String, Object>) seatlayer.events()
.create((String) chart.get("id"), "Spring Gala").get("meta");
// 3. Sell four seats over the phone.
var held = seatlayer.inventory().holdBestAvailable((String) event.get("key"), 4);
// … take payment against held.get("items"), which carry authoritative prices …
seatlayer.inventory().book((String) event.get("key"), (String) held.get("holdId"), "order-8842");ctx := context.Background()
// 1. Provision a venue for a new organiser from one of your templates.
chart, err := client.Charts.Copy(ctx, "c_template_arena")
chartID := chart["meta"].(map[string]any)["id"].(string)
client.Charts.Publish(ctx, chartID)
// 2. Create an event on it.
event, err := client.Events.Create(ctx, seatlayer.EventCreateParams{
ChartID: chartID, Name: "Spring Gala",
})
eventKey := event["meta"].(map[string]any)["key"].(string)
// 3. Sell four seats over the phone.
held, err := client.Inventory.HoldBestAvailable(ctx, eventKey,
seatlayer.BestAvailableParams{Qty: 4})
// … take payment against held["items"], which carry authoritative prices …
client.Inventory.Book(ctx, eventKey, seatlayer.BookParams{
HoldID: held["holdId"].(string), BookingRef: "order-8842",
})# 1. Provision a venue for a new organiser from one of your templates.
chart = client.charts.copy("c_template_arena")["meta"]
client.charts.publish(chart["id"])
# 2. Create an event on it.
event = client.events.create(chart_id: chart["id"], name: "Spring Gala")["meta"]
# 3. Sell four seats over the phone.
held = client.inventory.hold_best_available(event["key"], qty: 4)
# … take payment against held["items"], which carry authoritative prices …
client.inventory.book(event["key"], hold_id: held["holdId"], booking_ref: "order-8842")// 1. Provision a venue for a new organiser from one of your templates.
var chart = (IReadOnlyDictionary<string, object?>)(await client.Charts.CopyAsync("c_template_arena"))["meta"]!;
await client.Charts.PublishAsync((string)chart["id"]!);
// 2. Create an event on it.
var created = await client.Events.CreateAsync((string)chart["id"]!, name: "Spring Gala");
var eventKey = (string)((IReadOnlyDictionary<string, object?>)created["meta"]!)["key"]!;
// 3. Sell four seats over the phone.
var held = await client.Inventory.HoldBestAvailableAsync(eventKey, new BestAvailableRequest { Qty = 4 });
// … take payment against held["items"], which carry authoritative prices …
await client.Inventory.BookAsync(eventKey, (string)held["holdId"]!, bookingRef: "order-8842");Test and live keys
A key carries its own mode. sk_test_… keys can only act on test-mode events and
sk_live_… only on live ones; crossing them answers 403 mode_mismatch. The
client exposes its mode so you can refuse to boot the wrong way round.
if (process.env.NODE_ENV === 'production' && seatlayer.mode !== 'live') {
throw new Error('Refusing to boot production against test-mode seating data.');
}if os.environ.get("ENV") == "production" and seatlayer.mode != "live":
raise RuntimeError("Refusing to boot production against test-mode seating data.")if (getenv('APP_ENV') === 'production' && $seatlayer->mode !== 'live') {
throw new RuntimeException('Refusing to boot production against test-mode seating data.');
}if ("production".equals(System.getenv("ENV")) && !"live".equals(seatlayer.mode())) {
throw new IllegalStateException("Refusing to boot production against test-mode seating data.");
}if os.Getenv("ENV") == "production" && client.Mode() != "live" {
return errors.New("refusing to boot production against test-mode seating data")
}raise "Refusing to boot production against test-mode seating data." if
ENV["RAILS_ENV"] == "production" && client.mode != "live"if (env.IsProduction() && client.Mode != "live")
{
throw new InvalidOperationException("Refusing to boot production against test-mode seating data.");
}A publishable pk_ key passed here is rejected at construction with a message
naming the mistake, rather than failing as a 401 three round-trips later.
Listing large accounts
list() returns one page plus a nextCursor. When you want everything, the
listAll helpers page for you and yield as they go — an iterator rather than an
array, because the point of paginating is to not hold an unbounded list in
memory.
for await (const event of seatlayer.events.listAll()) {
await sync(event);
}for event in seatlayer.events.list_all():
sync(event)foreach ($seatlayer->events->listAll() as $event) {
sync($event);
}for (Map<String, Object> event : seatlayer.events().listAll()) {
sync(event);
}for event, err := range client.Events.All(ctx, nil) {
if err != nil {
return err
}
sync(event)
}client.events.list_all do |event|
sync(event)
endawait foreach (var seatEvent in client.Events.ListAllAsync())
{
await SyncAsync(seatEvent);
}Walking the whole catalogue also drops the per-event availability counts, which cost one internal round-trip each — exactly the cost pagination exists to avoid.
Source
| Language | Package | Repository |
|---|---|---|
| Node.js | @seatlayer/server |
seatlayer/seatlayer-node |
| Python | seatlayer |
seatlayer/seatlayer-python |
| PHP | seatlayer/seatlayer-php |
seatlayer/seatlayer-php |
| Java | io.seatlayer:seatlayer-java |
seatlayer/seatlayer-java |
| Go | github.com/seatlayer/seatlayer-go |
seatlayer/seatlayer-go |
| Ruby | seatlayer |
seatlayer/seatlayer-ruby |
| .NET | SeatLayer |
seatlayer/seatlayer-dotnet |
Both are MIT licensed. Working in another language? The API is plain HTTP and JSON — start from the server API reference and the OpenAPI description.