---
title: "Install a server SDK"
description: "Official SeatLayer server SDKs for Node.js, Python, PHP, Java, Go, Ruby and .NET, with idempotency, retries and typed errors built in."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.seatlayer.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Install a server SDK

The server SDKs wrap the [server API](/server-api/events/) 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.

> **Server-side only**
>
> These packages authenticate with your secret key. Never bundle one into a
> browser, a mobile app, or anything a ticket buyer can open. Browser surfaces
> get short-lived scoped tokens that you mint — see
> [embed sessions](/platform/embed-sessions/).

## Install

### Node.js

```bash
npm install @seatlayer/server
```

Requires Node 20.19.4 or newer. No runtime dependencies.

```ts title="Create a client"
import { SeatLayer } from '@seatlayer/server';

const seatlayer = new SeatLayer(process.env.SEATLAYER_SECRET_KEY!);
```
### Python

```bash
pip install seatlayer
```

Requires Python 3.10 or newer. No runtime dependencies.

```python title="Create a client"
import os
from seatlayer import SeatLayer

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

```bash
composer require seatlayer/seatlayer-php
```

Requires PHP 8.1 or newer with `ext-curl`. No Composer dependencies.

```php title="Create a client"
use SeatLayer\SeatLayer;

$seatlayer = new SeatLayer(getenv('SEATLAYER_SECRET_KEY'));
```
### Java

```xml
<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.

```java title="Create a client"
import io.seatlayer.SeatLayer;

SeatLayer seatlayer = new SeatLayer(System.getenv("SEATLAYER_SECRET_KEY"));
```
### Go

```bash
go get github.com/seatlayer/seatlayer-go
```

Requires Go 1.23 or newer. No dependencies — standard library only.

```go title="Create a client"
import "github.com/seatlayer/seatlayer-go"

client, err := seatlayer.New(os.Getenv("SEATLAYER_SECRET_KEY"))
```
### Ruby

```bash
gem install seatlayer
```

Requires Ruby 3.0 or newer. No runtime dependencies.

```ruby title="Create a client"
require "seatlayer"

client = SeatLayer::Client.new(ENV.fetch("SEATLAYER_SECRET_KEY"))
```
### .NET

```bash
dotnet add package SeatLayer
```

Requires .NET 8 or newer. No package dependencies.

```csharp title="Create a client"
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.

### Node.js

```ts
// 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',
});
```
### Python

```python
# 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"
)
```
### PHP

```php
// 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');
```
### Java

```java
// 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");
```
### Go

```go
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",
})
```
### Ruby

```ruby
# 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")
```
### .NET

```csharp
// 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.

### Node.js

```ts
if (process.env.NODE_ENV === 'production' && seatlayer.mode !== 'live') {
  throw new Error('Refusing to boot production against test-mode seating data.');
}
```
### Python

```python
if os.environ.get("ENV") == "production" and seatlayer.mode != "live":
    raise RuntimeError("Refusing to boot production against test-mode seating data.")
```
### PHP

```php
if (getenv('APP_ENV') === 'production' && $seatlayer->mode !== 'live') {
    throw new RuntimeException('Refusing to boot production against test-mode seating data.');
}
```
### Java

```java
if ("production".equals(System.getenv("ENV")) && !"live".equals(seatlayer.mode())) {
    throw new IllegalStateException("Refusing to boot production against test-mode seating data.");
}
```
### Go

```go
if os.Getenv("ENV") == "production" && client.Mode() != "live" {
    return errors.New("refusing to boot production against test-mode seating data")
}
```
### Ruby

```ruby
raise "Refusing to boot production against test-mode seating data." if
  ENV["RAILS_ENV"] == "production" && client.mode != "live"
```
### .NET

```csharp
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.

### Node.js

```ts
for await (const event of seatlayer.events.listAll()) {
  await sync(event);
}
```
### Python

```python
for event in seatlayer.events.list_all():
    sync(event)
```
### PHP

```php
foreach ($seatlayer->events->listAll() as $event) {
    sync($event);
}
```
### Java

```java
for (Map<String, Object> event : seatlayer.events().listAll()) {
    sync(event);
}
```
### Go

```go
for event, err := range client.Events.All(ctx, nil) {
    if err != nil {
        return err
    }
    sync(event)
}
```
### Ruby

```ruby
client.events.list_all do |event|
  sync(event)
end
```
### .NET

```csharp
await 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](https://github.com/seatlayer/seatlayer-node) |
| Python | `seatlayer` | [seatlayer/seatlayer-python](https://github.com/seatlayer/seatlayer-python) |
| PHP | `seatlayer/seatlayer-php` | [seatlayer/seatlayer-php](https://github.com/seatlayer/seatlayer-php) |
| Java | `io.seatlayer:seatlayer-java` | [seatlayer/seatlayer-java](https://github.com/seatlayer/seatlayer-java) |
| Go | `github.com/seatlayer/seatlayer-go` | [seatlayer/seatlayer-go](https://github.com/seatlayer/seatlayer-go) |
| Ruby | `seatlayer` | [seatlayer/seatlayer-ruby](https://github.com/seatlayer/seatlayer-ruby) |
| .NET | `SeatLayer` | [seatlayer/seatlayer-dotnet](https://github.com/seatlayer/seatlayer-dotnet) |

Both are MIT licensed. Working in another language? The API is plain HTTP and
JSON — start from the [server API reference](/server-api/events/) and the
[OpenAPI description](https://docs.seatlayer.io/openapi.json).

Source: https://docs.seatlayer.io/server-sdk/install/index.mdx
