shopstack 0.2.6 → 0.3.1

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.
@@ -0,0 +1,3322 @@
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "Shopstack API",
5
+ "version": "0.1.0",
6
+ "description": "Shopstack checkout automation and guided guest restaurant reservation API.",
7
+ "license": {
8
+ "name": "Proprietary",
9
+ "identifier": "LicenseRef-Proprietary"
10
+ }
11
+ },
12
+ "servers": [
13
+ {
14
+ "url": "/v1"
15
+ }
16
+ ],
17
+ "security": [
18
+ {
19
+ "bearerAuth": []
20
+ }
21
+ ],
22
+ "paths": {
23
+ "/accounts": {
24
+ "post": {
25
+ "operationId": "startAccountSignup",
26
+ "summary": "Start email-verified signup or sign-in",
27
+ "description": "A new email creates the selected account type after verification. An existing email signs in to the same account with a new profile key; other devices remain signed in. The requested account type must match the existing account. Suspended accounts and users remain blocked. No key is issued before verification, and the key is delivered once through the caller's private polling capability, never in the email page.",
28
+ "security": [],
29
+ "parameters": [
30
+ {
31
+ "$ref": "#/components/parameters/IdempotencyKey"
32
+ },
33
+ {
34
+ "$ref": "#/components/parameters/SignupRecoveryKey"
35
+ }
36
+ ],
37
+ "requestBody": {
38
+ "required": true,
39
+ "content": {
40
+ "application/json": {
41
+ "schema": {
42
+ "$ref": "#/components/schemas/CreateAccountRequest"
43
+ }
44
+ }
45
+ }
46
+ },
47
+ "responses": {
48
+ "202": {
49
+ "description": "Verification email accepted; no new API key has been issued",
50
+ "content": {
51
+ "application/json": {
52
+ "schema": {
53
+ "$ref": "#/components/schemas/AccountSignupStarted"
54
+ }
55
+ }
56
+ }
57
+ },
58
+ "401": {
59
+ "description": "An idempotent replay did not present the original signup recovery capability",
60
+ "content": {
61
+ "application/json": {
62
+ "schema": {
63
+ "$ref": "#/components/schemas/ErrorResponse"
64
+ }
65
+ }
66
+ }
67
+ },
68
+ "409": {
69
+ "$ref": "#/components/responses/Conflict"
70
+ },
71
+ "422": {
72
+ "$ref": "#/components/responses/Unprocessable"
73
+ },
74
+ "429": {
75
+ "description": "Bounded signup rate limit reached",
76
+ "headers": {
77
+ "Retry-After": {
78
+ "required": true,
79
+ "schema": {
80
+ "type": "integer",
81
+ "minimum": 1
82
+ }
83
+ }
84
+ },
85
+ "content": {
86
+ "application/json": {
87
+ "schema": {
88
+ "$ref": "#/components/schemas/ErrorResponse"
89
+ }
90
+ }
91
+ }
92
+ },
93
+ "503": {
94
+ "description": "Verification email or account storage is unavailable",
95
+ "content": {
96
+ "application/json": {
97
+ "schema": {
98
+ "$ref": "#/components/schemas/ErrorResponse"
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ },
106
+ "/accounts/{signup_id}": {
107
+ "get": {
108
+ "operationId": "pollAccountSignup",
109
+ "summary": "Poll one signup using its checkout-independent polling capability",
110
+ "security": [
111
+ {
112
+ "signupPollAuth": []
113
+ }
114
+ ],
115
+ "parameters": [
116
+ {
117
+ "$ref": "#/components/parameters/SignupId"
118
+ }
119
+ ],
120
+ "responses": {
121
+ "200": {
122
+ "description": "Current signup state; the initial API key appears only in the first successful poll after email verification",
123
+ "content": {
124
+ "application/json": {
125
+ "schema": {
126
+ "oneOf": [
127
+ {
128
+ "$ref": "#/components/schemas/AccountSignupPending"
129
+ },
130
+ {
131
+ "$ref": "#/components/schemas/AccountSignupVerified"
132
+ }
133
+ ]
134
+ }
135
+ }
136
+ }
137
+ },
138
+ "401": {
139
+ "description": "Polling capability is missing or invalid",
140
+ "content": {
141
+ "application/json": {
142
+ "schema": {
143
+ "$ref": "#/components/schemas/ErrorResponse"
144
+ }
145
+ }
146
+ }
147
+ },
148
+ "410": {
149
+ "description": "Signup expired, or its initial API key was already delivered and cannot be retrieved again",
150
+ "content": {
151
+ "application/json": {
152
+ "schema": {
153
+ "$ref": "#/components/schemas/ErrorResponse"
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ },
161
+ "/accounts/{signup_id}/verify": {
162
+ "get": {
163
+ "operationId": "verifyAccountEmail",
164
+ "summary": "Consume the one-time email-verification capability",
165
+ "security": [],
166
+ "parameters": [
167
+ {
168
+ "$ref": "#/components/parameters/SignupId"
169
+ },
170
+ {
171
+ "name": "token",
172
+ "in": "query",
173
+ "required": true,
174
+ "schema": {
175
+ "type": "string",
176
+ "minLength": 32,
177
+ "maxLength": 128
178
+ }
179
+ }
180
+ ],
181
+ "responses": {
182
+ "200": {
183
+ "description": "Email verified; the page never contains the API key",
184
+ "content": {
185
+ "text/html": {
186
+ "schema": {
187
+ "type": "string"
188
+ }
189
+ }
190
+ }
191
+ },
192
+ "400": {
193
+ "description": "Verification capability is invalid",
194
+ "content": {
195
+ "text/html": {
196
+ "schema": {
197
+ "type": "string"
198
+ }
199
+ }
200
+ }
201
+ },
202
+ "403": {
203
+ "description": "The verified account or implicit user is unavailable",
204
+ "content": {
205
+ "text/html": {
206
+ "schema": {
207
+ "type": "string"
208
+ }
209
+ }
210
+ }
211
+ },
212
+ "409": {
213
+ "description": "The requested account type does not match the verified existing account",
214
+ "content": {
215
+ "text/html": {
216
+ "schema": {
217
+ "type": "string"
218
+ }
219
+ }
220
+ }
221
+ },
222
+ "410": {
223
+ "description": "Verification capability expired",
224
+ "content": {
225
+ "text/html": {
226
+ "schema": {
227
+ "type": "string"
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+ },
235
+ "/users": {
236
+ "get": {
237
+ "operationId": "listUsers",
238
+ "summary": "List users owned by the authenticated developer account",
239
+ "responses": {
240
+ "200": {
241
+ "description": "Ordered developer-user collection",
242
+ "content": {
243
+ "application/json": {
244
+ "schema": {
245
+ "$ref": "#/components/schemas/UserPage"
246
+ }
247
+ }
248
+ }
249
+ },
250
+ "403": {
251
+ "$ref": "#/components/responses/Forbidden"
252
+ }
253
+ }
254
+ },
255
+ "post": {
256
+ "operationId": "createUser",
257
+ "summary": "Create a developer-owned user and its first user API key",
258
+ "parameters": [
259
+ {
260
+ "$ref": "#/components/parameters/IdempotencyKey"
261
+ }
262
+ ],
263
+ "requestBody": {
264
+ "required": true,
265
+ "content": {
266
+ "application/json": {
267
+ "schema": {
268
+ "$ref": "#/components/schemas/CreateUserRequest"
269
+ }
270
+ }
271
+ }
272
+ },
273
+ "responses": {
274
+ "201": {
275
+ "description": "User created; api_key is returned only by this mutation or its idempotent replay",
276
+ "content": {
277
+ "application/json": {
278
+ "schema": {
279
+ "$ref": "#/components/schemas/UserCreated"
280
+ }
281
+ }
282
+ }
283
+ },
284
+ "403": {
285
+ "$ref": "#/components/responses/Forbidden"
286
+ },
287
+ "409": {
288
+ "$ref": "#/components/responses/Conflict"
289
+ },
290
+ "422": {
291
+ "$ref": "#/components/responses/Unprocessable"
292
+ }
293
+ }
294
+ }
295
+ },
296
+ "/users/{user_id}": {
297
+ "get": {
298
+ "operationId": "getUser",
299
+ "summary": "Get one user owned by the authenticated developer account",
300
+ "parameters": [
301
+ {
302
+ "$ref": "#/components/parameters/UserId"
303
+ }
304
+ ],
305
+ "responses": {
306
+ "200": {
307
+ "description": "Developer-owned user",
308
+ "content": {
309
+ "application/json": {
310
+ "schema": {
311
+ "$ref": "#/components/schemas/User"
312
+ }
313
+ }
314
+ }
315
+ },
316
+ "403": {
317
+ "$ref": "#/components/responses/Forbidden"
318
+ },
319
+ "404": {
320
+ "$ref": "#/components/responses/NotFound"
321
+ }
322
+ }
323
+ },
324
+ "patch": {
325
+ "operationId": "updateUser",
326
+ "summary": "Activate or suspend one developer-owned user",
327
+ "parameters": [
328
+ {
329
+ "$ref": "#/components/parameters/UserId"
330
+ },
331
+ {
332
+ "$ref": "#/components/parameters/IdempotencyKey"
333
+ }
334
+ ],
335
+ "requestBody": {
336
+ "required": true,
337
+ "content": {
338
+ "application/json": {
339
+ "schema": {
340
+ "$ref": "#/components/schemas/UpdateUserRequest"
341
+ }
342
+ }
343
+ }
344
+ },
345
+ "responses": {
346
+ "200": {
347
+ "description": "Updated user",
348
+ "content": {
349
+ "application/json": {
350
+ "schema": {
351
+ "$ref": "#/components/schemas/User"
352
+ }
353
+ }
354
+ }
355
+ },
356
+ "403": {
357
+ "$ref": "#/components/responses/Forbidden"
358
+ },
359
+ "404": {
360
+ "$ref": "#/components/responses/NotFound"
361
+ },
362
+ "409": {
363
+ "$ref": "#/components/responses/Conflict"
364
+ },
365
+ "422": {
366
+ "$ref": "#/components/responses/Unprocessable"
367
+ }
368
+ }
369
+ }
370
+ },
371
+ "/users/{user_id}/api-key/rotate": {
372
+ "post": {
373
+ "operationId": "rotateUserApiKey",
374
+ "summary": "Replace one user's API key and return the new key once",
375
+ "parameters": [
376
+ {
377
+ "$ref": "#/components/parameters/UserId"
378
+ },
379
+ {
380
+ "$ref": "#/components/parameters/IdempotencyKey"
381
+ }
382
+ ],
383
+ "responses": {
384
+ "200": {
385
+ "description": "Replacement user API key",
386
+ "content": {
387
+ "application/json": {
388
+ "schema": {
389
+ "$ref": "#/components/schemas/UserApiKeyRotated"
390
+ }
391
+ }
392
+ }
393
+ },
394
+ "403": {
395
+ "$ref": "#/components/responses/Forbidden"
396
+ },
397
+ "404": {
398
+ "$ref": "#/components/responses/NotFound"
399
+ },
400
+ "409": {
401
+ "$ref": "#/components/responses/Conflict"
402
+ }
403
+ }
404
+ }
405
+ },
406
+ "/connect": {
407
+ "get": {
408
+ "operationId": "listPaymentProviderConnections",
409
+ "summary": "List available payment providers and this user's connection state",
410
+ "responses": {
411
+ "200": {
412
+ "description": "Available payment-provider connections",
413
+ "content": {
414
+ "application/json": {
415
+ "schema": {
416
+ "$ref": "#/components/schemas/ConnectionList"
417
+ }
418
+ }
419
+ }
420
+ },
421
+ "403": {
422
+ "$ref": "#/components/responses/Forbidden"
423
+ }
424
+ }
425
+ },
426
+ "post": {
427
+ "operationId": "connectPaymentProvider",
428
+ "summary": "Start, resume or reauthorize this user's selected payment provider",
429
+ "parameters": [
430
+ {
431
+ "$ref": "#/components/parameters/IdempotencyKey"
432
+ }
433
+ ],
434
+ "requestBody": {
435
+ "required": true,
436
+ "content": {
437
+ "application/json": {
438
+ "schema": {
439
+ "$ref": "#/components/schemas/ConnectRequest"
440
+ }
441
+ }
442
+ }
443
+ },
444
+ "responses": {
445
+ "200": {
446
+ "description": "Current connection state",
447
+ "content": {
448
+ "application/json": {
449
+ "schema": {
450
+ "$ref": "#/components/schemas/PaymentProviderConnection"
451
+ }
452
+ }
453
+ }
454
+ },
455
+ "403": {
456
+ "$ref": "#/components/responses/Forbidden"
457
+ },
458
+ "409": {
459
+ "$ref": "#/components/responses/Conflict"
460
+ },
461
+ "422": {
462
+ "$ref": "#/components/responses/Unprocessable"
463
+ }
464
+ }
465
+ }
466
+ },
467
+ "/reservations": {
468
+ "post": {
469
+ "operationId": "createReservationConversation",
470
+ "summary": "Start a guided guest restaurant reservation",
471
+ "description": "Starts a short-lived natural-language conversation. Shopstack asks for missing details, resolves a location, or returns the first six compact observed options. It does not create a guest profile or make a booking. Send Accept application/x-ndjson for optional measured progress. Stream HTTP 200 only accepts the stream; the final result frame contains the original status (including errors) and unchanged JSON body. Progress is display-only, may be dropped for slow readers, and grants no authority. A disconnected stream must not trigger a new booking attempt.",
472
+ "parameters": [
473
+ {
474
+ "$ref": "#/components/parameters/IdempotencyKey"
475
+ }
476
+ ],
477
+ "requestBody": {
478
+ "required": true,
479
+ "content": {
480
+ "application/json": {
481
+ "schema": {
482
+ "$ref": "#/components/schemas/CreateReservationRequest"
483
+ }
484
+ }
485
+ }
486
+ },
487
+ "responses": {
488
+ "200": {
489
+ "$ref": "#/components/responses/ReservationProgressStream"
490
+ },
491
+ "201": {
492
+ "description": "Reservation conversation created",
493
+ "content": {
494
+ "application/json": {
495
+ "schema": {
496
+ "$ref": "#/components/schemas/Reservation"
497
+ }
498
+ }
499
+ }
500
+ },
501
+ "403": {
502
+ "$ref": "#/components/responses/Forbidden"
503
+ },
504
+ "409": {
505
+ "$ref": "#/components/responses/Conflict"
506
+ },
507
+ "422": {
508
+ "$ref": "#/components/responses/Unprocessable"
509
+ },
510
+ "429": {
511
+ "$ref": "#/components/responses/RateLimited"
512
+ },
513
+ "502": {
514
+ "$ref": "#/components/responses/BadGateway"
515
+ },
516
+ "503": {
517
+ "$ref": "#/components/responses/Unavailable"
518
+ }
519
+ }
520
+ }
521
+ },
522
+ "/reservations/{reservation_id}": {
523
+ "get": {
524
+ "operationId": "getReservationConversation",
525
+ "summary": "Get the current reservation conversation turn",
526
+ "parameters": [
527
+ {
528
+ "$ref": "#/components/parameters/ReservationId"
529
+ }
530
+ ],
531
+ "responses": {
532
+ "200": {
533
+ "description": "Current reservation conversation",
534
+ "content": {
535
+ "application/json": {
536
+ "schema": {
537
+ "$ref": "#/components/schemas/Reservation"
538
+ }
539
+ }
540
+ }
541
+ },
542
+ "403": {
543
+ "$ref": "#/components/responses/Forbidden"
544
+ },
545
+ "404": {
546
+ "$ref": "#/components/responses/NotFound"
547
+ }
548
+ }
549
+ }
550
+ },
551
+ "/reservations/{reservation_id}/options": {
552
+ "get": {
553
+ "operationId": "listReservationOptions",
554
+ "summary": "Read one page from the current OpenTable-ordered restaurant pool",
555
+ "description": "Returns stored provider results without another model inference or restaurant-provider search. The default page contains six compact options. A refined search invalidates the prior option IDs.",
556
+ "parameters": [
557
+ {
558
+ "$ref": "#/components/parameters/ReservationId"
559
+ },
560
+ {
561
+ "name": "offset",
562
+ "in": "query",
563
+ "required": false,
564
+ "schema": {
565
+ "type": "integer",
566
+ "minimum": 0,
567
+ "maximum": 29,
568
+ "default": 0
569
+ }
570
+ },
571
+ {
572
+ "name": "limit",
573
+ "in": "query",
574
+ "required": false,
575
+ "schema": {
576
+ "type": "integer",
577
+ "minimum": 1,
578
+ "maximum": 6,
579
+ "default": 6
580
+ }
581
+ }
582
+ ],
583
+ "responses": {
584
+ "200": {
585
+ "description": "Current compact restaurant option page",
586
+ "content": {
587
+ "application/json": {
588
+ "schema": {
589
+ "$ref": "#/components/schemas/ReservationOptionPage"
590
+ }
591
+ }
592
+ }
593
+ },
594
+ "403": {
595
+ "$ref": "#/components/responses/Forbidden"
596
+ },
597
+ "404": {
598
+ "$ref": "#/components/responses/NotFound"
599
+ },
600
+ "422": {
601
+ "$ref": "#/components/responses/Unprocessable"
602
+ }
603
+ }
604
+ }
605
+ },
606
+ "/reservations/{reservation_id}/options/{option_id}": {
607
+ "get": {
608
+ "operationId": "getReservationOption",
609
+ "summary": "Read full stored provider details for one current option",
610
+ "description": "Returns the exact stored provider name, description, photos, facts, and current time IDs without another model inference or provider search.",
611
+ "parameters": [
612
+ {
613
+ "$ref": "#/components/parameters/ReservationId"
614
+ },
615
+ {
616
+ "$ref": "#/components/parameters/ReservationOptionId"
617
+ }
618
+ ],
619
+ "responses": {
620
+ "200": {
621
+ "description": "Full current restaurant option",
622
+ "content": {
623
+ "application/json": {
624
+ "schema": {
625
+ "$ref": "#/components/schemas/ReservationOption"
626
+ }
627
+ }
628
+ }
629
+ },
630
+ "403": {
631
+ "$ref": "#/components/responses/Forbidden"
632
+ },
633
+ "404": {
634
+ "$ref": "#/components/responses/NotFound"
635
+ },
636
+ "409": {
637
+ "$ref": "#/components/responses/Conflict"
638
+ }
639
+ }
640
+ }
641
+ },
642
+ "/reservations/{reservation_id}/messages": {
643
+ "post": {
644
+ "operationId": "sendReservationMessage",
645
+ "summary": "Continue a reservation conversation",
646
+ "description": "Adds natural-language detail, answers a follow-up question, selects a location, or selects an exact current option_id and time_id. Restaurant selection rechecks availability and locks the slot. It returns an exact booking summary. A later, clear confirmation message creates the guest profile and sends one booking request. Ambiguous messages do not book. Accept application/x-ndjson enables the same optional progress stream as creation. Its final frame, not stream HTTP 200, is the operation result.",
647
+ "parameters": [
648
+ {
649
+ "$ref": "#/components/parameters/ReservationId"
650
+ },
651
+ {
652
+ "$ref": "#/components/parameters/IdempotencyKey"
653
+ }
654
+ ],
655
+ "requestBody": {
656
+ "required": true,
657
+ "content": {
658
+ "application/json": {
659
+ "schema": {
660
+ "$ref": "#/components/schemas/SendReservationMessageRequest"
661
+ }
662
+ }
663
+ }
664
+ },
665
+ "responses": {
666
+ "200": {
667
+ "description": "Current reservation conversation after the turn",
668
+ "content": {
669
+ "application/x-ndjson": {
670
+ "schema": {
671
+ "$ref": "#/components/schemas/ReservationProgressStream"
672
+ }
673
+ },
674
+ "application/json": {
675
+ "schema": {
676
+ "$ref": "#/components/schemas/Reservation"
677
+ }
678
+ }
679
+ }
680
+ },
681
+ "403": {
682
+ "$ref": "#/components/responses/Forbidden"
683
+ },
684
+ "404": {
685
+ "$ref": "#/components/responses/NotFound"
686
+ },
687
+ "409": {
688
+ "$ref": "#/components/responses/Conflict"
689
+ },
690
+ "422": {
691
+ "$ref": "#/components/responses/Unprocessable"
692
+ },
693
+ "429": {
694
+ "$ref": "#/components/responses/RateLimited"
695
+ },
696
+ "502": {
697
+ "$ref": "#/components/responses/BadGateway"
698
+ },
699
+ "503": {
700
+ "$ref": "#/components/responses/Unavailable"
701
+ }
702
+ }
703
+ }
704
+ },
705
+ "/reservations/{reservation_id}/cancel": {
706
+ "post": {
707
+ "operationId": "cancelReservation",
708
+ "summary": "Cancel a confirmed guest reservation",
709
+ "description": "Sends at most one cancellation request. A successful provider cancellation response establishes cancelled, following the original app. An uncertain response triggers a read-only lookup of the exact booking; if that cannot establish cancellation, the result is cancellation_unknown. A new call with the current revision and a new idempotency key first reconciles that state, and sends another cancellation only if the provider confirms that the same booking is still active. Reusing an idempotency key replays its saved result. Booking submission is never retried. Accept application/x-ndjson enables optional display progress. The final frame retains the original response status and body, including errors.",
710
+ "parameters": [
711
+ {
712
+ "$ref": "#/components/parameters/ReservationId"
713
+ },
714
+ {
715
+ "$ref": "#/components/parameters/IdempotencyKey"
716
+ }
717
+ ],
718
+ "requestBody": {
719
+ "required": true,
720
+ "content": {
721
+ "application/json": {
722
+ "schema": {
723
+ "$ref": "#/components/schemas/CancelReservationRequest"
724
+ }
725
+ }
726
+ }
727
+ },
728
+ "responses": {
729
+ "200": {
730
+ "description": "Current reservation after cancellation",
731
+ "content": {
732
+ "application/x-ndjson": {
733
+ "schema": {
734
+ "$ref": "#/components/schemas/ReservationProgressStream"
735
+ }
736
+ },
737
+ "application/json": {
738
+ "schema": {
739
+ "$ref": "#/components/schemas/Reservation"
740
+ }
741
+ }
742
+ }
743
+ },
744
+ "403": {
745
+ "$ref": "#/components/responses/Forbidden"
746
+ },
747
+ "404": {
748
+ "$ref": "#/components/responses/NotFound"
749
+ },
750
+ "409": {
751
+ "$ref": "#/components/responses/Conflict"
752
+ },
753
+ "422": {
754
+ "$ref": "#/components/responses/Unprocessable"
755
+ },
756
+ "503": {
757
+ "$ref": "#/components/responses/Unavailable"
758
+ }
759
+ }
760
+ }
761
+ },
762
+ "/checkout": {
763
+ "post": {
764
+ "operationId": "createCheckout",
765
+ "summary": "Create an ephemeral checkout",
766
+ "parameters": [
767
+ {
768
+ "$ref": "#/components/parameters/IdempotencyKey"
769
+ }
770
+ ],
771
+ "requestBody": {
772
+ "required": true,
773
+ "content": {
774
+ "application/json": {
775
+ "schema": {
776
+ "$ref": "#/components/schemas/CreateCheckoutRequest"
777
+ }
778
+ }
779
+ }
780
+ },
781
+ "responses": {
782
+ "202": {
783
+ "description": "Checkout accepted",
784
+ "content": {
785
+ "application/json": {
786
+ "schema": {
787
+ "$ref": "#/components/schemas/CheckoutAccepted"
788
+ }
789
+ }
790
+ }
791
+ },
792
+ "409": {
793
+ "$ref": "#/components/responses/Conflict"
794
+ },
795
+ "422": {
796
+ "$ref": "#/components/responses/Unprocessable"
797
+ }
798
+ }
799
+ }
800
+ },
801
+ "/checkout/{checkout_id}": {
802
+ "get": {
803
+ "operationId": "getCheckout",
804
+ "summary": "Get checkout status and result",
805
+ "parameters": [
806
+ {
807
+ "$ref": "#/components/parameters/CheckoutId"
808
+ }
809
+ ],
810
+ "responses": {
811
+ "200": {
812
+ "description": "Current checkout representation",
813
+ "content": {
814
+ "application/json": {
815
+ "schema": {
816
+ "$ref": "#/components/schemas/Checkout"
817
+ }
818
+ }
819
+ }
820
+ },
821
+ "404": {
822
+ "$ref": "#/components/responses/NotFound"
823
+ }
824
+ }
825
+ }
826
+ },
827
+ "/checkout/{checkout_id}/updates": {
828
+ "post": {
829
+ "operationId": "createCheckoutUpdateSubscription",
830
+ "summary": "Create one short-lived checkout update subscription",
831
+ "description": "Returns a one-time, checkout- and owner-bound WebSocket capability. Connect to socket_url with the subprotocols shopstack.v1 and the returned token. Notifications contain only presentation_revision and terminal; GET /checkout/{checkout_id} remains authoritative.",
832
+ "parameters": [
833
+ {
834
+ "$ref": "#/components/parameters/CheckoutId"
835
+ }
836
+ ],
837
+ "responses": {
838
+ "201": {
839
+ "description": "One-time update subscription capability",
840
+ "content": {
841
+ "application/json": {
842
+ "schema": {
843
+ "$ref": "#/components/schemas/CheckoutUpdateSubscription"
844
+ }
845
+ }
846
+ }
847
+ },
848
+ "403": {
849
+ "$ref": "#/components/responses/Forbidden"
850
+ },
851
+ "404": {
852
+ "$ref": "#/components/responses/NotFound"
853
+ }
854
+ }
855
+ },
856
+ "get": {
857
+ "operationId": "waitForCheckoutUpdate",
858
+ "summary": "Wait boundedly for a newer checkout presentation",
859
+ "description": "Revision-aware fallback for clients without WebSocket support. A WebSocket upgrade instead uses the one-time capability returned by the POST operation and never places an API key or capability in the URL.",
860
+ "parameters": [
861
+ {
862
+ "$ref": "#/components/parameters/CheckoutId"
863
+ },
864
+ {
865
+ "name": "after",
866
+ "in": "query",
867
+ "required": true,
868
+ "schema": {
869
+ "type": "integer",
870
+ "minimum": 0
871
+ }
872
+ },
873
+ {
874
+ "name": "wait",
875
+ "in": "query",
876
+ "required": false,
877
+ "schema": {
878
+ "type": "integer",
879
+ "minimum": 1,
880
+ "maximum": 25,
881
+ "default": 25
882
+ }
883
+ }
884
+ ],
885
+ "responses": {
886
+ "101": {
887
+ "description": "Hibernating checkout update WebSocket accepted"
888
+ },
889
+ "200": {
890
+ "description": "Canonical checkout representation after a change",
891
+ "content": {
892
+ "application/json": {
893
+ "schema": {
894
+ "$ref": "#/components/schemas/Checkout"
895
+ }
896
+ }
897
+ }
898
+ },
899
+ "204": {
900
+ "description": "No newer presentation was available within the bound"
901
+ },
902
+ "403": {
903
+ "$ref": "#/components/responses/Forbidden"
904
+ },
905
+ "404": {
906
+ "$ref": "#/components/responses/NotFound"
907
+ },
908
+ "422": {
909
+ "$ref": "#/components/responses/Unprocessable"
910
+ }
911
+ }
912
+ }
913
+ },
914
+ "/checkout/{checkout_id}/live-view": {
915
+ "post": {
916
+ "operationId": "createLiveView",
917
+ "summary": "Replace the active checkout's one-time live-view link",
918
+ "description": "Returns a new owner-only URL using the stable /v/{checkout_id}#token=... format. The fragment token is one-time-use, is omitted from later checkout representations, and invalidates any earlier viewer capability or viewer session for this checkout. This operation requires both checkout:read and checkout:cancel because the resulting ordinary viewer can issue the exact checkout Stop command. While one typed payment-card request is active, the same checkout-bound viewer may also submit one protected card through the live page. That input does not grant payment approval or submission authority and never enters the viewer WebSocket.",
919
+ "parameters": [
920
+ {
921
+ "$ref": "#/components/parameters/CheckoutId"
922
+ },
923
+ {
924
+ "$ref": "#/components/parameters/IdempotencyKey"
925
+ }
926
+ ],
927
+ "responses": {
928
+ "201": {
929
+ "description": "Replacement one-time live-view capability",
930
+ "content": {
931
+ "application/json": {
932
+ "schema": {
933
+ "$ref": "#/components/schemas/LiveViewCapability"
934
+ }
935
+ }
936
+ }
937
+ },
938
+ "403": {
939
+ "$ref": "#/components/responses/Forbidden"
940
+ },
941
+ "404": {
942
+ "$ref": "#/components/responses/NotFound"
943
+ },
944
+ "409": {
945
+ "$ref": "#/components/responses/Conflict"
946
+ }
947
+ }
948
+ }
949
+ },
950
+ "/checkout/{checkout_id}/cancel": {
951
+ "post": {
952
+ "operationId": "cancelCheckout",
953
+ "summary": "Cancel a checkout before submission",
954
+ "parameters": [
955
+ {
956
+ "$ref": "#/components/parameters/CheckoutId"
957
+ },
958
+ {
959
+ "$ref": "#/components/parameters/IdempotencyKey"
960
+ }
961
+ ],
962
+ "responses": {
963
+ "200": {
964
+ "description": "Current checkout representation",
965
+ "content": {
966
+ "application/json": {
967
+ "schema": {
968
+ "$ref": "#/components/schemas/Checkout"
969
+ }
970
+ }
971
+ }
972
+ },
973
+ "404": {
974
+ "$ref": "#/components/responses/NotFound"
975
+ },
976
+ "409": {
977
+ "$ref": "#/components/responses/Conflict"
978
+ }
979
+ }
980
+ }
981
+ },
982
+ "/checkout/{checkout_id}/messages": {
983
+ "get": {
984
+ "operationId": "listCheckoutMessages",
985
+ "summary": "List user and assistant messages",
986
+ "parameters": [
987
+ {
988
+ "$ref": "#/components/parameters/CheckoutId"
989
+ },
990
+ {
991
+ "$ref": "#/components/parameters/AfterCursor"
992
+ },
993
+ {
994
+ "$ref": "#/components/parameters/PageLimit"
995
+ }
996
+ ],
997
+ "responses": {
998
+ "200": {
999
+ "description": "Ordered message page",
1000
+ "content": {
1001
+ "application/json": {
1002
+ "schema": {
1003
+ "$ref": "#/components/schemas/MessagePage"
1004
+ }
1005
+ }
1006
+ }
1007
+ },
1008
+ "404": {
1009
+ "$ref": "#/components/responses/NotFound"
1010
+ }
1011
+ }
1012
+ },
1013
+ "post": {
1014
+ "operationId": "sendCheckoutMessage",
1015
+ "summary": "Send a general user message to the model",
1016
+ "parameters": [
1017
+ {
1018
+ "$ref": "#/components/parameters/CheckoutId"
1019
+ },
1020
+ {
1021
+ "$ref": "#/components/parameters/IdempotencyKey"
1022
+ }
1023
+ ],
1024
+ "requestBody": {
1025
+ "required": true,
1026
+ "content": {
1027
+ "application/json": {
1028
+ "schema": {
1029
+ "$ref": "#/components/schemas/SendMessageRequest"
1030
+ }
1031
+ }
1032
+ }
1033
+ },
1034
+ "responses": {
1035
+ "201": {
1036
+ "description": "Message accepted",
1037
+ "content": {
1038
+ "application/json": {
1039
+ "schema": {
1040
+ "$ref": "#/components/schemas/Message"
1041
+ }
1042
+ }
1043
+ }
1044
+ },
1045
+ "404": {
1046
+ "$ref": "#/components/responses/NotFound"
1047
+ },
1048
+ "409": {
1049
+ "$ref": "#/components/responses/Conflict"
1050
+ },
1051
+ "422": {
1052
+ "$ref": "#/components/responses/Unprocessable"
1053
+ }
1054
+ }
1055
+ }
1056
+ },
1057
+ "/checkout/{checkout_id}/payment-approval": {
1058
+ "post": {
1059
+ "operationId": "decidePaymentApproval",
1060
+ "summary": "Approve or decline the current exact payment",
1061
+ "description": "Approval grants the same paused runner one-use permission to revalidate the checkout and click Pay itself. It does not hand browser control to the caller. The terminal checkout result reports available order and amount facts.",
1062
+ "parameters": [
1063
+ {
1064
+ "$ref": "#/components/parameters/CheckoutId"
1065
+ },
1066
+ {
1067
+ "$ref": "#/components/parameters/IdempotencyKey"
1068
+ }
1069
+ ],
1070
+ "requestBody": {
1071
+ "required": true,
1072
+ "content": {
1073
+ "application/json": {
1074
+ "schema": {
1075
+ "$ref": "#/components/schemas/PaymentApprovalRequest"
1076
+ }
1077
+ }
1078
+ }
1079
+ },
1080
+ "responses": {
1081
+ "200": {
1082
+ "description": "Approval decision accepted",
1083
+ "content": {
1084
+ "application/json": {
1085
+ "schema": {
1086
+ "$ref": "#/components/schemas/Checkout"
1087
+ }
1088
+ }
1089
+ }
1090
+ },
1091
+ "403": {
1092
+ "$ref": "#/components/responses/Forbidden"
1093
+ },
1094
+ "404": {
1095
+ "$ref": "#/components/responses/NotFound"
1096
+ },
1097
+ "409": {
1098
+ "$ref": "#/components/responses/Conflict"
1099
+ }
1100
+ }
1101
+ }
1102
+ },
1103
+ "/checkout/{checkout_id}/payment-details": {
1104
+ "post": {
1105
+ "operationId": "provideCheckoutPaymentDetails",
1106
+ "summary": "Provide one protected card only when an unconnected checkout requests it",
1107
+ "description": "The card is encrypted for the active checkout and never enters messages, queue payloads, viewer events, model output, or checkout resources. This does not approve or submit the payment.",
1108
+ "parameters": [
1109
+ {
1110
+ "$ref": "#/components/parameters/CheckoutId"
1111
+ },
1112
+ {
1113
+ "$ref": "#/components/parameters/IdempotencyKey"
1114
+ }
1115
+ ],
1116
+ "requestBody": {
1117
+ "required": true,
1118
+ "content": {
1119
+ "application/json": {
1120
+ "schema": {
1121
+ "$ref": "#/components/schemas/PaymentDetailsRequest"
1122
+ }
1123
+ }
1124
+ }
1125
+ },
1126
+ "responses": {
1127
+ "200": {
1128
+ "description": "Protected payment details accepted for the waiting runner",
1129
+ "content": {
1130
+ "application/json": {
1131
+ "schema": {
1132
+ "$ref": "#/components/schemas/Checkout"
1133
+ }
1134
+ }
1135
+ }
1136
+ },
1137
+ "403": {
1138
+ "$ref": "#/components/responses/Forbidden"
1139
+ },
1140
+ "404": {
1141
+ "$ref": "#/components/responses/NotFound"
1142
+ },
1143
+ "409": {
1144
+ "$ref": "#/components/responses/Conflict"
1145
+ },
1146
+ "422": {
1147
+ "$ref": "#/components/responses/Unprocessable"
1148
+ }
1149
+ }
1150
+ }
1151
+ },
1152
+ "/checkout/{checkout_id}/events": {
1153
+ "get": {
1154
+ "operationId": "listCheckoutEvents",
1155
+ "summary": "List durable checkout lifecycle events",
1156
+ "parameters": [
1157
+ {
1158
+ "$ref": "#/components/parameters/CheckoutId"
1159
+ },
1160
+ {
1161
+ "$ref": "#/components/parameters/AfterCursor"
1162
+ },
1163
+ {
1164
+ "$ref": "#/components/parameters/PageLimit"
1165
+ }
1166
+ ],
1167
+ "responses": {
1168
+ "200": {
1169
+ "description": "Ordered lifecycle event page",
1170
+ "content": {
1171
+ "application/json": {
1172
+ "schema": {
1173
+ "$ref": "#/components/schemas/EventPage"
1174
+ }
1175
+ }
1176
+ }
1177
+ },
1178
+ "404": {
1179
+ "$ref": "#/components/responses/NotFound"
1180
+ }
1181
+ }
1182
+ }
1183
+ }
1184
+ },
1185
+ "components": {
1186
+ "securitySchemes": {
1187
+ "bearerAuth": {
1188
+ "type": "http",
1189
+ "scheme": "bearer",
1190
+ "bearerFormat": "Shopstack developer-management or user API key"
1191
+ },
1192
+ "signupPollAuth": {
1193
+ "type": "http",
1194
+ "scheme": "bearer",
1195
+ "bearerFormat": "Checkout-independent signup polling capability"
1196
+ }
1197
+ },
1198
+ "parameters": {
1199
+ "CheckoutId": {
1200
+ "name": "checkout_id",
1201
+ "in": "path",
1202
+ "required": true,
1203
+ "schema": {
1204
+ "$ref": "#/components/schemas/CheckoutId"
1205
+ }
1206
+ },
1207
+ "UserId": {
1208
+ "name": "user_id",
1209
+ "in": "path",
1210
+ "required": true,
1211
+ "schema": {
1212
+ "$ref": "#/components/schemas/UserId"
1213
+ }
1214
+ },
1215
+ "ReservationId": {
1216
+ "name": "reservation_id",
1217
+ "in": "path",
1218
+ "required": true,
1219
+ "schema": {
1220
+ "$ref": "#/components/schemas/ReservationId"
1221
+ }
1222
+ },
1223
+ "ReservationOptionId": {
1224
+ "name": "option_id",
1225
+ "in": "path",
1226
+ "required": true,
1227
+ "schema": {
1228
+ "$ref": "#/components/schemas/ReservationOptionId"
1229
+ }
1230
+ },
1231
+ "SignupId": {
1232
+ "name": "signup_id",
1233
+ "in": "path",
1234
+ "required": true,
1235
+ "schema": {
1236
+ "$ref": "#/components/schemas/SignupId"
1237
+ }
1238
+ },
1239
+ "IdempotencyKey": {
1240
+ "name": "Idempotency-Key",
1241
+ "in": "header",
1242
+ "required": true,
1243
+ "schema": {
1244
+ "type": "string",
1245
+ "minLength": 8,
1246
+ "maxLength": 255
1247
+ }
1248
+ },
1249
+ "SignupRecoveryKey": {
1250
+ "name": "Signup-Recovery-Key",
1251
+ "in": "header",
1252
+ "required": true,
1253
+ "description": "Independently random caller-held capability required to recover an idempotent signup-start response",
1254
+ "schema": {
1255
+ "type": "string",
1256
+ "pattern": "^signup_recovery_[A-Za-z0-9_-]{43}$"
1257
+ }
1258
+ },
1259
+ "AfterCursor": {
1260
+ "name": "after",
1261
+ "in": "query",
1262
+ "required": false,
1263
+ "schema": {
1264
+ "type": "string",
1265
+ "minLength": 1,
1266
+ "maxLength": 128
1267
+ }
1268
+ },
1269
+ "PageLimit": {
1270
+ "name": "limit",
1271
+ "in": "query",
1272
+ "required": false,
1273
+ "schema": {
1274
+ "type": "integer",
1275
+ "minimum": 1,
1276
+ "maximum": 100,
1277
+ "default": 50
1278
+ }
1279
+ }
1280
+ },
1281
+ "responses": {
1282
+ "ReservationProgressStream": {
1283
+ "description": "Optional display stream; the final frame holds the operation status and body",
1284
+ "headers": {
1285
+ "X-Request-Id": {
1286
+ "schema": {
1287
+ "type": "string"
1288
+ }
1289
+ }
1290
+ },
1291
+ "content": {
1292
+ "application/x-ndjson": {
1293
+ "schema": {
1294
+ "$ref": "#/components/schemas/ReservationProgressStream"
1295
+ }
1296
+ }
1297
+ }
1298
+ },
1299
+ "Forbidden": {
1300
+ "description": "Authenticated but not authorized",
1301
+ "content": {
1302
+ "application/json": {
1303
+ "schema": {
1304
+ "$ref": "#/components/schemas/ErrorResponse"
1305
+ }
1306
+ }
1307
+ }
1308
+ },
1309
+ "NotFound": {
1310
+ "description": "Resource not found",
1311
+ "content": {
1312
+ "application/json": {
1313
+ "schema": {
1314
+ "$ref": "#/components/schemas/ErrorResponse"
1315
+ }
1316
+ }
1317
+ }
1318
+ },
1319
+ "Conflict": {
1320
+ "description": "Request conflicts with current state",
1321
+ "content": {
1322
+ "application/json": {
1323
+ "schema": {
1324
+ "$ref": "#/components/schemas/ErrorResponse"
1325
+ }
1326
+ }
1327
+ }
1328
+ },
1329
+ "Unprocessable": {
1330
+ "description": "Request failed semantic validation",
1331
+ "content": {
1332
+ "application/json": {
1333
+ "schema": {
1334
+ "$ref": "#/components/schemas/ErrorResponse"
1335
+ }
1336
+ }
1337
+ }
1338
+ },
1339
+ "RateLimited": {
1340
+ "description": "A bounded upstream or account rate limit was reached",
1341
+ "headers": {
1342
+ "Retry-After": {
1343
+ "schema": {
1344
+ "type": "integer",
1345
+ "minimum": 1
1346
+ }
1347
+ }
1348
+ },
1349
+ "content": {
1350
+ "application/json": {
1351
+ "schema": {
1352
+ "$ref": "#/components/schemas/ErrorResponse"
1353
+ }
1354
+ }
1355
+ }
1356
+ },
1357
+ "BadGateway": {
1358
+ "description": "A required upstream returned an invalid bounded response",
1359
+ "content": {
1360
+ "application/json": {
1361
+ "schema": {
1362
+ "$ref": "#/components/schemas/ErrorResponse"
1363
+ }
1364
+ }
1365
+ }
1366
+ },
1367
+ "Unavailable": {
1368
+ "description": "A required service is temporarily unavailable",
1369
+ "content": {
1370
+ "application/json": {
1371
+ "schema": {
1372
+ "$ref": "#/components/schemas/ErrorResponse"
1373
+ }
1374
+ }
1375
+ }
1376
+ }
1377
+ },
1378
+ "schemas": {
1379
+ "ReservationProgressStream": {
1380
+ "type": "string",
1381
+ "description": "UTF-8 newline-delimited JSON. At most 512 progress frames, then exactly one result frame and EOF. A progress frame has type progress, request_id (req_ plus 32 lowercase hex digits), increasing sequence (1-512), call_id, operation, phase (started, completed, failed, selected), elapsed_ms, optional duration_ms, http_status and a closed mechanical failure code. Operations are request, model_decision, model_request, model_retry_wait, provider_session, location_lookup, restaurant_request, resolve_location, get_search_filters, search_restaurants, prepare_booking, confirm_booking, decline_booking, ask_user, reply, availability_recheck, slot_lock, slot_unlock, guest_profile, guest_inbox, booking_submit, booking_status, booking_cancel. No URLs, arguments, secrets, personal data, model output or reasoning appear in progress. Timings overlap for nested operations; do not add parent and child durations. A result frame has type result, request_id, status (original HTTP status), body (original JSON result or error), and optional retry_after_seconds (integer, 0-3600) carrying the original Retry-After delay. X-Request-Id correlates every frame and server-side operation. Authentication and scope checks remain mandatory. Progress before those checks can only report request start; stream acceptance is not access. Absence of a final frame is an unknown outcome, never a booking failure or permission to retry. No automatic retry or resumable stream exists."
1382
+ },
1383
+ "AccountId": {
1384
+ "type": "string",
1385
+ "pattern": "^acct_[A-Za-z0-9_-]{10,80}$"
1386
+ },
1387
+ "ReservationId": {
1388
+ "type": "string",
1389
+ "pattern": "^rsv_[A-Za-z0-9_-]{10,80}$"
1390
+ },
1391
+ "ReservationOptionId": {
1392
+ "type": "string",
1393
+ "pattern": "^opt_[A-Za-z0-9_-]{10,80}$"
1394
+ },
1395
+ "ReservationTimeId": {
1396
+ "type": "string",
1397
+ "pattern": "^rtime_[A-Za-z0-9_-]{10,80}$"
1398
+ },
1399
+ "ReservationLocationId": {
1400
+ "type": "string",
1401
+ "pattern": "^rloc_[A-Za-z0-9_-]{10,80}$"
1402
+ },
1403
+ "AccountType": {
1404
+ "type": "string",
1405
+ "enum": [
1406
+ "personal",
1407
+ "developer"
1408
+ ]
1409
+ },
1410
+ "SignupId": {
1411
+ "type": "string",
1412
+ "pattern": "^sup_[A-Za-z0-9_-]{10,80}$"
1413
+ },
1414
+ "Account": {
1415
+ "type": "object",
1416
+ "additionalProperties": false,
1417
+ "required": [
1418
+ "id",
1419
+ "account_type",
1420
+ "email",
1421
+ "created_at"
1422
+ ],
1423
+ "properties": {
1424
+ "id": {
1425
+ "$ref": "#/components/schemas/AccountId"
1426
+ },
1427
+ "account_type": {
1428
+ "$ref": "#/components/schemas/AccountType"
1429
+ },
1430
+ "email": {
1431
+ "type": "string",
1432
+ "format": "email",
1433
+ "maxLength": 320
1434
+ },
1435
+ "created_at": {
1436
+ "type": "string",
1437
+ "format": "date-time"
1438
+ }
1439
+ }
1440
+ },
1441
+ "CreateAccountRequest": {
1442
+ "type": "object",
1443
+ "additionalProperties": false,
1444
+ "required": [
1445
+ "account_type",
1446
+ "email"
1447
+ ],
1448
+ "properties": {
1449
+ "account_type": {
1450
+ "$ref": "#/components/schemas/AccountType"
1451
+ },
1452
+ "email": {
1453
+ "type": "string",
1454
+ "format": "email",
1455
+ "maxLength": 320
1456
+ }
1457
+ }
1458
+ },
1459
+ "AccountSignupPending": {
1460
+ "type": "object",
1461
+ "additionalProperties": false,
1462
+ "required": [
1463
+ "id",
1464
+ "account_type",
1465
+ "email",
1466
+ "status",
1467
+ "created_at",
1468
+ "expires_at"
1469
+ ],
1470
+ "properties": {
1471
+ "id": {
1472
+ "$ref": "#/components/schemas/SignupId"
1473
+ },
1474
+ "account_type": {
1475
+ "$ref": "#/components/schemas/AccountType"
1476
+ },
1477
+ "email": {
1478
+ "type": "string",
1479
+ "format": "email",
1480
+ "maxLength": 320
1481
+ },
1482
+ "status": {
1483
+ "const": "verification_required"
1484
+ },
1485
+ "created_at": {
1486
+ "type": "string",
1487
+ "format": "date-time"
1488
+ },
1489
+ "expires_at": {
1490
+ "type": "string",
1491
+ "format": "date-time"
1492
+ }
1493
+ }
1494
+ },
1495
+ "AccountSignupStarted": {
1496
+ "type": "object",
1497
+ "additionalProperties": false,
1498
+ "required": [
1499
+ "id",
1500
+ "account_type",
1501
+ "email",
1502
+ "status",
1503
+ "created_at",
1504
+ "expires_at",
1505
+ "poll_token"
1506
+ ],
1507
+ "properties": {
1508
+ "id": {
1509
+ "$ref": "#/components/schemas/SignupId"
1510
+ },
1511
+ "account_type": {
1512
+ "$ref": "#/components/schemas/AccountType"
1513
+ },
1514
+ "email": {
1515
+ "type": "string",
1516
+ "format": "email",
1517
+ "maxLength": 320
1518
+ },
1519
+ "status": {
1520
+ "const": "verification_required"
1521
+ },
1522
+ "created_at": {
1523
+ "type": "string",
1524
+ "format": "date-time"
1525
+ },
1526
+ "expires_at": {
1527
+ "type": "string",
1528
+ "format": "date-time"
1529
+ },
1530
+ "poll_token": {
1531
+ "type": "string",
1532
+ "minLength": 32,
1533
+ "maxLength": 128,
1534
+ "writeOnly": true
1535
+ }
1536
+ }
1537
+ },
1538
+ "AccountSignupVerified": {
1539
+ "description": "One-time initial API-key delivery returned by the first successful verified poll only",
1540
+ "oneOf": [
1541
+ {
1542
+ "type": "object",
1543
+ "additionalProperties": false,
1544
+ "required": [
1545
+ "id",
1546
+ "status",
1547
+ "verified_at",
1548
+ "account",
1549
+ "api_key",
1550
+ "key_type"
1551
+ ],
1552
+ "properties": {
1553
+ "id": {
1554
+ "$ref": "#/components/schemas/SignupId"
1555
+ },
1556
+ "status": {
1557
+ "const": "verified"
1558
+ },
1559
+ "verified_at": {
1560
+ "type": "string",
1561
+ "format": "date-time"
1562
+ },
1563
+ "account": {
1564
+ "$ref": "#/components/schemas/Account"
1565
+ },
1566
+ "api_key": {
1567
+ "type": "string",
1568
+ "minLength": 40,
1569
+ "maxLength": 255,
1570
+ "writeOnly": true
1571
+ },
1572
+ "key_type": {
1573
+ "const": "developer"
1574
+ }
1575
+ }
1576
+ },
1577
+ {
1578
+ "type": "object",
1579
+ "additionalProperties": false,
1580
+ "required": [
1581
+ "id",
1582
+ "status",
1583
+ "verified_at",
1584
+ "account",
1585
+ "user",
1586
+ "api_key",
1587
+ "key_type"
1588
+ ],
1589
+ "properties": {
1590
+ "id": {
1591
+ "$ref": "#/components/schemas/SignupId"
1592
+ },
1593
+ "status": {
1594
+ "const": "verified"
1595
+ },
1596
+ "verified_at": {
1597
+ "type": "string",
1598
+ "format": "date-time"
1599
+ },
1600
+ "account": {
1601
+ "$ref": "#/components/schemas/Account"
1602
+ },
1603
+ "user": {
1604
+ "$ref": "#/components/schemas/User"
1605
+ },
1606
+ "api_key": {
1607
+ "type": "string",
1608
+ "minLength": 40,
1609
+ "maxLength": 255,
1610
+ "writeOnly": true
1611
+ },
1612
+ "key_type": {
1613
+ "const": "user"
1614
+ }
1615
+ }
1616
+ }
1617
+ ]
1618
+ },
1619
+ "UserId": {
1620
+ "type": "string",
1621
+ "pattern": "^usr_[A-Za-z0-9_-]{10,80}$"
1622
+ },
1623
+ "UserStatus": {
1624
+ "type": "string",
1625
+ "enum": [
1626
+ "active",
1627
+ "suspended"
1628
+ ]
1629
+ },
1630
+ "User": {
1631
+ "type": "object",
1632
+ "additionalProperties": false,
1633
+ "required": [
1634
+ "id",
1635
+ "external_id",
1636
+ "status",
1637
+ "created_at",
1638
+ "updated_at"
1639
+ ],
1640
+ "properties": {
1641
+ "id": {
1642
+ "$ref": "#/components/schemas/UserId"
1643
+ },
1644
+ "external_id": {
1645
+ "type": "string",
1646
+ "minLength": 1,
1647
+ "maxLength": 255
1648
+ },
1649
+ "status": {
1650
+ "$ref": "#/components/schemas/UserStatus"
1651
+ },
1652
+ "created_at": {
1653
+ "type": "string",
1654
+ "format": "date-time"
1655
+ },
1656
+ "updated_at": {
1657
+ "type": "string",
1658
+ "format": "date-time"
1659
+ }
1660
+ }
1661
+ },
1662
+ "UserCreated": {
1663
+ "allOf": [
1664
+ {
1665
+ "$ref": "#/components/schemas/User"
1666
+ },
1667
+ {
1668
+ "type": "object",
1669
+ "required": [
1670
+ "api_key"
1671
+ ],
1672
+ "properties": {
1673
+ "api_key": {
1674
+ "type": "string",
1675
+ "minLength": 40,
1676
+ "maxLength": 255,
1677
+ "writeOnly": true
1678
+ }
1679
+ }
1680
+ }
1681
+ ]
1682
+ },
1683
+ "UserApiKeyRotated": {
1684
+ "type": "object",
1685
+ "additionalProperties": false,
1686
+ "required": [
1687
+ "user_id",
1688
+ "api_key"
1689
+ ],
1690
+ "properties": {
1691
+ "user_id": {
1692
+ "$ref": "#/components/schemas/UserId"
1693
+ },
1694
+ "api_key": {
1695
+ "type": "string",
1696
+ "minLength": 40,
1697
+ "maxLength": 255,
1698
+ "writeOnly": true
1699
+ }
1700
+ }
1701
+ },
1702
+ "CreateUserRequest": {
1703
+ "type": "object",
1704
+ "additionalProperties": false,
1705
+ "required": [
1706
+ "external_id"
1707
+ ],
1708
+ "properties": {
1709
+ "external_id": {
1710
+ "type": "string",
1711
+ "minLength": 1,
1712
+ "maxLength": 255
1713
+ }
1714
+ }
1715
+ },
1716
+ "UpdateUserRequest": {
1717
+ "type": "object",
1718
+ "additionalProperties": false,
1719
+ "required": [
1720
+ "status"
1721
+ ],
1722
+ "properties": {
1723
+ "status": {
1724
+ "$ref": "#/components/schemas/UserStatus"
1725
+ }
1726
+ }
1727
+ },
1728
+ "UserPage": {
1729
+ "type": "object",
1730
+ "additionalProperties": false,
1731
+ "required": [
1732
+ "data",
1733
+ "has_more"
1734
+ ],
1735
+ "properties": {
1736
+ "data": {
1737
+ "type": "array",
1738
+ "items": {
1739
+ "$ref": "#/components/schemas/User"
1740
+ }
1741
+ },
1742
+ "next_cursor": {
1743
+ "oneOf": [
1744
+ {
1745
+ "type": "string"
1746
+ },
1747
+ {
1748
+ "type": "null"
1749
+ }
1750
+ ]
1751
+ },
1752
+ "has_more": {
1753
+ "type": "boolean"
1754
+ }
1755
+ }
1756
+ },
1757
+ "PaymentProvider": {
1758
+ "type": "string",
1759
+ "enum": [
1760
+ "link"
1761
+ ]
1762
+ },
1763
+ "ConnectionStatus": {
1764
+ "type": "string",
1765
+ "enum": [
1766
+ "not_connected",
1767
+ "connecting",
1768
+ "action_required",
1769
+ "active",
1770
+ "reauthorization_required",
1771
+ "failed"
1772
+ ]
1773
+ },
1774
+ "PaymentProviderConnection": {
1775
+ "type": "object",
1776
+ "additionalProperties": false,
1777
+ "required": [
1778
+ "payment_provider",
1779
+ "display_name",
1780
+ "connection_status",
1781
+ "checkout_ready",
1782
+ "capabilities"
1783
+ ],
1784
+ "properties": {
1785
+ "payment_provider": {
1786
+ "$ref": "#/components/schemas/PaymentProvider"
1787
+ },
1788
+ "display_name": {
1789
+ "type": "string",
1790
+ "minLength": 1,
1791
+ "maxLength": 80
1792
+ },
1793
+ "connection_status": {
1794
+ "$ref": "#/components/schemas/ConnectionStatus"
1795
+ },
1796
+ "checkout_ready": {
1797
+ "type": "boolean"
1798
+ },
1799
+ "capabilities": {
1800
+ "type": "array",
1801
+ "items": {
1802
+ "type": "string",
1803
+ "enum": [
1804
+ "one_time_virtual_card"
1805
+ ]
1806
+ },
1807
+ "uniqueItems": true
1808
+ },
1809
+ "connect_url": {
1810
+ "type": "string",
1811
+ "format": "uri",
1812
+ "maxLength": 2048
1813
+ },
1814
+ "phrase": {
1815
+ "type": "string",
1816
+ "minLength": 1,
1817
+ "maxLength": 80
1818
+ }
1819
+ }
1820
+ },
1821
+ "ConnectionList": {
1822
+ "type": "object",
1823
+ "additionalProperties": false,
1824
+ "required": [
1825
+ "connections"
1826
+ ],
1827
+ "properties": {
1828
+ "connections": {
1829
+ "type": "array",
1830
+ "items": {
1831
+ "$ref": "#/components/schemas/PaymentProviderConnection"
1832
+ }
1833
+ }
1834
+ }
1835
+ },
1836
+ "ConnectRequest": {
1837
+ "type": "object",
1838
+ "additionalProperties": false,
1839
+ "required": [
1840
+ "payment_provider"
1841
+ ],
1842
+ "properties": {
1843
+ "payment_provider": {
1844
+ "$ref": "#/components/schemas/PaymentProvider"
1845
+ }
1846
+ }
1847
+ },
1848
+ "CreateReservationRequest": {
1849
+ "type": "object",
1850
+ "additionalProperties": false,
1851
+ "required": [
1852
+ "diner",
1853
+ "location",
1854
+ "request"
1855
+ ],
1856
+ "properties": {
1857
+ "diner": {
1858
+ "type": "object",
1859
+ "additionalProperties": false,
1860
+ "required": [
1861
+ "country",
1862
+ "first_name",
1863
+ "last_name",
1864
+ "phone_number"
1865
+ ],
1866
+ "properties": {
1867
+ "email": {
1868
+ "type": "string",
1869
+ "format": "email",
1870
+ "maxLength": 254,
1871
+ "description": "Optional booking contact email. When supplied, this exact trimmed address is used for the guest profile. Otherwise Shopstack creates a temporary inbox when booking. Do not ask for an email if it was omitted."
1872
+ },
1873
+ "country": {
1874
+ "type": "string",
1875
+ "pattern": "^[A-Z]{2}$"
1876
+ },
1877
+ "first_name": {
1878
+ "type": "string",
1879
+ "minLength": 1,
1880
+ "maxLength": 100
1881
+ },
1882
+ "last_name": {
1883
+ "type": "string",
1884
+ "minLength": 1,
1885
+ "maxLength": 100
1886
+ },
1887
+ "phone_number": {
1888
+ "type": "string",
1889
+ "minLength": 7,
1890
+ "maxLength": 40
1891
+ }
1892
+ }
1893
+ },
1894
+ "location": {
1895
+ "type": "object",
1896
+ "additionalProperties": false,
1897
+ "required": [
1898
+ "query"
1899
+ ],
1900
+ "properties": {
1901
+ "query": {
1902
+ "type": "string",
1903
+ "minLength": 2,
1904
+ "maxLength": 300,
1905
+ "description": "Human-readable place name for OpenTable location matching."
1906
+ }
1907
+ }
1908
+ },
1909
+ "request": {
1910
+ "type": "string",
1911
+ "minLength": 1,
1912
+ "maxLength": 12000,
1913
+ "description": "Natural-language search request. Include as much useful detail as possible, such as date, local time or range, party size, cuisine, budget, travel distance, atmosphere, dietary needs, seating, accessibility needs, occasion, and anything to avoid."
1914
+ },
1915
+ "external_reference": {
1916
+ "type": "string",
1917
+ "maxLength": 255
1918
+ }
1919
+ }
1920
+ },
1921
+ "SendReservationMessageRequest": {
1922
+ "type": "object",
1923
+ "additionalProperties": false,
1924
+ "required": [
1925
+ "content",
1926
+ "expected_revision"
1927
+ ],
1928
+ "properties": {
1929
+ "content": {
1930
+ "type": "string",
1931
+ "minLength": 1,
1932
+ "maxLength": 4000
1933
+ },
1934
+ "expected_revision": {
1935
+ "type": "integer",
1936
+ "minimum": 1
1937
+ }
1938
+ }
1939
+ },
1940
+ "CancelReservationRequest": {
1941
+ "type": "object",
1942
+ "additionalProperties": false,
1943
+ "required": [
1944
+ "expected_revision"
1945
+ ],
1946
+ "properties": {
1947
+ "expected_revision": {
1948
+ "type": "integer",
1949
+ "minimum": 1
1950
+ }
1951
+ }
1952
+ },
1953
+ "ReservationTime": {
1954
+ "type": "object",
1955
+ "additionalProperties": false,
1956
+ "required": [
1957
+ "date_time",
1958
+ "time_id"
1959
+ ],
1960
+ "properties": {
1961
+ "date_time": {
1962
+ "type": "string",
1963
+ "format": "date-time"
1964
+ },
1965
+ "time_id": {
1966
+ "$ref": "#/components/schemas/ReservationTimeId"
1967
+ }
1968
+ }
1969
+ },
1970
+ "ReservationOption": {
1971
+ "type": "object",
1972
+ "additionalProperties": false,
1973
+ "required": [
1974
+ "option_id",
1975
+ "name",
1976
+ "address",
1977
+ "cuisines",
1978
+ "times",
1979
+ "match_reasons",
1980
+ "photo_urls",
1981
+ "unverified"
1982
+ ],
1983
+ "properties": {
1984
+ "option_id": {
1985
+ "$ref": "#/components/schemas/ReservationOptionId"
1986
+ },
1987
+ "name": {
1988
+ "type": "string"
1989
+ },
1990
+ "address": {
1991
+ "type": "string"
1992
+ },
1993
+ "cuisines": {
1994
+ "type": "array",
1995
+ "items": {
1996
+ "type": "string"
1997
+ }
1998
+ },
1999
+ "description": {
2000
+ "type": "string"
2001
+ },
2002
+ "price_label": {
2003
+ "type": "string"
2004
+ },
2005
+ "rating": {
2006
+ "type": "number",
2007
+ "minimum": 0,
2008
+ "maximum": 5
2009
+ },
2010
+ "review_count": {
2011
+ "type": "integer",
2012
+ "minimum": 0
2013
+ },
2014
+ "times": {
2015
+ "type": "array",
2016
+ "minItems": 1,
2017
+ "maxItems": 30,
2018
+ "items": {
2019
+ "$ref": "#/components/schemas/ReservationTime"
2020
+ }
2021
+ },
2022
+ "match_reasons": {
2023
+ "type": "array",
2024
+ "items": {
2025
+ "type": "string"
2026
+ }
2027
+ },
2028
+ "photo_urls": {
2029
+ "type": "array",
2030
+ "maxItems": 5,
2031
+ "items": {
2032
+ "type": "string",
2033
+ "format": "uri",
2034
+ "pattern": "^https://"
2035
+ }
2036
+ },
2037
+ "unverified": {
2038
+ "type": "array",
2039
+ "items": {
2040
+ "type": "string"
2041
+ }
2042
+ }
2043
+ }
2044
+ },
2045
+ "ReservationCompactOption": {
2046
+ "type": "object",
2047
+ "additionalProperties": false,
2048
+ "required": [
2049
+ "option_id",
2050
+ "name",
2051
+ "address",
2052
+ "cuisines",
2053
+ "available_times",
2054
+ "fit",
2055
+ "caveats",
2056
+ "tags"
2057
+ ],
2058
+ "properties": {
2059
+ "option_id": {
2060
+ "$ref": "#/components/schemas/ReservationOptionId"
2061
+ },
2062
+ "name": {
2063
+ "type": "string"
2064
+ },
2065
+ "address": {
2066
+ "type": "string"
2067
+ },
2068
+ "cuisines": {
2069
+ "type": "array",
2070
+ "items": {
2071
+ "type": "string"
2072
+ }
2073
+ },
2074
+ "description_excerpt": {
2075
+ "type": "string",
2076
+ "maxLength": 240
2077
+ },
2078
+ "cover_photo_url": {
2079
+ "type": "string",
2080
+ "format": "uri",
2081
+ "pattern": "^https://"
2082
+ },
2083
+ "price_label": {
2084
+ "type": "string"
2085
+ },
2086
+ "rating": {
2087
+ "type": "number",
2088
+ "minimum": 0,
2089
+ "maximum": 5
2090
+ },
2091
+ "review_count": {
2092
+ "type": "integer",
2093
+ "minimum": 0
2094
+ },
2095
+ "available_times": {
2096
+ "type": "array",
2097
+ "minItems": 1,
2098
+ "maxItems": 30,
2099
+ "items": {
2100
+ "$ref": "#/components/schemas/ReservationTime"
2101
+ }
2102
+ },
2103
+ "fit": {
2104
+ "type": "array",
2105
+ "items": {
2106
+ "type": "string"
2107
+ }
2108
+ },
2109
+ "caveats": {
2110
+ "type": "array",
2111
+ "items": {
2112
+ "type": "string"
2113
+ }
2114
+ },
2115
+ "tags": {
2116
+ "type": "array",
2117
+ "maxItems": 4,
2118
+ "items": {
2119
+ "type": "string",
2120
+ "enum": [
2121
+ "most_proven",
2122
+ "more_availability"
2123
+ ]
2124
+ }
2125
+ }
2126
+ }
2127
+ },
2128
+ "ReservationOptionPage": {
2129
+ "type": "object",
2130
+ "additionalProperties": false,
2131
+ "required": [
2132
+ "offset",
2133
+ "limit",
2134
+ "total_available",
2135
+ "more_available",
2136
+ "options"
2137
+ ],
2138
+ "properties": {
2139
+ "offset": {
2140
+ "type": "integer",
2141
+ "minimum": 0
2142
+ },
2143
+ "limit": {
2144
+ "type": "integer",
2145
+ "minimum": 1,
2146
+ "maximum": 6
2147
+ },
2148
+ "total_available": {
2149
+ "type": "integer",
2150
+ "minimum": 0,
2151
+ "maximum": 30
2152
+ },
2153
+ "more_available": {
2154
+ "type": "integer",
2155
+ "minimum": 0,
2156
+ "maximum": 30
2157
+ },
2158
+ "next_offset": {
2159
+ "type": "integer",
2160
+ "minimum": 1,
2161
+ "maximum": 29
2162
+ },
2163
+ "options": {
2164
+ "type": "array",
2165
+ "maxItems": 6,
2166
+ "items": {
2167
+ "$ref": "#/components/schemas/ReservationCompactOption"
2168
+ }
2169
+ }
2170
+ }
2171
+ },
2172
+ "ReservationTurn": {
2173
+ "oneOf": [
2174
+ {
2175
+ "type": "object",
2176
+ "additionalProperties": false,
2177
+ "required": [
2178
+ "type",
2179
+ "message"
2180
+ ],
2181
+ "properties": {
2182
+ "type": {
2183
+ "enum": [
2184
+ "question",
2185
+ "message"
2186
+ ]
2187
+ },
2188
+ "message": {
2189
+ "type": "string"
2190
+ }
2191
+ }
2192
+ },
2193
+ {
2194
+ "type": "object",
2195
+ "additionalProperties": false,
2196
+ "required": [
2197
+ "type",
2198
+ "message",
2199
+ "locations"
2200
+ ],
2201
+ "properties": {
2202
+ "type": {
2203
+ "const": "location_options"
2204
+ },
2205
+ "message": {
2206
+ "type": "string"
2207
+ },
2208
+ "locations": {
2209
+ "type": "array",
2210
+ "maxItems": 5,
2211
+ "items": {
2212
+ "type": "object",
2213
+ "additionalProperties": false,
2214
+ "required": [
2215
+ "location_id",
2216
+ "label"
2217
+ ],
2218
+ "properties": {
2219
+ "location_id": {
2220
+ "$ref": "#/components/schemas/ReservationLocationId"
2221
+ },
2222
+ "label": {
2223
+ "type": "string"
2224
+ }
2225
+ }
2226
+ }
2227
+ }
2228
+ }
2229
+ },
2230
+ {
2231
+ "type": "object",
2232
+ "additionalProperties": false,
2233
+ "required": [
2234
+ "type",
2235
+ "message",
2236
+ "options",
2237
+ "more_available"
2238
+ ],
2239
+ "properties": {
2240
+ "type": {
2241
+ "const": "options"
2242
+ },
2243
+ "message": {
2244
+ "type": "string"
2245
+ },
2246
+ "more_available": {
2247
+ "type": "integer",
2248
+ "minimum": 0,
2249
+ "maximum": 30
2250
+ },
2251
+ "options": {
2252
+ "type": "array",
2253
+ "maxItems": 6,
2254
+ "items": {
2255
+ "$ref": "#/components/schemas/ReservationCompactOption"
2256
+ }
2257
+ }
2258
+ }
2259
+ },
2260
+ {
2261
+ "$ref": "#/components/schemas/ReservationConfirmationRequiredTurn"
2262
+ },
2263
+ {
2264
+ "$ref": "#/components/schemas/ReservationConfirmationTurn"
2265
+ },
2266
+ {
2267
+ "$ref": "#/components/schemas/ReservationCancellationTurn"
2268
+ }
2269
+ ]
2270
+ },
2271
+ "ReservationConfirmationRequiredTurn": {
2272
+ "type": "object",
2273
+ "additionalProperties": false,
2274
+ "required": [
2275
+ "type",
2276
+ "message",
2277
+ "confirmation_prompt"
2278
+ ],
2279
+ "properties": {
2280
+ "type": {
2281
+ "const": "confirmation_required"
2282
+ },
2283
+ "message": {
2284
+ "type": "string"
2285
+ },
2286
+ "confirmation_prompt": {
2287
+ "type": "object",
2288
+ "additionalProperties": false,
2289
+ "required": [
2290
+ "restaurant",
2291
+ "address",
2292
+ "date_time",
2293
+ "party_size",
2294
+ "option_id",
2295
+ "time_id",
2296
+ "diner"
2297
+ ],
2298
+ "properties": {
2299
+ "restaurant": {
2300
+ "type": "string"
2301
+ },
2302
+ "address": {
2303
+ "type": "string"
2304
+ },
2305
+ "date_time": {
2306
+ "type": "string",
2307
+ "format": "date-time"
2308
+ },
2309
+ "party_size": {
2310
+ "type": "integer",
2311
+ "minimum": 1
2312
+ },
2313
+ "option_id": {
2314
+ "$ref": "#/components/schemas/ReservationOptionId"
2315
+ },
2316
+ "time_id": {
2317
+ "$ref": "#/components/schemas/ReservationTimeId"
2318
+ },
2319
+ "diner": {
2320
+ "type": "object",
2321
+ "additionalProperties": false,
2322
+ "required": [
2323
+ "first_name",
2324
+ "last_name",
2325
+ "phone_number",
2326
+ "country"
2327
+ ],
2328
+ "properties": {
2329
+ "first_name": {
2330
+ "type": "string"
2331
+ },
2332
+ "last_name": {
2333
+ "type": "string"
2334
+ },
2335
+ "phone_number": {
2336
+ "type": "string"
2337
+ },
2338
+ "country": {
2339
+ "type": "string"
2340
+ }
2341
+ }
2342
+ }
2343
+ }
2344
+ }
2345
+ }
2346
+ },
2347
+ "ReservationConfirmationTurn": {
2348
+ "type": "object",
2349
+ "additionalProperties": false,
2350
+ "required": [
2351
+ "type",
2352
+ "message",
2353
+ "confirmation"
2354
+ ],
2355
+ "properties": {
2356
+ "type": {
2357
+ "const": "confirmation"
2358
+ },
2359
+ "message": {
2360
+ "type": "string"
2361
+ },
2362
+ "confirmation": {
2363
+ "type": "object",
2364
+ "additionalProperties": false,
2365
+ "required": [
2366
+ "confirmation_number",
2367
+ "restaurant",
2368
+ "date_time",
2369
+ "party_size",
2370
+ "status"
2371
+ ],
2372
+ "properties": {
2373
+ "confirmation_number": {
2374
+ "type": "string"
2375
+ },
2376
+ "restaurant": {
2377
+ "type": "string"
2378
+ },
2379
+ "date_time": {
2380
+ "type": "string",
2381
+ "format": "date-time"
2382
+ },
2383
+ "party_size": {
2384
+ "type": "integer",
2385
+ "minimum": 1
2386
+ },
2387
+ "status": {
2388
+ "type": "string",
2389
+ "description": "Shopstack booking outcome, confirmed. Provider lifecycle labels are not returned as this status."
2390
+ }
2391
+ }
2392
+ }
2393
+ }
2394
+ },
2395
+ "ReservationCancellationTurn": {
2396
+ "type": "object",
2397
+ "additionalProperties": false,
2398
+ "required": [
2399
+ "type",
2400
+ "message",
2401
+ "cancellation"
2402
+ ],
2403
+ "properties": {
2404
+ "type": {
2405
+ "const": "cancellation"
2406
+ },
2407
+ "message": {
2408
+ "type": "string"
2409
+ },
2410
+ "cancellation": {
2411
+ "type": "object",
2412
+ "additionalProperties": false,
2413
+ "required": [
2414
+ "confirmation_number",
2415
+ "status"
2416
+ ],
2417
+ "properties": {
2418
+ "confirmation_number": {
2419
+ "type": "string"
2420
+ },
2421
+ "status": {
2422
+ "type": "string",
2423
+ "description": "Shopstack cancellation outcome, cancelled."
2424
+ }
2425
+ }
2426
+ }
2427
+ }
2428
+ },
2429
+ "ReservationStatus": {
2430
+ "type": "string",
2431
+ "enum": [
2432
+ "active",
2433
+ "confirmation_required",
2434
+ "confirmed",
2435
+ "cancelled",
2436
+ "booking_unknown",
2437
+ "cancellation_unknown"
2438
+ ]
2439
+ },
2440
+ "Reservation": {
2441
+ "type": "object",
2442
+ "additionalProperties": false,
2443
+ "description": "Search expires at expires_at. Submitted bookings remain readable and, when supported by the provider, cancellable after search expiry. This release adds no booking access cutoff. Unknown booking outcomes are never automatically resubmitted.",
2444
+ "required": [
2445
+ "id",
2446
+ "status",
2447
+ "revision",
2448
+ "turn",
2449
+ "created_at",
2450
+ "updated_at",
2451
+ "expires_at"
2452
+ ],
2453
+ "properties": {
2454
+ "id": {
2455
+ "$ref": "#/components/schemas/ReservationId"
2456
+ },
2457
+ "revision": {
2458
+ "type": "integer",
2459
+ "minimum": 1
2460
+ },
2461
+ "status": {
2462
+ "$ref": "#/components/schemas/ReservationStatus"
2463
+ },
2464
+ "turn": {
2465
+ "$ref": "#/components/schemas/ReservationTurn"
2466
+ },
2467
+ "created_at": {
2468
+ "type": "string",
2469
+ "format": "date-time"
2470
+ },
2471
+ "updated_at": {
2472
+ "type": "string",
2473
+ "format": "date-time"
2474
+ },
2475
+ "expires_at": {
2476
+ "type": "string",
2477
+ "format": "date-time",
2478
+ "description": "Search conversation deadline; not the confirmed booking lifetime."
2479
+ }
2480
+ }
2481
+ },
2482
+ "CheckoutId": {
2483
+ "type": "string",
2484
+ "pattern": "^chk_[A-Za-z0-9_-]{10,80}$"
2485
+ },
2486
+ "MessageId": {
2487
+ "type": "string",
2488
+ "pattern": "^msg_[A-Za-z0-9_-]{10,80}$"
2489
+ },
2490
+ "ApprovalId": {
2491
+ "type": "string",
2492
+ "pattern": "^apr_[A-Za-z0-9_-]{10,80}$"
2493
+ },
2494
+ "EventId": {
2495
+ "type": "string",
2496
+ "pattern": "^evt_[A-Za-z0-9_-]{10,80}$"
2497
+ },
2498
+ "Money": {
2499
+ "type": "string",
2500
+ "pattern": "^(0|[1-9][0-9]{0,9})\\.[0-9]{2}$"
2501
+ },
2502
+ "Currency": {
2503
+ "type": "string",
2504
+ "pattern": "^[A-Z]{3}$"
2505
+ },
2506
+ "CheckoutStatus": {
2507
+ "type": "string",
2508
+ "enum": [
2509
+ "queued",
2510
+ "started",
2511
+ "help_required",
2512
+ "approval_required",
2513
+ "submitting",
2514
+ "complete",
2515
+ "failed",
2516
+ "cancelled"
2517
+ ]
2518
+ },
2519
+ "CheckoutIntent": {
2520
+ "description": "Bounded mechanical runner action progress; never includes action arguments or model reasoning.",
2521
+ "type": "object",
2522
+ "additionalProperties": false,
2523
+ "required": [
2524
+ "name",
2525
+ "phase",
2526
+ "updated_at"
2527
+ ],
2528
+ "properties": {
2529
+ "name": {
2530
+ "type": "string",
2531
+ "pattern": "^[a-z][a-z0-9_]{0,63}$"
2532
+ },
2533
+ "phase": {
2534
+ "type": "string",
2535
+ "enum": [
2536
+ "selected",
2537
+ "completed",
2538
+ "uncertain",
2539
+ "failed"
2540
+ ]
2541
+ },
2542
+ "updated_at": {
2543
+ "type": "string",
2544
+ "format": "date-time"
2545
+ }
2546
+ }
2547
+ },
2548
+ "ShippingAddress": {
2549
+ "type": "object",
2550
+ "additionalProperties": false,
2551
+ "required": [
2552
+ "line1",
2553
+ "city",
2554
+ "postal_code",
2555
+ "country"
2556
+ ],
2557
+ "properties": {
2558
+ "line1": {
2559
+ "type": "string",
2560
+ "minLength": 1,
2561
+ "maxLength": 240
2562
+ },
2563
+ "line2": {
2564
+ "type": "string",
2565
+ "maxLength": 240
2566
+ },
2567
+ "suburb": {
2568
+ "type": "string",
2569
+ "maxLength": 120
2570
+ },
2571
+ "city": {
2572
+ "type": "string",
2573
+ "minLength": 1,
2574
+ "maxLength": 120
2575
+ },
2576
+ "region": {
2577
+ "type": "string",
2578
+ "maxLength": 120
2579
+ },
2580
+ "postal_code": {
2581
+ "type": "string",
2582
+ "minLength": 1,
2583
+ "maxLength": 32
2584
+ },
2585
+ "country": {
2586
+ "type": "string",
2587
+ "pattern": "^[A-Z]{2}$"
2588
+ }
2589
+ }
2590
+ },
2591
+ "Customer": {
2592
+ "type": "object",
2593
+ "additionalProperties": false,
2594
+ "required": [
2595
+ "name",
2596
+ "shipping_address"
2597
+ ],
2598
+ "properties": {
2599
+ "email": {
2600
+ "anyOf": [
2601
+ {
2602
+ "type": "string",
2603
+ "format": "email",
2604
+ "maxLength": 320
2605
+ },
2606
+ {
2607
+ "const": ""
2608
+ }
2609
+ ],
2610
+ "description": "When omitted or blank, the runner uses one checkout-scoped temporary inbox for required email entry."
2611
+ },
2612
+ "phone": {
2613
+ "type": "string",
2614
+ "maxLength": 40
2615
+ },
2616
+ "name": {
2617
+ "type": "string",
2618
+ "minLength": 1,
2619
+ "maxLength": 200
2620
+ },
2621
+ "shipping_address": {
2622
+ "$ref": "#/components/schemas/ShippingAddress"
2623
+ }
2624
+ }
2625
+ },
2626
+ "ProxyRequest": {
2627
+ "type": "object",
2628
+ "additionalProperties": false,
2629
+ "properties": {
2630
+ "country": {
2631
+ "type": "string",
2632
+ "pattern": "^[A-Z]{2}$"
2633
+ },
2634
+ "locale": {
2635
+ "type": "string",
2636
+ "minLength": 2,
2637
+ "maxLength": 35,
2638
+ "pattern": "^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$",
2639
+ "description": "Explicit browser locale; it is never inferred from country or retailer content."
2640
+ }
2641
+ }
2642
+ },
2643
+ "CreateCheckoutRequest": {
2644
+ "type": "object",
2645
+ "additionalProperties": false,
2646
+ "required": [
2647
+ "item_url",
2648
+ "customer",
2649
+ "maximum_amount",
2650
+ "currency"
2651
+ ],
2652
+ "properties": {
2653
+ "item_url": {
2654
+ "type": "string",
2655
+ "format": "uri",
2656
+ "maxLength": 2048
2657
+ },
2658
+ "instructions": {
2659
+ "type": "string",
2660
+ "maxLength": 4000
2661
+ },
2662
+ "customer": {
2663
+ "$ref": "#/components/schemas/Customer"
2664
+ },
2665
+ "maximum_amount": {
2666
+ "$ref": "#/components/schemas/Money"
2667
+ },
2668
+ "currency": {
2669
+ "$ref": "#/components/schemas/Currency"
2670
+ },
2671
+ "payment_provider": {
2672
+ "$ref": "#/components/schemas/PaymentProvider",
2673
+ "description": "Optional. Select Link only when this user has an active Link connection; otherwise the checkout requests protected one-checkout card input at the payment form."
2674
+ },
2675
+ "external_reference": {
2676
+ "type": "string",
2677
+ "maxLength": 255
2678
+ },
2679
+ "model_handle": {
2680
+ "type": "string",
2681
+ "enum": [
2682
+ "qwen"
2683
+ ],
2684
+ "description": "Optional idempotent selector for the fixed product Qwen route. Omit it to use the same default route."
2685
+ },
2686
+ "proxy": {
2687
+ "$ref": "#/components/schemas/ProxyRequest"
2688
+ }
2689
+ }
2690
+ },
2691
+ "PaymentApproval": {
2692
+ "type": "object",
2693
+ "additionalProperties": false,
2694
+ "required": [
2695
+ "id",
2696
+ "subtotal",
2697
+ "shipping",
2698
+ "tax",
2699
+ "total",
2700
+ "currency",
2701
+ "expires_at"
2702
+ ],
2703
+ "properties": {
2704
+ "id": {
2705
+ "$ref": "#/components/schemas/ApprovalId"
2706
+ },
2707
+ "subtotal": {
2708
+ "oneOf": [
2709
+ {
2710
+ "$ref": "#/components/schemas/Money"
2711
+ },
2712
+ {
2713
+ "type": "null"
2714
+ }
2715
+ ]
2716
+ },
2717
+ "shipping": {
2718
+ "oneOf": [
2719
+ {
2720
+ "$ref": "#/components/schemas/Money"
2721
+ },
2722
+ {
2723
+ "type": "null"
2724
+ }
2725
+ ]
2726
+ },
2727
+ "tax": {
2728
+ "oneOf": [
2729
+ {
2730
+ "$ref": "#/components/schemas/Money"
2731
+ },
2732
+ {
2733
+ "type": "null"
2734
+ }
2735
+ ]
2736
+ },
2737
+ "total": {
2738
+ "$ref": "#/components/schemas/Money"
2739
+ },
2740
+ "currency": {
2741
+ "$ref": "#/components/schemas/Currency"
2742
+ },
2743
+ "expires_at": {
2744
+ "type": "string",
2745
+ "format": "date-time"
2746
+ }
2747
+ }
2748
+ },
2749
+ "PaymentDetailsRequest": {
2750
+ "type": "object",
2751
+ "additionalProperties": false,
2752
+ "required": [
2753
+ "card"
2754
+ ],
2755
+ "properties": {
2756
+ "card": {
2757
+ "type": "object",
2758
+ "additionalProperties": false,
2759
+ "required": [
2760
+ "number",
2761
+ "exp_month",
2762
+ "exp_year",
2763
+ "cvc"
2764
+ ],
2765
+ "properties": {
2766
+ "number": {
2767
+ "type": "string",
2768
+ "pattern": "^[0-9]{12,19}$",
2769
+ "writeOnly": true
2770
+ },
2771
+ "exp_month": {
2772
+ "type": "integer",
2773
+ "minimum": 1,
2774
+ "maximum": 12,
2775
+ "writeOnly": true
2776
+ },
2777
+ "exp_year": {
2778
+ "type": "integer",
2779
+ "minimum": 2000,
2780
+ "maximum": 2200,
2781
+ "writeOnly": true
2782
+ },
2783
+ "cvc": {
2784
+ "type": "string",
2785
+ "pattern": "^[0-9]{3,4}$",
2786
+ "writeOnly": true
2787
+ }
2788
+ }
2789
+ }
2790
+ }
2791
+ },
2792
+ "RequiredInput": {
2793
+ "type": "object",
2794
+ "additionalProperties": false,
2795
+ "required": [
2796
+ "type"
2797
+ ],
2798
+ "properties": {
2799
+ "type": {
2800
+ "const": "payment_card"
2801
+ }
2802
+ }
2803
+ },
2804
+ "CreatedAccount": {
2805
+ "type": "object",
2806
+ "additionalProperties": false,
2807
+ "required": [
2808
+ "email",
2809
+ "password"
2810
+ ],
2811
+ "properties": {
2812
+ "email": {
2813
+ "type": "string",
2814
+ "format": "email",
2815
+ "maxLength": 320
2816
+ },
2817
+ "password": {
2818
+ "oneOf": [
2819
+ {
2820
+ "type": "string",
2821
+ "minLength": 8,
2822
+ "maxLength": 128
2823
+ },
2824
+ {
2825
+ "type": "null"
2826
+ }
2827
+ ]
2828
+ }
2829
+ }
2830
+ },
2831
+ "CheckoutResult": {
2832
+ "type": "object",
2833
+ "additionalProperties": false,
2834
+ "anyOf": [
2835
+ {
2836
+ "required": [
2837
+ "created_account"
2838
+ ]
2839
+ },
2840
+ {
2841
+ "required": [
2842
+ "order_number",
2843
+ "confirmation_url",
2844
+ "subtotal",
2845
+ "shipping",
2846
+ "tax",
2847
+ "total",
2848
+ "currency"
2849
+ ]
2850
+ }
2851
+ ],
2852
+ "properties": {
2853
+ "created_account": {
2854
+ "$ref": "#/components/schemas/CreatedAccount"
2855
+ },
2856
+ "order_number": {
2857
+ "oneOf": [
2858
+ {
2859
+ "type": "string",
2860
+ "maxLength": 255
2861
+ },
2862
+ {
2863
+ "type": "null"
2864
+ }
2865
+ ]
2866
+ },
2867
+ "confirmation_url": {
2868
+ "oneOf": [
2869
+ {
2870
+ "type": "string",
2871
+ "format": "uri",
2872
+ "maxLength": 2048
2873
+ },
2874
+ {
2875
+ "type": "null"
2876
+ }
2877
+ ]
2878
+ },
2879
+ "subtotal": {
2880
+ "oneOf": [
2881
+ {
2882
+ "$ref": "#/components/schemas/Money"
2883
+ },
2884
+ {
2885
+ "type": "null"
2886
+ }
2887
+ ]
2888
+ },
2889
+ "shipping": {
2890
+ "oneOf": [
2891
+ {
2892
+ "$ref": "#/components/schemas/Money"
2893
+ },
2894
+ {
2895
+ "type": "null"
2896
+ }
2897
+ ]
2898
+ },
2899
+ "tax": {
2900
+ "oneOf": [
2901
+ {
2902
+ "$ref": "#/components/schemas/Money"
2903
+ },
2904
+ {
2905
+ "type": "null"
2906
+ }
2907
+ ]
2908
+ },
2909
+ "total": {
2910
+ "$ref": "#/components/schemas/Money"
2911
+ },
2912
+ "currency": {
2913
+ "$ref": "#/components/schemas/Currency"
2914
+ }
2915
+ }
2916
+ },
2917
+ "CheckoutFailure": {
2918
+ "type": "object",
2919
+ "additionalProperties": false,
2920
+ "required": [
2921
+ "code",
2922
+ "message"
2923
+ ],
2924
+ "properties": {
2925
+ "code": {
2926
+ "type": "string",
2927
+ "pattern": "^[a-z0-9_]{1,80}$"
2928
+ },
2929
+ "message": {
2930
+ "type": "string",
2931
+ "minLength": 1,
2932
+ "maxLength": 500
2933
+ },
2934
+ "diagnostic": {
2935
+ "description": "Present only when private evidence finalization fails. It contains bounded mechanical upload state and no checkout or credential data.",
2936
+ "$ref": "#/components/schemas/ArtifactFinalizationFailure"
2937
+ }
2938
+ }
2939
+ },
2940
+ "ArtifactFinalizationFailure": {
2941
+ "type": "object",
2942
+ "additionalProperties": false,
2943
+ "required": [
2944
+ "stage",
2945
+ "exception_category",
2946
+ "retry_count",
2947
+ "finalization_state",
2948
+ "missing_sequence"
2949
+ ],
2950
+ "properties": {
2951
+ "stage": {
2952
+ "type": "string",
2953
+ "enum": [
2954
+ "read",
2955
+ "create",
2956
+ "validate_contract",
2957
+ "put",
2958
+ "complete",
2959
+ "trace_read",
2960
+ "trace_validate",
2961
+ "trace_append"
2962
+ ]
2963
+ },
2964
+ "exception_category": {
2965
+ "type": "string",
2966
+ "enum": [
2967
+ "filesystem",
2968
+ "network",
2969
+ "timeout",
2970
+ "http",
2971
+ "contract",
2972
+ "runtime"
2973
+ ]
2974
+ },
2975
+ "retry_count": {
2976
+ "type": "integer",
2977
+ "minimum": 0,
2978
+ "maximum": 2
2979
+ },
2980
+ "finalization_state": {
2981
+ "type": "string",
2982
+ "enum": [
2983
+ "unavailable"
2984
+ ]
2985
+ },
2986
+ "missing_sequence": {
2987
+ "description": "First trace sequence not durably accepted when the failure occurred; null for non-trace artifact stages.",
2988
+ "type": [
2989
+ "integer",
2990
+ "null"
2991
+ ],
2992
+ "minimum": 1,
2993
+ "maximum": 100000
2994
+ }
2995
+ }
2996
+ },
2997
+ "Checkout": {
2998
+ "type": "object",
2999
+ "additionalProperties": false,
3000
+ "required": [
3001
+ "id",
3002
+ "status",
3003
+ "revision",
3004
+ "presentation_revision",
3005
+ "activity",
3006
+ "item_url",
3007
+ "created_at",
3008
+ "updated_at",
3009
+ "expires_at"
3010
+ ],
3011
+ "properties": {
3012
+ "id": {
3013
+ "$ref": "#/components/schemas/CheckoutId"
3014
+ },
3015
+ "status": {
3016
+ "$ref": "#/components/schemas/CheckoutStatus"
3017
+ },
3018
+ "revision": {
3019
+ "type": "integer",
3020
+ "minimum": 1
3021
+ },
3022
+ "presentation_revision": {
3023
+ "description": "Monotonic version of the current checkout representation. It may advance for bounded progress changes without changing the authoritative lifecycle revision or public status.",
3024
+ "type": "integer",
3025
+ "minimum": 1
3026
+ },
3027
+ "activity": {
3028
+ "type": "string",
3029
+ "maxLength": 160
3030
+ },
3031
+ "intent": {
3032
+ "$ref": "#/components/schemas/CheckoutIntent"
3033
+ },
3034
+ "item_url": {
3035
+ "type": "string",
3036
+ "format": "uri"
3037
+ },
3038
+ "live_view_url": {
3039
+ "type": "string",
3040
+ "format": "uri"
3041
+ },
3042
+ "approval": {
3043
+ "$ref": "#/components/schemas/PaymentApproval"
3044
+ },
3045
+ "required_input": {
3046
+ "$ref": "#/components/schemas/RequiredInput"
3047
+ },
3048
+ "result": {
3049
+ "description": "Available purchase facts and, when mandatory signup created a merchant account, the checkout-scoped account credentials. Created account credentials may be present before or after a terminal checkout status and are returned only to an authenticated owning caller with checkout:read.",
3050
+ "$ref": "#/components/schemas/CheckoutResult"
3051
+ },
3052
+ "failure": {
3053
+ "$ref": "#/components/schemas/CheckoutFailure"
3054
+ },
3055
+ "created_at": {
3056
+ "type": "string",
3057
+ "format": "date-time"
3058
+ },
3059
+ "updated_at": {
3060
+ "type": "string",
3061
+ "format": "date-time"
3062
+ },
3063
+ "expires_at": {
3064
+ "type": "string",
3065
+ "format": "date-time"
3066
+ }
3067
+ }
3068
+ },
3069
+ "CheckoutUpdateSubscription": {
3070
+ "type": "object",
3071
+ "additionalProperties": false,
3072
+ "required": [
3073
+ "socket_url",
3074
+ "protocol",
3075
+ "token",
3076
+ "expires_at",
3077
+ "presentation_revision"
3078
+ ],
3079
+ "properties": {
3080
+ "socket_url": {
3081
+ "type": "string",
3082
+ "format": "uri"
3083
+ },
3084
+ "protocol": {
3085
+ "const": "shopstack.v1"
3086
+ },
3087
+ "token": {
3088
+ "type": "string",
3089
+ "pattern": "^cus_v1\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$",
3090
+ "writeOnly": true
3091
+ },
3092
+ "expires_at": {
3093
+ "type": "string",
3094
+ "format": "date-time"
3095
+ },
3096
+ "presentation_revision": {
3097
+ "type": "integer",
3098
+ "minimum": 1
3099
+ }
3100
+ }
3101
+ },
3102
+ "CheckoutAccepted": {
3103
+ "allOf": [
3104
+ {
3105
+ "$ref": "#/components/schemas/Checkout"
3106
+ },
3107
+ {
3108
+ "type": "object",
3109
+ "required": [
3110
+ "live_view_url"
3111
+ ]
3112
+ }
3113
+ ]
3114
+ },
3115
+ "LiveViewCapability": {
3116
+ "type": "object",
3117
+ "additionalProperties": false,
3118
+ "required": [
3119
+ "live_view_url"
3120
+ ],
3121
+ "properties": {
3122
+ "live_view_url": {
3123
+ "description": "Exact owner-only one-time URL. Clients must present this value verbatim and must never reconstruct it from the checkout ID. The redeemed viewer may stop the checkout and may satisfy one active typed payment-card request, but cannot approve or submit payment.",
3124
+ "type": "string",
3125
+ "format": "uri",
3126
+ "writeOnly": true
3127
+ }
3128
+ }
3129
+ },
3130
+ "SendMessageRequest": {
3131
+ "type": "object",
3132
+ "additionalProperties": false,
3133
+ "required": [
3134
+ "content"
3135
+ ],
3136
+ "properties": {
3137
+ "content": {
3138
+ "type": "string",
3139
+ "minLength": 1,
3140
+ "maxLength": 4000
3141
+ }
3142
+ }
3143
+ },
3144
+ "Message": {
3145
+ "type": "object",
3146
+ "additionalProperties": false,
3147
+ "required": [
3148
+ "id",
3149
+ "checkout_id",
3150
+ "sequence",
3151
+ "role",
3152
+ "content",
3153
+ "created_at"
3154
+ ],
3155
+ "properties": {
3156
+ "id": {
3157
+ "$ref": "#/components/schemas/MessageId"
3158
+ },
3159
+ "checkout_id": {
3160
+ "$ref": "#/components/schemas/CheckoutId"
3161
+ },
3162
+ "sequence": {
3163
+ "type": "integer",
3164
+ "minimum": 1
3165
+ },
3166
+ "role": {
3167
+ "type": "string",
3168
+ "enum": [
3169
+ "user",
3170
+ "assistant"
3171
+ ]
3172
+ },
3173
+ "content": {
3174
+ "type": "string",
3175
+ "minLength": 1,
3176
+ "maxLength": 4000
3177
+ },
3178
+ "created_at": {
3179
+ "type": "string",
3180
+ "format": "date-time"
3181
+ }
3182
+ }
3183
+ },
3184
+ "MessagePage": {
3185
+ "type": "object",
3186
+ "additionalProperties": false,
3187
+ "required": [
3188
+ "data",
3189
+ "has_more"
3190
+ ],
3191
+ "properties": {
3192
+ "data": {
3193
+ "type": "array",
3194
+ "items": {
3195
+ "$ref": "#/components/schemas/Message"
3196
+ }
3197
+ },
3198
+ "next_cursor": {
3199
+ "oneOf": [
3200
+ {
3201
+ "type": "string"
3202
+ },
3203
+ {
3204
+ "type": "null"
3205
+ }
3206
+ ]
3207
+ },
3208
+ "has_more": {
3209
+ "type": "boolean"
3210
+ }
3211
+ }
3212
+ },
3213
+ "PaymentApprovalRequest": {
3214
+ "type": "object",
3215
+ "additionalProperties": false,
3216
+ "required": [
3217
+ "approval_id",
3218
+ "approved"
3219
+ ],
3220
+ "properties": {
3221
+ "approval_id": {
3222
+ "$ref": "#/components/schemas/ApprovalId"
3223
+ },
3224
+ "approved": {
3225
+ "type": "boolean"
3226
+ }
3227
+ }
3228
+ },
3229
+ "LifecycleEvent": {
3230
+ "type": "object",
3231
+ "additionalProperties": false,
3232
+ "required": [
3233
+ "id",
3234
+ "checkout_id",
3235
+ "revision",
3236
+ "status",
3237
+ "occurred_at"
3238
+ ],
3239
+ "properties": {
3240
+ "id": {
3241
+ "$ref": "#/components/schemas/EventId"
3242
+ },
3243
+ "checkout_id": {
3244
+ "$ref": "#/components/schemas/CheckoutId"
3245
+ },
3246
+ "revision": {
3247
+ "type": "integer",
3248
+ "minimum": 1
3249
+ },
3250
+ "status": {
3251
+ "$ref": "#/components/schemas/CheckoutStatus"
3252
+ },
3253
+ "occurred_at": {
3254
+ "type": "string",
3255
+ "format": "date-time"
3256
+ }
3257
+ }
3258
+ },
3259
+ "EventPage": {
3260
+ "type": "object",
3261
+ "additionalProperties": false,
3262
+ "required": [
3263
+ "data",
3264
+ "has_more"
3265
+ ],
3266
+ "properties": {
3267
+ "data": {
3268
+ "type": "array",
3269
+ "items": {
3270
+ "$ref": "#/components/schemas/LifecycleEvent"
3271
+ }
3272
+ },
3273
+ "next_cursor": {
3274
+ "oneOf": [
3275
+ {
3276
+ "type": "string"
3277
+ },
3278
+ {
3279
+ "type": "null"
3280
+ }
3281
+ ]
3282
+ },
3283
+ "has_more": {
3284
+ "type": "boolean"
3285
+ }
3286
+ }
3287
+ },
3288
+ "ErrorResponse": {
3289
+ "type": "object",
3290
+ "additionalProperties": false,
3291
+ "required": [
3292
+ "error"
3293
+ ],
3294
+ "properties": {
3295
+ "error": {
3296
+ "type": "object",
3297
+ "additionalProperties": false,
3298
+ "required": [
3299
+ "code",
3300
+ "message",
3301
+ "request_id"
3302
+ ],
3303
+ "properties": {
3304
+ "code": {
3305
+ "type": "string",
3306
+ "pattern": "^[a-z0-9_]{1,80}$"
3307
+ },
3308
+ "message": {
3309
+ "type": "string",
3310
+ "maxLength": 500
3311
+ },
3312
+ "request_id": {
3313
+ "type": "string",
3314
+ "maxLength": 128
3315
+ }
3316
+ }
3317
+ }
3318
+ }
3319
+ }
3320
+ }
3321
+ }
3322
+ }