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