seatlayer 0.3.0 → 0.5.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: 07a3046d14b181d770f7dddf673476c7b0d695d31d725adc042555afa145c144
4
- data.tar.gz: 5923f05115c9741d3ab20ac8598616351eeac43d74a91b5da0c590275f0105b0
3
+ metadata.gz: 0ab55cb5752c5ddc06db43cfb1653248cebf2b0bd6c8b06be7b308653ca7f5cf
4
+ data.tar.gz: 44ec1f8c5e9f9da17e76c7e5913d1d1929ed4ca8be31d2fed6d24fc9d8521fe9
5
5
  SHA512:
6
- metadata.gz: d9b16c52a2dad95aab45a2d0d75dad7be100ddb60497b554ce9adc1a86dacdab4e8a6dd273314ba67dd93a7156eebb5984550ebf9a5ad8c2cdc519a356ccf70a
7
- data.tar.gz: 0fbe576e636045d4cdfc6ff3cffcbd16403fc3e595913832d352bdcebe66cc03fc128dcc2ab7bca3fc490558e099c77838a4fe6a88d54f66305c9a7928ea0ea8
6
+ metadata.gz: f1252c05e9535563f4459383151d4dd8b8d9699766fbbce0fe453f21e020b310fe6d315f176aa071b3282af1f390a9cc1fd78b3d1efbdbe12d8f67f71249375f
7
+ data.tar.gz: 11f5318bd9b583d2b818446569d7f6b33285966a5dd7544045a03e2dad82a3325c685b51ae290a754a60182c067122eb8bb0999c03b0fe3403919eb21cfd1178
data/CHANGELOG.md CHANGED
@@ -2,8 +2,22 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.5.0 — 2026-08-21
6
+
7
+ - Added `performance_groups`, the trusted server resource for fixed two-to-eight
8
+ performance runs. It creates and activates groups, mints one-time browser
9
+ access, retrieves authoritative group holds, and confirms bookings with
10
+ stable action and order references. Browser-only group routes remain outside
11
+ this secret-key SDK.
12
+
13
+ - Added `templates.instantiate_template` and the ticket-release lifecycle on
14
+ `events` (`list_ticket_releases`, `update_ticket_releases`, and
15
+ `close_ticket_release`). Template instantiation sends `{}` when no overrides
16
+ are supplied, URI-escapes identifiers, and uses exact-response replay;
17
+ ticket-release writes remain single-attempt.
18
+
5
19
  - **Security/reliability:** Mutations now default to a single attempt. Automatic header-replay
6
- retries are limited to chart create/copy, event create, and workspace create, preventing
20
+ retries are limited to chart create/copy, template instantiation, event create, and workspace create, preventing
7
21
  transient failures from duplicating holds or best-available results and from issuing extra
8
22
  show-once credentials.
9
23
  - Aligned event, inventory, webhook, manage-session, and Designer requests with the generated
data/README.md CHANGED
@@ -30,8 +30,8 @@ require "seatlayer"
30
30
 
31
31
  client = SeatLayer::Client.new(ENV.fetch("SEATLAYER_SECRET_KEY"))
32
32
 
33
- # 1. Provision a venue for a new organiser from one of your templates.
34
- chart = client.charts.copy("c_template_arena")["meta"]
33
+ # 1. Provision a venue for a new organiser from a public template.
34
+ chart = client.templates.instantiate_template("arena-standard")["meta"]
35
35
  client.charts.publish(chart["id"])
36
36
 
37
37
  # 2. Create an event on it.
@@ -252,11 +252,12 @@ error carries `status`, `code`, `body` and `request_id` — quote the request id
252
252
  ## Reliability
253
253
 
254
254
  **Retries.** Reads (`GET`/`HEAD`) retry 429, 408 and 5xx with exponential backoff and full jitter;
255
- `Retry-After` wins when the server sends it. Automatic mutation retries are limited to the four
256
- operations backed by exact response replay: `charts.create`, `charts.copy`, `events.create`, and
257
- `workspaces.create`. Other 4xx responses are never retried.
255
+ `Retry-After` wins when the server sends it. Automatic mutation retries are limited to the five
256
+ operations backed by exact response replay: `charts.create`, `charts.copy`,
257
+ `templates.instantiate_template`, `events.create`, and `workspaces.create`. Other 4xx responses
258
+ are never retried.
258
259
 
