seatlayer 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6aa9b6c1e8985b0f261f5e0bf7743148a2fcf5dc3c9f9585b482d78e24b492a5
4
- data.tar.gz: 70b5fb6fd1f3eb7cbed9d80917a7e1ef56d212c1bafb924e97a961878e71e906
3
+ metadata.gz: 0d51f4f004070a397c4ac39e08f183df2c8a72dca70c35ce1ef61042d247b30c
4
+ data.tar.gz: a37f6748bf6d656ee1b5daa4902609678bf039992766c9fa9dce7742e4ee47f8
5
5
  SHA512:
6
- metadata.gz: 21ab307cdbc1676e6d463c3bb990ebe886f5d38906b988d129d1f5a722c4199ffeb80938b582e3d7b906fe287b9ea5a190dda5043200f27f569fc0c2d2aab7f1
7
- data.tar.gz: d4d261bb2e9240a49bf373fd9faa63896fac970f9fb7a9f5c7583becce1c26a445f45ff623115463e833587f0a58be1762990864dba1066139c384d62d85005e
6
+ metadata.gz: 25dfd25bc366329bb38f52f59522dde781225df50fcfe5b6c523e9b7af3ab54d16aa22b6c6ea022b8e65d5e98b1a8dcbabd04c3716026cc257918c0fa3b9ce73
7
+ data.tar.gz: 98af014bd435e3f5ee7233c5b3c8feeb9b4ed950e9df55b5c1be4ec1dc94506657cb83c0149202c9421bbf223efb760ad15fb713ea88797222326b435236f41c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 — 2026-08-12
4
+
5
+ - Added the `channels` resource for allocation management, access previews,
6
+ channel reporting, and origin-bound buyer access sessions.
7
+ - Added inventory booking lifecycle reads and channel-aware hold/book options.
8
+ - Require a non-empty stable booking reference for booking, unbooking, and
9
+ booking-detail reads.
10
+ - Expanded registry, documentation, and agent-discovery links in the README.
11
+
3
12
  ## 0.1.0 — 2026-08-04
4
13
 
5
14
  First public release: RubyGems `seatlayer`.
