seatlayer 0.6.1 → 0.7.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: abe617b9eff9a2dbeb571b81e16f8d41096b4cb4d49f35a3461f03b292316a87
4
- data.tar.gz: d901b82e10dd7194b726f7c3d4b8f2d642030a1458c42ae7a851b200a9b0ef9a
3
+ metadata.gz: a65cc789351eb854341bf167fdedbd818035c29639e212df5312597f46e3f23c
4
+ data.tar.gz: 0fc4163ccaedd380264fbc53853c71c476f4bbfdb4f99180663f66c434d94b66
5
5
  SHA512:
6
- metadata.gz: d12c27e2f846f0aca0f95e8cc064723f9ad5ff37a83c64a854529db34a4b4651865011e3634a4eacd48bed9214e8ee64e1bccf162db24ca21d020f035cd6f091
7
- data.tar.gz: e764024fd33ae0ca89714490934a7bbac28a2527a0ccee40a1892b1d2292f6945deb575c28b69665f77fcb04dd2ba987b1ce80cdb0b512236a497386563ee9c3
6
+ metadata.gz: dc59365e346c8a14cf52014ca804f70aa63eabee928214906913c060bf58f4c2edb0dbb3eaec90305096b396bf2257468f444b5d8ba3f154e78f57625b125126
7
+ data.tar.gz: c74c63603bd068dd4566b18b1c940762de0258fbcf186b083608c263ce10ffa8a9934db69a135cdbb0d3c225a4334ecfb15a6f672e6e853c745a11d3cc9eab44
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0 — 2026-08-30
4
+
5
+ - Added coverage for all 48 Fixed Renewable Season server
6
+ operations under `client.seasons`, with exact path encoding and
7
+ operation-specific retry/idempotency behavior.
8
+ - Season allocations are identity-only and the API response declares host
9
+ pricing authority. Buyer rehearsal validation sends no evidence body because
10
+ SeatLayer discovers the retained hold, booking, cancellation, and delivered
11
+ webhook chain automatically.
12
+
3
13
  ## 0.6.1
4
14
 
5
15
  - Documentation only. Refreshes the README, adds frequently asked
data/README.md CHANGED
@@ -12,9 +12,9 @@ allocations, and webhooks through one typed ticketing API client.
12
12
 