259
- **Idempotency.** Those four replay-backed operations carry an `Idempotency-Key`, generated when you
260
+ **Idempotency.** Those five replay-backed operations carry an `Idempotency-Key`, generated when you
260
261
  do not supply one and reused across attempts. Other mutations are single-attempt and receive no
261
262
  automatic key. A caller-supplied key is forwarded but does not enable retries. This includes
262
263
  inventory holds and bookings, show-once credential or secret creation, unsupported operations, and
@@ -289,7 +290,8 @@ client.request("POST", "/v1/events/ev_1/some-new-route", body: { "qty" => 2 })
289
290
  | Resource | Methods |
290
291
  | --- | --- |
291
292
  | `charts` | `list` `list_all` `create` `retrieve` `update` `delete` `copy` `archive` `unarchive` `publish` |
292
- | `events` | `list` `list_all` `create` `retrieve` `update` `delete` `update_poster` `delete_poster` `update_chart` `close` `reopen` `archive` `retrieve_hold_ttl` `update_hold_ttl` `retrieve_report` `retrieve_log` |
293
+ | `templates` | `instantiate_template` |
294
+ | `events` | `list` `list_all` `create` `retrieve` `update` `delete` `update_poster` `delete_poster` `update_chart` `close` `reopen` `archive` `retrieve_hold_ttl` `update_hold_ttl` `list_ticket_releases` `update_ticket_releases` `close_ticket_release` `retrieve_report` `retrieve_log` |
293
295
  | `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` |
294
296
  | `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` |
295
297
  | `sessions` | `create_manage_session` `revoke_manage_session` `create_designer_session` `revoke_designer_session` |