data/README.md CHANGED
@@ -74,6 +74,27 @@ client.inventory.book_best_available(event_key, qty: 2, booking_ref: "phone-1183
74
74
  client.inventory.box_office_book(event_key, labels: ["A-1", "A-2"], booking_ref: "comp-14")
75
75
  ```
76
76
 
77
+ ## Private and partner sales
78
+
79
+ Channels split event inventory into explicit allocations. A channel id is
80
+ reporting/routing metadata, not browser authority. Authenticate the buyer in
81
+ your backend and mint a short-lived token restricted to the event, origin, and
82
+ allowed allocations:
83
+
84
+ ```ruby
85
+ access = client.channels.create_buyer_access_session(
86
+ event_key,
87
+ channel_ids: ["chn_partner_a"],
88
+ include_public: false,
89
+ allowed_origin: "https://tickets.example"
90
+ )
91
+ # Return access["token"] to the in-memory buyerAccessTokenProvider only.
92
+ ```
93
+
94
+ Never log or persist the returned `bse_…` bearer. Allocation setup, previews,
95
+ pause/archive controls, audit-safe session listing, and channel reports are on
96
+ `client.channels`.
97
+
77
98
  ## Listing and pagination
78
99
 
79
100
  `list` returns one page plus a `nextCursor`. `list_all` pages for you and returns a lazy
@@ -243,7 +264,8 @@ client.request("POST", "/v1/events/ev_1/some-new-route", body: { "qty" => 2 })
243
264
  | --- | --- |
244
265
  | `charts` | `list` `list_all` `create` `retrieve` `update` `delete` `copy` `archive` `unarchive` `publish` |
245
266
  | `events` | `list` `list_all` `create` `retrieve` `update` `delete` `update_chart` `close` `reopen` `archive` `retrieve_hold_ttl` `update_hold_ttl` `retrieve_report` `retrieve_log` |
246
- | `inventory` | `hold` `hold_best_available` `book_best_available` `extend_hold` `retrieve_hold` `release` `book` `box_office_book` `unbook` `block` `unblock` `unblock_all` `retrieve_availability` `update_availability` |
267
+ | `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` |
268
+ | `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` |
247
269
  | `sessions` | `create_manage_session` `revoke_manage_session` `create_designer_session` `revoke_designer_session` |
248
270
  | `webhooks` | `list` `create` `update` `delete` `list_deliveries` |
249
271
  | `workspaces` | `list` `create` `retrieve` `update` |
@@ -273,24 +295,25 @@ the public manifest, not just from the wrapper.
273
295
  - [Webhook verification](https://docs.seatlayer.io/server-sdk/webhooks/)
274
296
  - [Server API reference](https://docs.seatlayer.io/server-api/events/)
275
297
  - [OpenAPI description](https://docs.seatlayer.io/openapi.json)
298
+ - [Agent-readable documentation](https://docs.seatlayer.io/llms.txt)
276
299
  - [SeatLayer GitHub organization](https://github.com/seatlayer)
277
300
 
278
301
  ### Other SeatLayer SDKs
279
302
 
280
303
  | Surface | Package |
281
304
  |---|---|
282
- | Browser (vanilla) | [`@seatlayer/js`](https://github.com/seatlayer/seatlayer-sdk) |
283
- | React | [`@seatlayer/react`](https://github.com/seatlayer/seatlayer-sdk) |
284
- | React Native | [`@seatlayer/react-native`](https://github.com/seatlayer/seatlayer-react-native) |
305
+ | Browser (vanilla) | [`@seatlayer/js`](https://www.npmjs.com/package/@seatlayer/js) |
306
+ | React | [`@seatlayer/react`](https://www.npmjs.com/package/@seatlayer/react) |
307
+ | React Native | [`@seatlayer/react-native`](https://www.npmjs.com/package/@seatlayer/react-native) |
285
308
  | iOS | [`seatlayer-ios`](https://github.com/seatlayer/seatlayer-ios) |
286
309
  | Android | [`seatlayer-android`](https://github.com/seatlayer/seatlayer-android) |
287
- | Flutter | [`seatlayer_flutter`](https://github.com/seatlayer/seatlayer-flutter) |
288
- | Node.js (server) | [`@seatlayer/server`](https://github.com/seatlayer/seatlayer-node) |
289
- | Python (server) | [`seatlayer`](https://github.com/seatlayer/seatlayer-python) |
290
- | PHP (server) | [`seatlayer/seatlayer-php`](https://github.com/seatlayer/seatlayer-php) |
291
- | Java (server) | [`io.seatlayer:seatlayer-java`](https://github.com/seatlayer/seatlayer-java) |
292
- | Go (server) | [`github.com/seatlayer/seatlayer-go`](https://github.com/seatlayer/seatlayer-go) |
293
- | .NET (server) | [`SeatLayer`](https://github.com/seatlayer/seatlayer-dotnet) |
310
+ | Flutter | [`seatlayer`](https://pub.dev/packages/seatlayer) |
311
+ | Node.js (server) | [`@seatlayer/server`](https://www.npmjs.com/package/@seatlayer/server) |
312
+ | Python (server) | [`seatlayer`](https://pypi.org/project/seatlayer/) |
313
+ | PHP (server) | [`seatlayer/seatlayer-php`](https://packagist.org/packages/seatlayer/seatlayer-php) |
314
+ | Java (server) | [`io.seatlayer:seatlayer-java`](https://central.sonatype.com/artifact/io.seatlayer/seatlayer-java/0.1.0) |
315
+ | Go (server) | [`github.com/seatlayer/seatlayer-go`](https://pkg.go.dev/github.com/seatlayer/seatlayer-go) |
316
+ | .NET (server) | [`SeatLayer`](https://www.nuget.org/packages/SeatLayer) |
294
317
 
295
318
  ## Development
296
319
 
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SeatLayer
4
+ # Private allocations, reporting, and origin-bound buyer access.
5
+ class Channels < Resource
6
+ def list_channels(event_key, include_archived: false)
7
+ query = include_archived ? { "includeArchived" => "1" } : nil
8
+ @client.get(path(event_key), query)
9
+ end
10
+
11
+ def create_channel(event_key, name:, color: nil, marker: nil, external_ref: nil,
12
+ access_intent: nil, reason: nil, idempotency_key: nil)
13
+ body = compact({ "name" => name, "color" => color, "marker" => marker,
14
+ "externalRef" => external_ref, "accessIntent" => access_intent,
15
+ "reason" => reason })
16
+ @client.post(path(event_key), body, idempotency_key: idempotency_key)
17
+ end
18
+
19
+ def update_channel(event_key, channel_id, name: nil, access_intent: nil,
20
+ acknowledge_live_access: nil, reason: nil)
21
+ body = compact({ "name" => name, "accessIntent" => access_intent,
22
+ "acknowledgeLiveAccess" => acknowledge_live_access, "reason" => reason })
23
+ @client.patch(path(event_key, "/#{encode(channel_id)}"), body)
24
+ end
25
+
26
+ def update_assignments(event_key, labels:, assignment_version:, target_channel_id: nil,
27
+ reason: nil, idempotency_key: nil)
28
+ body = compact({ "targetChannelId" => target_channel_id, "labels" => labels,
29
+ "assignmentVersion" => assignment_version, "reason" => reason })
30
+ body["targetChannelId"] = nil if target_channel_id.nil?
31
+ @client.post(path(event_key, "/assignments"), body, idempotency_key: idempotency_key)
32
+ end
33
+
34
+ def list_allocation(event_key, after_label: nil, limit: nil)
35
+ @client.get(path(event_key, "/allocation"), compact({ "afterLabel" => after_label, "limit" => limit }))
36
+ end
37
+
38
+ def retrieve_access_preview(event_key, channel_ids: nil, include_public: nil)
39
+ include_public_value = if include_public.nil?
40
+ nil
41
+ else
42
+ include_public ? "1" : "0"
43
+ end
44
+ query = compact({ "channelIds" => channel_ids&.join(","),
45
+ "includePublic" => include_public_value })
46
+ @client.get(path(event_key, "/preview"), query)
47
+ end
48
+
49
+ def retrieve_report(event_key) = @client.get(path(event_key, "/report"))
50
+
51
+ def pause(event_key, channel_id, reason: nil)
52
+ @client.post(path(event_key, "/#{encode(channel_id)}/pause"), compact({ "reason" => reason }))
53
+ end
54
+
55
+ def unpause(event_key, channel_id, reason: nil)
56
+ @client.post(path(event_key, "/#{encode(channel_id)}/unpause"), compact({ "reason" => reason }))
57
+ end
58
+
59
+ def archive(event_key, channel_id, destination:, reason: nil)
60
+ body = compact({ "reason" => reason })
61
+ body["destination"] = destination
62
+ @client.post(path(event_key, "/#{encode(channel_id)}/archive"), body)
63
+ end
64
+
65
+ # Explicit keywords document each security boundary carried by the token.
66
+ # rubocop:disable Metrics/ParameterLists
67
+ def create_buyer_access_session(event_key, include_public:, allowed_origin:, channel_ids: nil,
68
+ expires_in_seconds: nil, max_quantity: nil, buyer_ref: nil,
69
+ partner_ref: nil, client_request_id: nil, idempotency_key: nil)
70
+ body = compact({ "channelIds" => channel_ids, "includePublic" => include_public,
71
+ "allowedOrigin" => allowed_origin, "expiresInSeconds" => expires_in_seconds,
72
+ "maxQuantity" => max_quantity, "buyerRef" => buyer_ref,
73
+ "partnerRef" => partner_ref, "clientRequestId" => client_request_id })
74
+ @client.post("/v1/events/#{encode(event_key)}/buyer-access-sessions", body,
75
+ idempotency_key: idempotency_key)
76
+ end
77
+ # rubocop:enable Metrics/ParameterLists
78
+
79
+ def list_buyer_access_sessions(event_key, state: nil, limit: nil, cursor: nil)
80
+ @client.get("/v1/events/#{encode(event_key)}/buyer-access-sessions",
81
+ compact({ "state" => state, "limit" => limit, "cursor" => cursor }))
82
+ end
83
+
84
+ def revoke_buyer_access_session(event_key, session_id)
85
+ @client.delete("/v1/events/#{encode(event_key)}/buyer-access-sessions/#{encode(session_id)}")
86
+ end
87
+
88
+ private
89
+
90
+ def path(event_key, suffix = "") = "/v1/events/#{encode(event_key)}/channels#{suffix}"
91
+ end
92
+ end
@@ -11,10 +11,16 @@ module SeatLayer
11
11
  # Never price from what the browser tells you. +retrieve_hold+ is the
12
12
  # authoritative answer, which is why it is a separate call.
13
13
  class Inventory < Resource
14
+ # Explicit keywords keep allocation authority visible at every sale call.
15
+ # rubocop:disable Metrics/ParameterLists
14
16
  def hold(event_key, labels: nil, selections: nil, ttl_ms: nil,
15
- replace_hold_id: nil, idempotency_key: nil)
17
+ replace_hold_id: nil, channel_ids: nil, ignore_channel_restrictions: nil,
18
+ reason: nil, idempotency_key: nil)
16
19
  body = compact({ "labels" => labels, "selections" => selections,
17
- "ttlMs" => ttl_ms, "replaceHoldId" => replace_hold_id })
20
+ "ttlMs" => ttl_ms, "replaceHoldId" => replace_hold_id,
21
+ "channelIds" => channel_ids,
22
+ "ignoreChannelRestrictions" => ignore_channel_restrictions,
23
+ "reason" => reason })
18
24
  @client.post(path(event_key, "/hold"), body, idempotency_key: idempotency_key)
19
25
  end
20
26
 
@@ -24,9 +30,13 @@ module SeatLayer
24
30
  # order get the same answer for the same inventory. A +qty+ above the server
25
31
  # cap is clamped, not rejected.
26
32
  def hold_best_available(event_key, qty:, category_key: nil, zone_id: nil,
27
- ttl_ms: nil, idempotency_key: nil)
33
+ ttl_ms: nil, channel_ids: nil, ignore_channel_restrictions: nil,
34
+ reason: nil, idempotency_key: nil)
28
35
  body = compact({ "qty" => qty, "categoryKey" => category_key,
29
- "zoneId" => zone_id, "ttlMs" => ttl_ms })
36
+ "zoneId" => zone_id, "ttlMs" => ttl_ms,
37
+ "channelIds" => channel_ids,
38
+ "ignoreChannelRestrictions" => ignore_channel_restrictions,
39
+ "reason" => reason })
30
40
  @client.post(path(event_key, "/best-available"), body, idempotency_key: idempotency_key)
31
41
  end
32
42
 
@@ -35,11 +45,16 @@ module SeatLayer
35
45
  # Prefer this over hold-then-book when payment is already taken: a failure
36
46
  # between two calls would strand inventory until the TTL expired.
37
47
  def book_best_available(event_key, qty:, booking_ref:, category_key: nil,
38
- zone_id: nil, idempotency_key: nil)
39
- body = compact({ "qty" => qty, "bookingRef" => booking_ref,
40
- "categoryKey" => category_key, "zoneId" => zone_id })
48
+ zone_id: nil, channel_ids: nil, ignore_channel_restrictions: nil,
49
+ reason: nil, idempotency_key: nil)
50
+ body = compact({ "qty" => qty, "bookingRef" => normalise_booking_ref(booking_ref),
51
+ "categoryKey" => category_key, "zoneId" => zone_id,
52
+ "channelIds" => channel_ids,
53
+ "ignoreChannelRestrictions" => ignore_channel_restrictions,
54
+ "reason" => reason })
41
55
  @client.post(path(event_key, "/best-available-book"), body, idempotency_key: idempotency_key)
42
56
  end
57
+ # rubocop:enable Metrics/ParameterLists
43
58
 
44
59
  # Push an active hold's expiry out by a fresh window before it lapses.
45
60
  #
@@ -60,20 +75,26 @@ module SeatLayer
60
75
  @client.post(path(event_key, "/release"), { "labels" => labels, "holdId" => hold_id })
61
76
  end
62
77
 
63
- def book(event_key, hold_id: nil, labels: nil, booking_ref: nil, idempotency_key: nil)
64
- body = compact({ "holdId" => hold_id, "labels" => labels, "bookingRef" => booking_ref })
78
+ def book(event_key, hold_id: nil, labels: nil, booking_ref: nil, channel_ids: nil,
79
+ ignore_channel_restrictions: nil, reason: nil, idempotency_key: nil)
80
+ body = compact({ "holdId" => hold_id, "labels" => labels,
81
+ "bookingRef" => normalise_booking_ref(booking_ref),
82
+ "channelIds" => channel_ids,
83
+ "ignoreChannelRestrictions" => ignore_channel_restrictions,
84
+ "reason" => reason })
65
85
  @client.post(path(event_key, "/book"), body, idempotency_key: idempotency_key)
66
86
  end
67
87
 
68
88
  def box_office_book(event_key, labels:, booking_ref:, idempotency_key: nil)
69
89
  @client.post(path(event_key, "/box-book"),
70
- { "labels" => labels, "bookingRef" => booking_ref },
90
+ { "labels" => labels, "bookingRef" => normalise_booking_ref(booking_ref) },
71
91
  idempotency_key: idempotency_key)
72
92
  end
73
93
 
74
94
  # Reverse a booking. Requires a key with cancel authority.
75
- def unbook(event_key, labels:)
76
- @client.post(path(event_key, "/unbook"), { "labels" => labels })
95
+ def unbook(event_key, labels:, booking_ref:)
96
+ @client.post(path(event_key, "/unbook"),
97
+ { "labels" => labels, "bookingRef" => normalise_booking_ref(booking_ref) })
77
98
  end
78
99
 
79
100
  # Hold inventory back from sale (house seats, production holds).
@@ -97,11 +118,28 @@ module SeatLayer
97
118
  @client.post(path(event_key, "/availability"), fields)
98
119
  end
99
120
 
121
+ # One page of inventory booking lifecycles, newest first.
122
+ def list_bookings(event_key, query: nil, state: nil, limit: nil, cursor: nil)
123
+ @client.get(path(event_key, "/bookings"),
124
+ compact({ "q" => query, "state" => state, "limit" => limit, "cursor" => cursor }))
125
+ end
126
+
127
+ def retrieve_booking(event_key, booking_ref)
128
+ @client.get(path(event_key, "/bookings/#{encode(normalise_booking_ref(booking_ref))}"))
129
+ end
130
+
100
131
  private
101
132
 
102
133
  def path(event_key, suffix)
103
134
  "/v1/events/#{encode(event_key)}#{suffix}"
104
135
  end
136
+
137
+ def normalise_booking_ref(booking_ref)
138
+ value = booking_ref&.strip
139
+ return value unless value.nil? || value.empty?
140
+
141
+ raise ArgumentError, "booking_ref is required and must be a non-empty stable reference"
142
+ end
105
143
  end
106
144
 
107
145
  # Short-lived, origin-bound browser tokens.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SeatLayer
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/seatlayer.rb CHANGED
@@ -5,6 +5,7 @@ require_relative "seatlayer/errors"
5
5
  require_relative "seatlayer/http_client"
6
6
  require_relative "seatlayer/resources"
7
7
  require_relative "seatlayer/inventory"
8
+ require_relative "seatlayer/channels"
8
9
  require_relative "seatlayer/webhook"
9
10
 
10
11
  # Official Ruby server SDK for the SeatLayer reserved-seating API.
@@ -18,7 +19,7 @@ require_relative "seatlayer/webhook"
18
19
  module SeatLayer
19
20
  # The SeatLayer client.
20
21
  class Client
21
- attr_reader :charts, :events, :inventory, :sessions, :webhooks, :workspaces
22
+ attr_reader :charts, :events, :inventory, :channels, :sessions, :webhooks, :workspaces
22
23
 
23
24
  def initialize(secret_key, base_url: HTTPClient::DEFAULT_BASE_URL,
24
25
  max_retries: HTTPClient::DEFAULT_MAX_RETRIES,
@@ -29,6 +30,7 @@ module SeatLayer
29
30
  @charts = Charts.new(@http)
30
31
  @events = Events.new(@http)
31
32
  @inventory = Inventory.new(@http)
33
+ @channels = Channels.new(@http)
32
34
  @sessions = Sessions.new(@http)
33
35
  @webhooks = Webhooks.new(@http)
34
36
  @workspaces = Workspaces.new(@http)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seatlayer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SeatLayer
@@ -21,6 +21,7 @@ files:
21
21
  - LICENSE
22
22
  - README.md
23
23
  - lib/seatlayer.rb
24
+ - lib/seatlayer/channels.rb
24
25
  - lib/seatlayer/errors.rb
25
26
  - lib/seatlayer/http_client.rb
26
27
  - lib/seatlayer/inventory.rb