13
13
  [`seatlayer` gem on RubyGems](https://rubygems.org/gems/seatlayer) ·
14
14
  [SeatLayer server SDK documentation](https://docs.seatlayer.io/server-sdk/install/) ·
15
- [SeatLayer reserved-seating platform](https://seatlayer.io/) ·
15
+ [SeatLayer developer platform](https://seatlayer.io/developers/) ·
16
16
  [SeatLayer JavaScript seat map SDK](https://www.npmjs.com/package/@seatlayer/js) ·
17
- [Server API reference](https://docs.seatlayer.io/server-api/)
17
+ [Server API reference](https://docs.seatlayer.io/server-api/events/)
18
18
 
19
19
  > **Server-side only.** This gem authenticates with your secret key. Never load it anywhere a
20
20
  > ticket buyer can reach — browser surfaces get short-lived, origin-bound tokens that you mint here.
@@ -57,6 +57,34 @@ Nullable event-create fields distinguish omission from an explicit reset: passin
57
57
 
58
58
  ## Test vs live
59
59
 
60
+ ## Fixed Renewable Seasons
61
+
62
+ Version `0.7.0` exposes all 48 trusted organizer operations through
63
+ `client.seasons`.
64
+
65
+ After the test hold/book/cancel journey and matching webhook deliveries,
66
+ `validate_season_buyer_rehearsal(season_key)` sends no evidence body; SeatLayer
67
+ discovers the retained chain automatically. Retrieved Season holds contain
68
+ inventory identity, not an authoritative amount—your platform owns package
69
+ price, payment, order, tax, refunds, benefits, and ticket or pass delivery.
70
+
71
+ ```ruby
72
+ checked = client.seasons.validate_season(
73
+ source_performance_group_keys: ["pg_subscription_run"]
74
+ )
75
+ draft = client.seasons.create_season(
76
+ name: "2027 subscription",
77
+ source_performance_group_keys: ["pg_subscription_run"],
78
+ idempotency_key: "season-create-2027"
79
+ ).fetch("season")
80
+ ```
81
+
82
+ Treat `202` as accepted work and poll `retrieve_season_lifecycle` with the
83
+ returned operation identity. Buyer-session minting and domain-exact booking,
84
+ cancellation, and renewal actions remain single-attempt; only declared
85
+ header-replay catalogue mutations retry automatically.
86
+
87
+
60
88
  Keys carry their own mode. `sk_test_…` keys can only touch test-mode events and `sk_live_…` only
61
89
  live ones; crossing them returns `403 mode_mismatch`, surfaced as `AuthError` with `mode_mismatch?`.
62
90
 
@@ -101,6 +101,12 @@ module SeatLayer
101
101
  request("POST", path, body: body, idempotency_key: idempotency_key, retry_policy: retry_policy)
102
102
  end
103
103
 
104
+ # Internal path for non-POST mutations backed by exact response replay.
105
+ def mutation_with_header_replay(method, path, body = nil, idempotency_key: nil)
106
+ request(method, path, body: body, idempotency_key: idempotency_key,
107
+ retry_policy: :header_replay)
108
+ end
109
+
104
110
  def put(path, body)
105
111
  request("PUT", path, body: body)
106
112
  end
@@ -0,0 +1,272 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SeatLayer
4
+ # Fixed Renewable Season organizer operations for trusted backends.
5
+ #
6
+ # Browser selection belongs in the distinct SeasonPicker. Give it only the
7
+ # show-once scoped token returned by +create_season_buyer_access_session+.
8
+ class Seasons < Resource
9
+ def list_seasons(workspace_id: nil, structure_state: nil, limit: nil, cursor: nil)
10
+ @client.get("/v1/seasons", compact({ "workspaceId" => workspace_id,
11
+ "structureState" => structure_state,
12
+ "limit" => limit, "cursor" => cursor }))
13
+ end
14
+
15
+ # Read-only compatibility preflight.
16
+ def validate_season(event_keys: nil, source_performance_group_keys: nil)
17
+ @client.post("/v1/seasons/validate",
18
+ selection(event_keys, source_performance_group_keys))
19
+ end
20
+
21
+ def create_season(name:, event_keys: nil, source_performance_group_keys: nil,
22
+ edition: UNSET, idempotency_key: nil)
23
+ body = selection(event_keys, source_performance_group_keys).merge("name" => name)
24
+ body.merge!(supplied({ "edition" => edition }))
25
+ @client.post("/v1/seasons", body, idempotency_key: idempotency_key,
26
+ retry_policy: :header_replay)
27
+ end
28
+
29
+ def retrieve_season(season_key)
30
+ @client.get(path(season_key))
31
+ end
32
+
33
+ def update_season(season_key, expected_revision:, name: nil, edition: UNSET,
34
+ idempotency_key: nil)
35
+ body = compact({ "expectedRevision" => expected_revision, "name" => name })
36
+ body.merge!(supplied({ "edition" => edition }))
37
+ @client.mutation_with_header_replay(
38
+ "PATCH", path(season_key), body, idempotency_key: idempotency_key
39
+ )
40
+ end
41
+
42
+ def delete_season(season_key, idempotency_key: nil)
43
+ @client.mutation_with_header_replay(
44
+ "DELETE", path(season_key), idempotency_key: idempotency_key
45
+ )
46
+ end
47
+
48
+ def activate_season(season_key, expected_revision:)
49
+ lifecycle(season_key, "activate", expected_revision)
50
+ end
51
+
52
+ def close_season(season_key, expected_revision:)
53
+ lifecycle(season_key, "close", expected_revision)
54
+ end
55
+
56
+ def archive_season(season_key, expected_revision:)
57
+ lifecycle(season_key, "archive", expected_revision)
58
+ end
59
+
60
+ def retrieve_season_lifecycle(season_key, operation_id)
61
+ @client.get(path(season_key, "/lifecycle/#{encode(operation_id)}"))
62
+ end
63
+
64
+ def create_season_plan(season_key, name:, event_keys: nil,
65
+ source_performance_group_keys: nil, idempotency_key: nil)
66
+ body = selection(event_keys, source_performance_group_keys).merge("name" => name)
67
+ @client.post(path(season_key, "/plans"), body, idempotency_key: idempotency_key,
68
+ retry_policy: :header_replay)
69
+ end
70
+
71
+ def retrieve_season_plan(season_key, plan_key)
72
+ @client.get(path(season_key, "/plans/#{encode(plan_key)}"))
73
+ end
74
+
75
+ def publish_season_plan(season_key, plan_key, expected_revision:)
76
+ @client.post(path(season_key, "/plans/#{encode(plan_key)}/publish"),
77
+ { "expectedRevision" => expected_revision })
78
+ end
79
+
80
+ def supersede_season_plan(season_key, plan_key, expected_revision:)
81
+ @client.post(path(season_key, "/plans/#{encode(plan_key)}/supersede"),
82
+ { "expectedRevision" => expected_revision })
83
+ end
84
+
85
+ def open_season_sales(season_key, expected_revision:)
86
+ sales(season_key, "open", expected_revision)
87
+ end
88
+
89
+ def pause_season_sales(season_key, expected_revision:)
90
+ sales(season_key, "pause", expected_revision)
91
+ end
92
+
93
+ def resume_season_sales(season_key, expected_revision:)
94
+ sales(season_key, "resume", expected_revision)
95
+ end
96
+
97
+ def end_season_sales(season_key, expected_revision:)
98
+ sales(season_key, "end", expected_revision)
99
+ end
100
+
101
+ def duplicate_season_to_live(season_key, event_keys:, name: nil, idempotency_key: nil)
102
+ @client.post(path(season_key, "/duplicate-to-live"),
103
+ compact({ "eventKeys" => event_keys, "name" => name }),
104
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
105
+ end
106
+
107
+ # Reveal one show-once browser bearer. This call is deliberately single-attempt.
108
+ def create_season_buyer_access_session(season_key, allowed_origin:, include_public:,
109
+ expires_in_seconds: nil, max_quantity: UNSET,
110
+ buyer_ref: UNSET)
111
+ body = compact({ "allowedOrigin" => allowed_origin, "includePublic" => include_public,
112
+ "expiresInSeconds" => expires_in_seconds })
113
+ body.merge!(supplied({ "maxQuantity" => max_quantity, "buyerRef" => buyer_ref }))
114
+ @client.post(path(season_key, "/buyer-access-sessions"), body)
115
+ end
116
+
117
+ def list_season_buyer_access_sessions(season_key, limit: nil)
118
+ @client.get(path(season_key, "/buyer-access-sessions"), compact({ "limit" => limit }))
119
+ end
120
+
121
+ def revoke_season_buyer_access_session(season_key, session_id)
122
+ @client.delete(path(season_key, "/buyer-access-sessions/#{encode(session_id)}"))
123
+ end
124
+
125
+ def retrieve_season_hold(season_key, operation_id)
126
+ @client.get(path(season_key, "/holds/#{encode(operation_id)}"))
127
+ end
128
+
129
+ def book_season_hold(season_key, operation_id, book_action_id:, booking_ref:)
130
+ @client.post(path(season_key, "/holds/#{encode(operation_id)}/book"),
131
+ { "bookActionId" => book_action_id, "bookingRef" => booking_ref })
132
+ end
133
+
134
+ def retrieve_season_booking(season_key, action_id)
135
+ @client.get(path(season_key, "/bookings/#{encode(action_id)}"))
136
+ end
137
+
138
+ def cancel_season_booking(season_key, action_id, cancel_action_id:, booking_ref:,
139
+ plan_activation_id:, right_disposition:)
140
+ @client.post(path(season_key, "/bookings/#{encode(action_id)}/cancel"),
141
+ { "cancelActionId" => cancel_action_id, "bookingRef" => booking_ref,
142
+ "planActivationId" => plan_activation_id,
143
+ "rightDisposition" => right_disposition })
144
+ end
145
+
146
+ def validate_season_buyer_rehearsal(season_key)
147
+ @client.post(path(season_key, "/buyer-rehearsals/validate"))
148
+ end
149
+
150
+ def create_season_holder_import(season_key, successor_plan_activation_id:, rows:,
151
+ dry_run: UNSET, idempotency_key: nil)
152
+ body = { "successorPlanActivationId" => successor_plan_activation_id, "rows" => rows }
153
+ body.merge!(supplied({ "dryRun" => dry_run }))
154
+ @client.post(path(season_key, "/imports"), body, idempotency_key: idempotency_key,
155
+ retry_policy: :header_replay)
156
+ end
157
+
158
+ def retrieve_season_holder_import(season_key, import_id)
159
+ @client.get(path(season_key, "/imports/#{encode(import_id)}"))
160
+ end
161
+
162
+ def create_season_renewal_offers(season_key, deadline_at:,
163
+ successor_plan_activation_id: nil, contract_ids: nil,
164
+ idempotency_key: nil)
165
+ body = compact({ "successorPlanActivationId" => successor_plan_activation_id,
166
+ "deadlineAt" => deadline_at, "contractIds" => contract_ids })
167
+ @client.post(path(season_key, "/renewal-offers"), body,
168
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
169
+ end
170
+
171
+ def list_season_renewal_offers(season_key)
172
+ @client.get(path(season_key, "/renewal-offers"))
173
+ end
174
+
175
+ def retrieve_season_renewal_offer(season_key, offer_id)
176
+ @client.get(path(season_key, "/renewal-offers/#{encode(offer_id)}"))
177
+ end
178
+
179
+ def extend_season_renewal_offer(season_key, offer_id, deadline_at:)
180
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/extend"),
181
+ { "deadlineAt" => deadline_at })
182
+ end
183
+
184
+ def inspect_season_renewal_offer(season_key, offer_id)
185
+ @client.get(path(season_key, "/renewal-offers/#{encode(offer_id)}/inspect"))
186
+ end
187
+
188
+ def commit_season_renewal_offer(season_key, offer_id, commit_action_id:, order_ref:,
189
+ booking_ref:, plan_activation_id:)
190
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/commit"),
191
+ { "commitActionId" => commit_action_id, "orderRef" => order_ref,
192
+ "bookingRef" => booking_ref, "planActivationId" => plan_activation_id })
193
+ end
194
+
195
+ def decline_season_renewal_offer(season_key, offer_id)
196
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/decline"), {})
197
+ end
198
+
199
+ def release_season_renewal_offer(season_key, offer_id)
200
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/release"), {})
201
+ end
202
+
203
+ def list_season_occurrences(season_key)
204
+ @client.get(path(season_key, "/occurrences"))
205
+ end
206
+
207
+ def create_season_amendment(season_key, event_key:, kind:, starts_at: nil, name: nil,
208
+ idempotency_key: nil)
209
+ @client.post(path(season_key, "/amendments"),
210
+ compact({ "eventKey" => event_key, "kind" => kind,
211
+ "startsAt" => starts_at, "name" => name }),
212
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
213
+ end
214
+
215
+ def list_season_amendments(season_key)
216
+ @client.get(path(season_key, "/amendments"))
217
+ end
218
+
219
+ def retrieve_season_amendment(season_key, amendment_id)
220
+ @client.get(path(season_key, "/amendments/#{encode(amendment_id)}"))
221
+ end
222
+
223
+ def retrieve_season_report(season_key)
224
+ @client.get(path(season_key, "/reports"))
225
+ end
226
+
227
+ def list_season_operations(season_key)
228
+ @client.get(path(season_key, "/operations"))
229
+ end
230
+
231
+ def retrieve_season_support_lookup(season_key, booking_ref: nil, holder_ref: nil)
232
+ @client.get(path(season_key, "/support-lookups"),
233
+ compact({ "bookingRef" => booking_ref, "holderRef" => holder_ref }))
234
+ end
235
+
236
+ def list_season_outbox(season_key)
237
+ @client.get(path(season_key, "/outbox"))
238
+ end
239
+
240
+ def replay_season_outbox(season_key, occurrence_id)
241
+ @client.post(path(season_key, "/outbox/#{encode(occurrence_id)}/replay"), {})
242
+ end
243
+
244
+ def list_season_audit(season_key)
245
+ @client.get(path(season_key, "/audit"))
246
+ end
247
+
248
+ def export_season_support_snapshot(season_key)
249
+ @client.get(path(season_key, "/export"))
250
+ end
251
+
252
+ private
253
+
254
+ def path(season_key, suffix = "")
255
+ "/v1/seasons/#{encode(season_key)}#{suffix}"
256
+ end
257
+
258
+ def selection(event_keys, source_performance_group_keys)
259
+ compact({ "eventKeys" => event_keys,
260
+ "sourcePerformanceGroupKeys" => source_performance_group_keys })
261
+ end
262
+
263
+ def lifecycle(season_key, action, expected_revision)
264
+ @client.post(path(season_key, "/#{action}"), { "expectedRevision" => expected_revision })
265
+ end
266
+
267
+ def sales(season_key, action, expected_revision)
268
+ @client.post(path(season_key, "/sales/#{action}"),
269
+ { "expectedRevision" => expected_revision })
270
+ end
271
+ end
272
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SeatLayer
4
- VERSION = "0.6.1"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/seatlayer.rb CHANGED
@@ -7,6 +7,7 @@ require_relative "seatlayer/resources"
7
7
  require_relative "seatlayer/inventory"
8
8
  require_relative "seatlayer/channels"
9
9
  require_relative "seatlayer/performance_groups"
10
+ require_relative "seatlayer/seasons"
10
11
  require_relative "seatlayer/webhook"
11
12
 
12
13
  # Official Ruby server SDK for the SeatLayer reserved-seating API.
@@ -21,7 +22,7 @@ module SeatLayer
21
22
  # The SeatLayer client.
22
23
  class Client
23
24
  attr_reader :charts, :events, :inventory, :channels, :performance_groups, :sessions,
24
- :webhooks, :workspaces, :templates
25
+ :seasons, :webhooks, :workspaces, :templates
25
26
 
26
27
  def initialize(secret_key, base_url: HTTPClient::DEFAULT_BASE_URL,
27
28
  max_retries: HTTPClient::DEFAULT_MAX_RETRIES,
@@ -34,6 +35,7 @@ module SeatLayer
34
35
  @inventory = Inventory.new(@http)
35
36
  @channels = Channels.new(@http)
36
37
  @performance_groups = PerformanceGroups.new(@http)
38
+ @seasons = Seasons.new(@http)
37
39
  @sessions = Sessions.new(@http)
38
40
  @templates = Templates.new(@http)
39
41
  @webhooks = Webhooks.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.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SeatLayer
@@ -28,6 +28,7 @@ files:
28
28
  - lib/seatlayer/inventory.rb
29
29
  - lib/seatlayer/performance_groups.rb
30
30
  - lib/seatlayer/resources.rb
31
+ - lib/seatlayer/seasons.rb
31
32
  - lib/seatlayer/version.rb
32
33
  - lib/seatlayer/webhook.rb
33
34
  homepage: https://seatlayer.io