@@ -337,7 +339,7 @@ the public manifest, not just from the wrapper.
337
339
  | Node.js (server) | [`@seatlayer/server`](https://www.npmjs.com/package/@seatlayer/server) |
338
340
  | Python (server) | [`seatlayer`](https://pypi.org/project/seatlayer/) |
339
341
  | PHP (server) | [`seatlayer/seatlayer-php`](https://packagist.org/packages/seatlayer/seatlayer-php) |
340
- | Java (server) | [`io.seatlayer:seatlayer-java`](https://central.sonatype.com/artifact/io.seatlayer/seatlayer-java/0.1.0) |
342
+ | Java (server) | [`io.seatlayer:seatlayer-java`](https://central.sonatype.com/artifact/io.seatlayer/seatlayer-java/0.3.0) |
341
343
  | Go (server) | [`github.com/seatlayer/seatlayer-go`](https://pkg.go.dev/github.com/seatlayer/seatlayer-go) |
342
344
  | .NET (server) | [`SeatLayer`](https://www.nuget.org/packages/SeatLayer) |
343
345
 
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SeatLayer
4
+ # Fixed multi-performance runs. This is a secret-key surface: mint the
5
+ # browser token here, then give that token (never this client) to the
6
+ # PerformanceGroupPicker in the browser SDK.
7
+ class PerformanceGroups < Resource
8
+ # One page of fixed runs.
9
+ def list(workspace_id: nil, external_ref: nil, state: nil, limit: nil, cursor: nil)
10
+ @client.get("/v1/performance-groups", compact({ "workspaceId" => workspace_id,
11
+ "externalRef" => external_ref,
12
+ "state" => state, "limit" => limit,
13
+ "cursor" => cursor }))
14
+ end
15
+
16
+ # Create a draft from two to eight compatible assigned-seat events. The
17
+ # server validates compatibility and exactly replays a request with the
18
+ # same idempotency key.
19
+ def create(name:, event_keys:, external_ref: UNSET, idempotency_key: nil)
20
+ body = { "name" => name, "eventKeys" => event_keys }
21
+ body.merge!(supplied({ "externalRef" => external_ref }))
22
+ @client.post("/v1/performance-groups", body, idempotency_key: idempotency_key,
23
+ retry_policy: :header_replay)
24
+ end
25
+
26
+ def retrieve(performance_group_key)
27
+ @client.get(path(performance_group_key))
28
+ end
29
+
30
+ # Only a draft can be deleted. Activated runs retain their audit identity.
31
+ def delete(performance_group_key)
32
+ @client.delete(path(performance_group_key))
33
+ end
34
+
35
+ # Starts lifecycle coordination. When its response contains a non-terminal
36
+ # lifecycle operation, poll +retrieve_lifecycle+ until it completes.
37
+ def activate(performance_group_key, expected_revision:)
38
+ @client.post(path(performance_group_key, "/activate"),
39
+ { "expectedRevision" => expected_revision })
40
+ end
41
+
42
+ # Stops new group sales. Poll +retrieve_lifecycle+ while the close remains pending.
43
+ def close(performance_group_key, expected_revision:)
44
+ @client.post(path(performance_group_key, "/close"),
45
+ { "expectedRevision" => expected_revision })
46
+ end
47
+
48
+ def retrieve_lifecycle(performance_group_key, operation_id)
49
+ @client.get(path(performance_group_key, "/lifecycle/#{encode(operation_id)}"))
50
+ end
51
+
52
+ # Reveals a one-time, origin-bound browser bearer. It intentionally remains
53
+ # single-attempt: a retry could create a token whose only reveal is lost.
54
+ def create_buyer_access_session(performance_group_key, allowed_origin:, include_public:,
55
+ channel_ids_by_event: nil, expires_in_seconds: nil,
56
+ max_quantity: UNSET, buyer_ref: UNSET, partner_ref: UNSET)
57
+ body = compact({ "allowedOrigin" => allowed_origin, "includePublic" => include_public,
58
+ "channelIdsByEvent" => channel_ids_by_event,
59
+ "expiresInSeconds" => expires_in_seconds })
60
+ body.merge!(supplied({ "maxQuantity" => max_quantity, "buyerRef" => buyer_ref,
61
+ "partnerRef" => partner_ref }))
62
+ @client.post(path(performance_group_key, "/buyer-access-sessions"), body)
63
+ end
64
+
65
+ # Token records only: the bearer value is never returned again.
66
+ def list_buyer_access_sessions(performance_group_key, limit: nil)
67
+ @client.get(path(performance_group_key, "/buyer-access-sessions"), { "limit" => limit })
68
+ end
69
+
70
+ def revoke_buyer_access_session(performance_group_key, session_id)
71
+ @client.delete(path(performance_group_key, "/buyer-access-sessions/#{encode(session_id)}"))
72
+ end
73
+
74
+ # Read the trusted server projection before charging; do not price from
75
+ # client input or the picker state.
76
+ def retrieve_hold(performance_group_key, operation_id)
77
+ @client.get(path(performance_group_key, "/holds/#{encode(operation_id)}"))
78
+ end
79
+
80
+ # Confirm external payment for a committed hold. Keep both identifiers
81
+ # stable, and poll +retrieve_booking+ if the response is +book_pending+.
82
+ def book_hold(performance_group_key, operation_id, book_action_id:, booking_ref:)
83
+ @client.post(path(performance_group_key, "/holds/#{encode(operation_id)}/book"),
84
+ { "bookActionId" => book_action_id,
85
+ "bookingRef" => normalise_booking_ref(booking_ref) })
86
+ end
87
+
88
+ def retrieve_booking(performance_group_key, action_id)
89
+ @client.get(path(performance_group_key, "/bookings/#{encode(action_id)}"))
90
+ end
91
+
92
+ private
93
+
94
+ def path(performance_group_key, suffix = "")
95
+ "/v1/performance-groups/#{encode(performance_group_key)}#{suffix}"
96
+ end
97
+
98
+ def normalise_booking_ref(booking_ref)
99
+ value = booking_ref&.strip
100
+ return value unless value.nil? || value.empty?
101
+
102
+ raise ArgumentError, "booking_ref is required and must be a non-empty stable reference"
103
+ end
104
+ end
105
+ end
@@ -129,6 +129,23 @@ module SeatLayer
129
129
  end
130
130
  end
131
131
 
132
+ # Published SeatLayer catalogue templates.
133
+ #
134
+ # Instantiation creates an independent draft chart. Publish that returned
135
+ # chart before creating an event from it.
136
+ class Templates < Resource
137
+ # Instantiate a public template into a new draft chart.
138
+ #
139
+ # +fields+ intentionally defaults to an empty Hash: the API requires a JSON
140
+ # object even when there are no overrides, and JSON.generate({}) is `{}`.
141
+ def instantiate_template(template_id, fields: {}, idempotency_key: nil)
142
+ @client.post(
143
+ "/v1/templates/#{encode(template_id)}/instantiate", fields,
144
+ idempotency_key: idempotency_key, retry_policy: :header_replay
145
+ )
146
+ end
147
+ end
148
+
132
149
  # Event lifecycle, metadata and reports.
133
150
  class Events < Resource
134
151
  # One page of events.
@@ -225,6 +242,21 @@ module SeatLayer
225
242
  @client.post("/v1/events/#{encode(event_key)}/hold-ttl", { "holdTtlMs" => hold_ttl_ms })
226
243
  end
227
244
 
245
+ def list_ticket_releases(event_key)
246
+ @client.get("/v1/events/#{encode(event_key)}/releases")
247
+ end
248
+
249
+ # Replace every ticket release for an event. This stays single-attempt: the
250
+ # route does not promise exact idempotent-response replay.
251
+ def update_ticket_releases(event_key, releases:)
252
+ @client.put("/v1/events/#{encode(event_key)}/releases", { "releases" => releases })
253
+ end
254
+
255
+ # Close one release while preserving its audit provenance.
256
+ def close_ticket_release(event_key, release_id)
257
+ @client.post("/v1/events/#{encode(event_key)}/releases/#{encode(release_id)}/close")
258
+ end
259
+
228
260
  def retrieve_report(event_key)
229
261
  @client.get("/v1/events/#{encode(event_key)}/report")
230
262
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SeatLayer
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/seatlayer.rb CHANGED
@@ -6,6 +6,7 @@ require_relative "seatlayer/http_client"
6
6
  require_relative "seatlayer/resources"
7
7
  require_relative "seatlayer/inventory"
8
8
  require_relative "seatlayer/channels"
9
+ require_relative "seatlayer/performance_groups"
9
10
  require_relative "seatlayer/webhook"
10
11
 
11
12
  # Official Ruby server SDK for the SeatLayer reserved-seating API.
@@ -19,7 +20,8 @@ require_relative "seatlayer/webhook"
19
20
  module SeatLayer
20
21
  # The SeatLayer client.
21
22
  class Client
22
- attr_reader :charts, :events, :inventory, :channels, :sessions, :webhooks, :workspaces
23
+ attr_reader :charts, :events, :inventory, :channels, :performance_groups, :sessions,
24
+ :webhooks, :workspaces, :templates
23
25
 
24
26
  def initialize(secret_key, base_url: HTTPClient::DEFAULT_BASE_URL,
25
27
  max_retries: HTTPClient::DEFAULT_MAX_RETRIES,
@@ -31,7 +33,9 @@ module SeatLayer
31
33
  @events = Events.new(@http)
32
34
  @inventory = Inventory.new(@http)
33
35
  @channels = Channels.new(@http)
36
+ @performance_groups = PerformanceGroups.new(@http)
34
37
  @sessions = Sessions.new(@http)
38
+ @templates = Templates.new(@http)
35
39
  @webhooks = Webhooks.new(@http)
36
40
  @workspaces = Workspaces.new(@http)
37
41
  end
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.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SeatLayer
@@ -25,6 +25,7 @@ files:
25
25
  - lib/seatlayer/errors.rb
26
26
  - lib/seatlayer/http_client.rb
27
27
  - lib/seatlayer/inventory.rb
28
+ - lib/seatlayer/performance_groups.rb
28
29
  - lib/seatlayer/resources.rb
29
30
  - lib/seatlayer/version.rb
30
31
  - lib/seatlayer/webhook.rb