@offerkit/sdk 0.2.2 → 0.2.4
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.
- package/README.md +84 -0
- package/dist/index.d.mts +837 -282
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +548 -280
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
package/dist/index.mjs
CHANGED
|
@@ -38,8 +38,9 @@ const apiKeys = {
|
|
|
38
38
|
revoke: oc.route({
|
|
39
39
|
method: "DELETE",
|
|
40
40
|
path: "/api-keys/{id}",
|
|
41
|
-
summary: "Disable an API key (cannot be re-enabled)"
|
|
42
|
-
|
|
41
|
+
summary: "Disable an API key (cannot be re-enabled)",
|
|
42
|
+
inputStructure: "detailed"
|
|
43
|
+
}).input(z.object({ params: z.object({ id: z.string() }) })).output(z.object({ ok: z.literal(true) }))
|
|
43
44
|
};
|
|
44
45
|
//#endregion
|
|
45
46
|
//#region ../contract/src/mcp.ts
|
|
@@ -140,8 +141,9 @@ const campaigns = {
|
|
|
140
141
|
})).route({
|
|
141
142
|
method: "GET",
|
|
142
143
|
path: "/campaigns/{id}",
|
|
143
|
-
summary: "Get campaign"
|
|
144
|
-
|
|
144
|
+
summary: "Get campaign",
|
|
145
|
+
inputStructure: "detailed"
|
|
146
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(campaignOutput),
|
|
145
147
|
create: oc.route({
|
|
146
148
|
method: "POST",
|
|
147
149
|
path: "/campaigns",
|
|
@@ -150,16 +152,18 @@ const campaigns = {
|
|
|
150
152
|
update: oc.route({
|
|
151
153
|
method: "PATCH",
|
|
152
154
|
path: "/campaigns/{id}",
|
|
153
|
-
summary: "Update campaign"
|
|
155
|
+
summary: "Update campaign",
|
|
156
|
+
inputStructure: "detailed"
|
|
154
157
|
}).input(z.object({
|
|
155
|
-
id: z.string().uuid(),
|
|
156
|
-
patch: campaignUpdateInput
|
|
158
|
+
params: z.object({ id: z.string().uuid() }),
|
|
159
|
+
body: z.object({ patch: campaignUpdateInput })
|
|
157
160
|
})).output(campaignOutput),
|
|
158
161
|
delete: oc.route({
|
|
159
162
|
method: "DELETE",
|
|
160
163
|
path: "/campaigns/{id}",
|
|
161
|
-
summary: "Soft-delete campaign"
|
|
162
|
-
|
|
164
|
+
summary: "Soft-delete campaign",
|
|
165
|
+
inputStructure: "detailed"
|
|
166
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
163
167
|
};
|
|
164
168
|
//#endregion
|
|
165
169
|
//#region ../contract/src/schemas/customer.ts
|
|
@@ -226,16 +230,18 @@ const customers = {
|
|
|
226
230
|
})).route({
|
|
227
231
|
method: "GET",
|
|
228
232
|
path: "/customers/{id}",
|
|
229
|
-
summary: "Get customer"
|
|
230
|
-
|
|
233
|
+
summary: "Get customer",
|
|
234
|
+
inputStructure: "detailed"
|
|
235
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(customerOutput),
|
|
231
236
|
getByExternalId: oc.meta(mcpMeta({
|
|
232
237
|
expose: true,
|
|
233
238
|
riskLevel: "safe"
|
|
234
239
|
})).route({
|
|
235
240
|
method: "GET",
|
|
236
241
|
path: "/customers/by-external-id/{externalId}",
|
|
237
|
-
summary: "Get a customer by integrator-supplied externalId"
|
|
238
|
-
|
|
242
|
+
summary: "Get a customer by integrator-supplied externalId",
|
|
243
|
+
inputStructure: "detailed"
|
|
244
|
+
}).input(z.object({ params: z.object({ externalId: z.string().min(1).max(256) }) })).output(customerOutput),
|
|
239
245
|
create: oc.route({
|
|
240
246
|
method: "POST",
|
|
241
247
|
path: "/customers",
|
|
@@ -249,16 +255,18 @@ const customers = {
|
|
|
249
255
|
update: oc.route({
|
|
250
256
|
method: "PATCH",
|
|
251
257
|
path: "/customers/{id}",
|
|
252
|
-
summary: "Update customer"
|
|
258
|
+
summary: "Update customer",
|
|
259
|
+
inputStructure: "detailed"
|
|
253
260
|
}).input(z.object({
|
|
254
|
-
id: z.string().uuid(),
|
|
255
|
-
patch: customerUpdateInput
|
|
261
|
+
params: z.object({ id: z.string().uuid() }),
|
|
262
|
+
body: z.object({ patch: customerUpdateInput })
|
|
256
263
|
})).output(customerOutput),
|
|
257
264
|
delete: oc.route({
|
|
258
265
|
method: "DELETE",
|
|
259
266
|
path: "/customers/{id}",
|
|
260
|
-
summary: "Soft-delete customer"
|
|
261
|
-
|
|
267
|
+
summary: "Soft-delete customer",
|
|
268
|
+
inputStructure: "detailed"
|
|
269
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
262
270
|
};
|
|
263
271
|
//#endregion
|
|
264
272
|
//#region ../contract/src/schemas/loyalty.ts
|
|
@@ -416,8 +424,9 @@ const loyalty = {
|
|
|
416
424
|
get: oc.route({
|
|
417
425
|
method: "GET",
|
|
418
426
|
path: "/loyalty/programs/{id}",
|
|
419
|
-
summary: "Get loyalty program"
|
|
420
|
-
|
|
427
|
+
summary: "Get loyalty program",
|
|
428
|
+
inputStructure: "detailed"
|
|
429
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(loyaltyProgramOutput),
|
|
421
430
|
create: oc.route({
|
|
422
431
|
method: "POST",
|
|
423
432
|
path: "/loyalty/programs",
|
|
@@ -426,23 +435,26 @@ const loyalty = {
|
|
|
426
435
|
update: oc.route({
|
|
427
436
|
method: "PATCH",
|
|
428
437
|
path: "/loyalty/programs/{id}",
|
|
429
|
-
summary: "Update loyalty program"
|
|
438
|
+
summary: "Update loyalty program",
|
|
439
|
+
inputStructure: "detailed"
|
|
430
440
|
}).input(z.object({
|
|
431
|
-
id: z.string().uuid(),
|
|
432
|
-
patch: loyaltyProgramUpdateInput
|
|
441
|
+
params: z.object({ id: z.string().uuid() }),
|
|
442
|
+
body: z.object({ patch: loyaltyProgramUpdateInput })
|
|
433
443
|
})).output(loyaltyProgramOutput),
|
|
434
444
|
delete: oc.route({
|
|
435
445
|
method: "DELETE",
|
|
436
446
|
path: "/loyalty/programs/{id}",
|
|
437
|
-
summary: "Soft-delete program"
|
|
438
|
-
|
|
447
|
+
summary: "Soft-delete program",
|
|
448
|
+
inputStructure: "detailed"
|
|
449
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
439
450
|
},
|
|
440
451
|
tiers: {
|
|
441
452
|
list: oc.route({
|
|
442
453
|
method: "GET",
|
|
443
454
|
path: "/loyalty/programs/{programId}/tiers",
|
|
444
|
-
summary: "List tiers"
|
|
445
|
-
|
|
455
|
+
summary: "List tiers",
|
|
456
|
+
inputStructure: "detailed"
|
|
457
|
+
}).input(z.object({ params: z.object({ programId: z.string().uuid() }) })).output(z.object({ data: z.array(loyaltyTierOutput) })),
|
|
446
458
|
create: oc.route({
|
|
447
459
|
method: "POST",
|
|
448
460
|
path: "/loyalty/tiers",
|
|
@@ -451,23 +463,26 @@ const loyalty = {
|
|
|
451
463
|
update: oc.route({
|
|
452
464
|
method: "PATCH",
|
|
453
465
|
path: "/loyalty/tiers/{id}",
|
|
454
|
-
summary: "Update tier"
|
|
466
|
+
summary: "Update tier",
|
|
467
|
+
inputStructure: "detailed"
|
|
455
468
|
}).input(z.object({
|
|
456
|
-
id: z.string().uuid(),
|
|
457
|
-
patch: loyaltyTierUpdateInput
|
|
469
|
+
params: z.object({ id: z.string().uuid() }),
|
|
470
|
+
body: z.object({ patch: loyaltyTierUpdateInput })
|
|
458
471
|
})).output(loyaltyTierOutput),
|
|
459
472
|
delete: oc.route({
|
|
460
473
|
method: "DELETE",
|
|
461
474
|
path: "/loyalty/tiers/{id}",
|
|
462
|
-
summary: "Delete tier"
|
|
463
|
-
|
|
475
|
+
summary: "Delete tier",
|
|
476
|
+
inputStructure: "detailed"
|
|
477
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
464
478
|
},
|
|
465
479
|
earningRules: {
|
|
466
480
|
list: oc.route({
|
|
467
481
|
method: "GET",
|
|
468
482
|
path: "/loyalty/programs/{programId}/earning-rules",
|
|
469
|
-
summary: "List earning rules"
|
|
470
|
-
|
|
483
|
+
summary: "List earning rules",
|
|
484
|
+
inputStructure: "detailed"
|
|
485
|
+
}).input(z.object({ params: z.object({ programId: z.string().uuid() }) })).output(z.object({ data: z.array(loyaltyEarningRuleOutput) })),
|
|
471
486
|
create: oc.route({
|
|
472
487
|
method: "POST",
|
|
473
488
|
path: "/loyalty/earning-rules",
|
|
@@ -476,23 +491,26 @@ const loyalty = {
|
|
|
476
491
|
update: oc.route({
|
|
477
492
|
method: "PATCH",
|
|
478
493
|
path: "/loyalty/earning-rules/{id}",
|
|
479
|
-
summary: "Update earning rule"
|
|
494
|
+
summary: "Update earning rule",
|
|
495
|
+
inputStructure: "detailed"
|
|
480
496
|
}).input(z.object({
|
|
481
|
-
id: z.string().uuid(),
|
|
482
|
-
patch: loyaltyEarningRuleUpdateInput
|
|
497
|
+
params: z.object({ id: z.string().uuid() }),
|
|
498
|
+
body: z.object({ patch: loyaltyEarningRuleUpdateInput })
|
|
483
499
|
})).output(loyaltyEarningRuleOutput),
|
|
484
500
|
delete: oc.route({
|
|
485
501
|
method: "DELETE",
|
|
486
502
|
path: "/loyalty/earning-rules/{id}",
|
|
487
|
-
summary: "Delete earning rule"
|
|
488
|
-
|
|
503
|
+
summary: "Delete earning rule",
|
|
504
|
+
inputStructure: "detailed"
|
|
505
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
489
506
|
},
|
|
490
507
|
rewards: {
|
|
491
508
|
list: oc.route({
|
|
492
509
|
method: "GET",
|
|
493
510
|
path: "/loyalty/programs/{programId}/rewards",
|
|
494
|
-
summary: "List rewards"
|
|
495
|
-
|
|
511
|
+
summary: "List rewards",
|
|
512
|
+
inputStructure: "detailed"
|
|
513
|
+
}).input(z.object({ params: z.object({ programId: z.string().uuid() }) })).output(z.object({ data: z.array(loyaltyRewardOutput) })),
|
|
496
514
|
create: oc.route({
|
|
497
515
|
method: "POST",
|
|
498
516
|
path: "/loyalty/rewards",
|
|
@@ -501,28 +519,35 @@ const loyalty = {
|
|
|
501
519
|
update: oc.route({
|
|
502
520
|
method: "PATCH",
|
|
503
521
|
path: "/loyalty/rewards/{id}",
|
|
504
|
-
summary: "Update reward"
|
|
522
|
+
summary: "Update reward",
|
|
523
|
+
inputStructure: "detailed"
|
|
505
524
|
}).input(z.object({
|
|
506
|
-
id: z.string().uuid(),
|
|
507
|
-
patch: loyaltyRewardUpdateInput
|
|
525
|
+
params: z.object({ id: z.string().uuid() }),
|
|
526
|
+
body: z.object({ patch: loyaltyRewardUpdateInput })
|
|
508
527
|
})).output(loyaltyRewardOutput),
|
|
509
528
|
delete: oc.route({
|
|
510
529
|
method: "DELETE",
|
|
511
530
|
path: "/loyalty/rewards/{id}",
|
|
512
|
-
summary: "Soft-delete reward"
|
|
513
|
-
|
|
531
|
+
summary: "Soft-delete reward",
|
|
532
|
+
inputStructure: "detailed"
|
|
533
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
514
534
|
},
|
|
515
535
|
members: {
|
|
516
536
|
list: oc.route({
|
|
517
537
|
method: "GET",
|
|
518
538
|
path: "/loyalty/programs/{programId}/members",
|
|
519
|
-
summary: "List members of a program"
|
|
520
|
-
|
|
539
|
+
summary: "List members of a program",
|
|
540
|
+
inputStructure: "detailed"
|
|
541
|
+
}).input(z.object({
|
|
542
|
+
params: z.object({ programId: z.string().uuid() }),
|
|
543
|
+
query: paginationInput
|
|
544
|
+
})).output(paginatedOutput(loyaltyMemberOutput)),
|
|
521
545
|
get: oc.route({
|
|
522
546
|
method: "GET",
|
|
523
547
|
path: "/loyalty/members/{id}",
|
|
524
|
-
summary: "Get member"
|
|
525
|
-
|
|
548
|
+
summary: "Get member",
|
|
549
|
+
inputStructure: "detailed"
|
|
550
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(loyaltyMemberOutput),
|
|
526
551
|
enroll: oc.route({
|
|
527
552
|
method: "POST",
|
|
528
553
|
path: "/loyalty/members",
|
|
@@ -574,8 +599,9 @@ const loyalty = {
|
|
|
574
599
|
})).route({
|
|
575
600
|
method: "GET",
|
|
576
601
|
path: "/loyalty/members/{id}/transactions",
|
|
577
|
-
summary: "Member transaction ledger"
|
|
578
|
-
|
|
602
|
+
summary: "Member transaction ledger",
|
|
603
|
+
inputStructure: "detailed"
|
|
604
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ data: z.array(loyaltyTransactionOutput) }))
|
|
579
605
|
}
|
|
580
606
|
};
|
|
581
607
|
//#endregion
|
|
@@ -690,8 +716,9 @@ const referrals = {
|
|
|
690
716
|
get: oc.route({
|
|
691
717
|
method: "GET",
|
|
692
718
|
path: "/referral-programs/{id}",
|
|
693
|
-
summary: "Get referral program"
|
|
694
|
-
|
|
719
|
+
summary: "Get referral program",
|
|
720
|
+
inputStructure: "detailed"
|
|
721
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(referralProgramOutput),
|
|
695
722
|
create: oc.route({
|
|
696
723
|
method: "POST",
|
|
697
724
|
path: "/referral-programs",
|
|
@@ -700,32 +727,43 @@ const referrals = {
|
|
|
700
727
|
update: oc.route({
|
|
701
728
|
method: "PATCH",
|
|
702
729
|
path: "/referral-programs/{id}",
|
|
703
|
-
summary: "Update referral program"
|
|
730
|
+
summary: "Update referral program",
|
|
731
|
+
inputStructure: "detailed"
|
|
704
732
|
}).input(z.object({
|
|
705
|
-
id: z.string().uuid(),
|
|
706
|
-
patch: referralProgramUpdateInput
|
|
733
|
+
params: z.object({ id: z.string().uuid() }),
|
|
734
|
+
body: z.object({ patch: referralProgramUpdateInput })
|
|
707
735
|
})).output(referralProgramOutput),
|
|
708
736
|
delete: oc.route({
|
|
709
737
|
method: "DELETE",
|
|
710
738
|
path: "/referral-programs/{id}",
|
|
711
|
-
summary: "Soft-delete referral program"
|
|
712
|
-
|
|
739
|
+
summary: "Soft-delete referral program",
|
|
740
|
+
inputStructure: "detailed"
|
|
741
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
713
742
|
},
|
|
714
743
|
listCodes: oc.route({
|
|
715
744
|
method: "GET",
|
|
716
745
|
path: "/referral-programs/{programId}/codes",
|
|
717
|
-
summary: "List referral codes in a program"
|
|
718
|
-
|
|
746
|
+
summary: "List referral codes in a program",
|
|
747
|
+
inputStructure: "detailed"
|
|
748
|
+
}).input(z.object({
|
|
749
|
+
params: z.object({ programId: z.string().uuid() }),
|
|
750
|
+
query: paginationInput
|
|
751
|
+
})).output(paginatedOutput(referralCodeOutput)),
|
|
719
752
|
listConversions: oc.route({
|
|
720
753
|
method: "GET",
|
|
721
754
|
path: "/referral-codes/{codeId}/conversions",
|
|
722
|
-
summary: "List conversions for a referral code"
|
|
723
|
-
|
|
755
|
+
summary: "List conversions for a referral code",
|
|
756
|
+
inputStructure: "detailed"
|
|
757
|
+
}).input(z.object({
|
|
758
|
+
params: z.object({ codeId: z.string().uuid() }),
|
|
759
|
+
query: paginationInput
|
|
760
|
+
})).output(paginatedOutput(referralConversionOutput)),
|
|
724
761
|
getByCode: oc.route({
|
|
725
762
|
method: "GET",
|
|
726
763
|
path: "/referrals/{code}",
|
|
727
|
-
summary: "Look up a referral code"
|
|
728
|
-
|
|
764
|
+
summary: "Look up a referral code",
|
|
765
|
+
inputStructure: "detailed"
|
|
766
|
+
}).input(z.object({ params: z.object({ code: z.string() }) })).output(referralCodeOutput),
|
|
729
767
|
issue: oc.route({
|
|
730
768
|
method: "POST",
|
|
731
769
|
path: "/referrals/issue",
|
|
@@ -738,6 +776,333 @@ const referrals = {
|
|
|
738
776
|
}).input(referralConvertInput).output(referralConvertOutput)
|
|
739
777
|
};
|
|
740
778
|
//#endregion
|
|
779
|
+
//#region ../contract/src/schemas/voucher.ts
|
|
780
|
+
const voucherType = z.enum(["DISCOUNT", "GIFT_CARD"]);
|
|
781
|
+
const voucherDiscount = z.object({
|
|
782
|
+
type: z.enum(["AMOUNT", "PERCENTAGE"]),
|
|
783
|
+
amount: z.number().int().min(0).optional(),
|
|
784
|
+
percent: z.number().int().min(0).max(1e4).optional(),
|
|
785
|
+
maxDiscountAmount: z.number().int().min(0).optional(),
|
|
786
|
+
appliesTo: z.object({
|
|
787
|
+
productIds: z.array(z.string()).optional(),
|
|
788
|
+
collectionIds: z.array(z.string()).optional()
|
|
789
|
+
}).optional()
|
|
790
|
+
});
|
|
791
|
+
const customReward = z.object({
|
|
792
|
+
typeKey: z.string().min(1),
|
|
793
|
+
payload: z.record(z.string(), z.unknown())
|
|
794
|
+
});
|
|
795
|
+
const voucherOutput = z.object({
|
|
796
|
+
id: z.string().uuid(),
|
|
797
|
+
code: z.string(),
|
|
798
|
+
campaignId: z.string().uuid().nullable(),
|
|
799
|
+
type: voucherType,
|
|
800
|
+
discount: voucherDiscount.nullable(),
|
|
801
|
+
customRewards: z.array(customReward),
|
|
802
|
+
giftBalance: z.number().int().nullable(),
|
|
803
|
+
redemptionLimit: z.number().int().nullable(),
|
|
804
|
+
redemptionCount: z.number().int(),
|
|
805
|
+
priority: z.number().int(),
|
|
806
|
+
exclusive: z.boolean(),
|
|
807
|
+
active: z.boolean(),
|
|
808
|
+
startDate: z.string().datetime().nullable(),
|
|
809
|
+
endDate: z.string().datetime().nullable(),
|
|
810
|
+
customerId: z.string().uuid().nullable(),
|
|
811
|
+
metadata: z.record(z.string(), z.unknown()),
|
|
812
|
+
createdAt: z.string().datetime(),
|
|
813
|
+
updatedAt: z.string().datetime()
|
|
814
|
+
});
|
|
815
|
+
const voucherCreateInput = z.object({
|
|
816
|
+
code: z.string().min(1).optional(),
|
|
817
|
+
campaignId: z.string().uuid().optional(),
|
|
818
|
+
type: voucherType,
|
|
819
|
+
discount: voucherDiscount.optional(),
|
|
820
|
+
customRewards: z.array(customReward).optional(),
|
|
821
|
+
giftBalance: z.number().int().min(0).optional(),
|
|
822
|
+
redemptionLimit: z.number().int().min(1).optional(),
|
|
823
|
+
priority: z.number().int().optional(),
|
|
824
|
+
exclusive: z.boolean().optional(),
|
|
825
|
+
startDate: z.string().datetime().optional(),
|
|
826
|
+
endDate: z.string().datetime().optional(),
|
|
827
|
+
customerId: z.string().uuid().optional(),
|
|
828
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
829
|
+
});
|
|
830
|
+
const voucherUpdateInput = voucherCreateInput.omit({
|
|
831
|
+
type: true,
|
|
832
|
+
code: true
|
|
833
|
+
}).partial().extend({ active: z.boolean().optional() });
|
|
834
|
+
const voucherBulkCreateInput = z.object({
|
|
835
|
+
campaignId: z.string().uuid(),
|
|
836
|
+
count: z.number().int().min(1).max(1e5)
|
|
837
|
+
});
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region ../contract/src/schemas/redemption.ts
|
|
840
|
+
const orderItem$1 = z.object({
|
|
841
|
+
productId: z.string(),
|
|
842
|
+
collectionId: z.string().optional(),
|
|
843
|
+
quantity: z.number().int().min(1),
|
|
844
|
+
unitPrice: z.number().int().min(0)
|
|
845
|
+
});
|
|
846
|
+
const orderInput = z.object({
|
|
847
|
+
amount: z.number().int().min(0),
|
|
848
|
+
currency: z.string().length(3),
|
|
849
|
+
items: z.array(orderItem$1).optional()
|
|
850
|
+
});
|
|
851
|
+
const validateInput = z.object({
|
|
852
|
+
code: z.string().min(1),
|
|
853
|
+
customerId: z.string().uuid().optional(),
|
|
854
|
+
order: orderInput.optional()
|
|
855
|
+
});
|
|
856
|
+
const redeemInput = validateInput.extend({
|
|
857
|
+
/** uuid of an `order` row created via the orders API. */
|
|
858
|
+
orderId: z.string().uuid().optional(),
|
|
859
|
+
/** Free-form integrator order reference (Shopify id, etc). */
|
|
860
|
+
externalOrderId: z.string().min(1).max(120).optional(),
|
|
861
|
+
idempotencyKey: z.string().min(1).max(128).optional()
|
|
862
|
+
});
|
|
863
|
+
const breakdownEntry = z.object({
|
|
864
|
+
voucherId: z.string().uuid(),
|
|
865
|
+
code: z.string(),
|
|
866
|
+
amount: z.number().int(),
|
|
867
|
+
type: z.enum(["AMOUNT", "PERCENTAGE"]).optional(),
|
|
868
|
+
reason: z.enum(["exclusivity_lost", "zero_after_running_total"]).optional()
|
|
869
|
+
});
|
|
870
|
+
const redemptionExplanation = z.object({
|
|
871
|
+
code: z.enum([
|
|
872
|
+
"voucher_not_found",
|
|
873
|
+
"voucher_disabled",
|
|
874
|
+
"voucher_expired",
|
|
875
|
+
"redemption_limit_reached",
|
|
876
|
+
"currency_mismatch",
|
|
877
|
+
"gift_balance_zero",
|
|
878
|
+
"order_required",
|
|
879
|
+
"exclusivity_lost",
|
|
880
|
+
"zero_after_running_total",
|
|
881
|
+
"gift_card_stacking_unsupported"
|
|
882
|
+
]),
|
|
883
|
+
message: z.string(),
|
|
884
|
+
voucherId: z.string().uuid().optional(),
|
|
885
|
+
voucherCode: z.string().optional(),
|
|
886
|
+
details: z.record(z.string(), z.union([
|
|
887
|
+
z.string(),
|
|
888
|
+
z.number(),
|
|
889
|
+
z.boolean(),
|
|
890
|
+
z.null()
|
|
891
|
+
])).optional()
|
|
892
|
+
});
|
|
893
|
+
const validateOutput = z.object({
|
|
894
|
+
valid: z.boolean(),
|
|
895
|
+
code: z.string().optional(),
|
|
896
|
+
message: z.string().optional(),
|
|
897
|
+
explanations: z.array(redemptionExplanation).optional(),
|
|
898
|
+
preview: z.object({
|
|
899
|
+
amount: z.number().int(),
|
|
900
|
+
finalOrder: z.object({
|
|
901
|
+
amount: z.number().int(),
|
|
902
|
+
currency: z.string()
|
|
903
|
+
}),
|
|
904
|
+
breakdown: z.array(breakdownEntry)
|
|
905
|
+
}).optional()
|
|
906
|
+
});
|
|
907
|
+
const qualifyInput = z.object({
|
|
908
|
+
customerId: z.string().uuid(),
|
|
909
|
+
order: orderInput,
|
|
910
|
+
filters: z.object({
|
|
911
|
+
campaignIds: z.array(z.string().uuid()).max(100).optional(),
|
|
912
|
+
includeSkipped: z.boolean().optional()
|
|
913
|
+
}).optional()
|
|
914
|
+
});
|
|
915
|
+
const qualifyOutput = z.object({
|
|
916
|
+
eligible: z.array(z.object({
|
|
917
|
+
code: z.string(),
|
|
918
|
+
campaignId: z.string().uuid().nullable(),
|
|
919
|
+
discount: voucherDiscount.omit({ appliesTo: true }).nullable(),
|
|
920
|
+
endDate: z.string().datetime().nullable(),
|
|
921
|
+
preview: z.object({
|
|
922
|
+
amount: z.number().int(),
|
|
923
|
+
finalOrder: z.object({
|
|
924
|
+
amount: z.number().int(),
|
|
925
|
+
currency: z.string()
|
|
926
|
+
}),
|
|
927
|
+
breakdown: z.array(breakdownEntry)
|
|
928
|
+
}).optional()
|
|
929
|
+
})),
|
|
930
|
+
skipped: z.array(z.object({
|
|
931
|
+
code: z.string(),
|
|
932
|
+
campaignId: z.string().uuid().nullable(),
|
|
933
|
+
reason: z.string(),
|
|
934
|
+
message: z.string()
|
|
935
|
+
}))
|
|
936
|
+
});
|
|
937
|
+
const redeemOutput = z.object({
|
|
938
|
+
ok: z.boolean(),
|
|
939
|
+
redemptionId: z.string().uuid().optional(),
|
|
940
|
+
amount: z.number().int().optional(),
|
|
941
|
+
finalOrder: z.object({
|
|
942
|
+
amount: z.number().int(),
|
|
943
|
+
currency: z.string()
|
|
944
|
+
}).optional(),
|
|
945
|
+
breakdown: z.array(breakdownEntry).optional(),
|
|
946
|
+
idempotent: z.boolean().optional(),
|
|
947
|
+
code: z.string().optional(),
|
|
948
|
+
message: z.string().optional(),
|
|
949
|
+
explanations: z.array(redemptionExplanation).optional()
|
|
950
|
+
});
|
|
951
|
+
const stackRedeemInput = z.object({
|
|
952
|
+
codes: z.array(z.string().min(1)).min(1).max(20),
|
|
953
|
+
customerId: z.string().uuid().optional(),
|
|
954
|
+
orderId: z.string().uuid().optional(),
|
|
955
|
+
externalOrderId: z.string().min(1).max(120).optional(),
|
|
956
|
+
order: orderInput,
|
|
957
|
+
idempotencyKey: z.string().min(1).max(128).optional()
|
|
958
|
+
});
|
|
959
|
+
const stackEntry = z.object({
|
|
960
|
+
voucherCode: z.string(),
|
|
961
|
+
voucherId: z.string().uuid(),
|
|
962
|
+
redemptionId: z.string().uuid(),
|
|
963
|
+
amount: z.number().int()
|
|
964
|
+
});
|
|
965
|
+
const stackRedeemOutput = z.object({
|
|
966
|
+
ok: z.boolean(),
|
|
967
|
+
batchId: z.string().uuid().optional(),
|
|
968
|
+
amount: z.number().int().optional(),
|
|
969
|
+
finalOrder: z.object({
|
|
970
|
+
amount: z.number().int(),
|
|
971
|
+
currency: z.string()
|
|
972
|
+
}).optional(),
|
|
973
|
+
breakdown: z.array(breakdownEntry).optional(),
|
|
974
|
+
entries: z.array(stackEntry).optional(),
|
|
975
|
+
idempotent: z.boolean().optional(),
|
|
976
|
+
code: z.string().optional(),
|
|
977
|
+
message: z.string().optional(),
|
|
978
|
+
explanations: z.array(redemptionExplanation).optional()
|
|
979
|
+
});
|
|
980
|
+
//#endregion
|
|
981
|
+
//#region ../contract/src/schemas/promotion.ts
|
|
982
|
+
const promotionTierOutput = z.object({
|
|
983
|
+
id: z.string().uuid(),
|
|
984
|
+
campaignId: z.string().uuid(),
|
|
985
|
+
name: z.string(),
|
|
986
|
+
description: z.string().nullable(),
|
|
987
|
+
effect: voucherDiscount,
|
|
988
|
+
customRewards: z.array(customReward),
|
|
989
|
+
validationRuleId: z.string().uuid().nullable(),
|
|
990
|
+
active: z.boolean(),
|
|
991
|
+
priority: z.number().int(),
|
|
992
|
+
exclusive: z.boolean(),
|
|
993
|
+
startDate: z.string().datetime().nullable(),
|
|
994
|
+
endDate: z.string().datetime().nullable(),
|
|
995
|
+
metadata: z.record(z.string(), z.unknown()),
|
|
996
|
+
createdAt: z.string().datetime(),
|
|
997
|
+
updatedAt: z.string().datetime()
|
|
998
|
+
});
|
|
999
|
+
const promotionTierCreateInput = z.object({
|
|
1000
|
+
campaignId: z.string().uuid(),
|
|
1001
|
+
name: z.string().min(1),
|
|
1002
|
+
description: z.string().optional(),
|
|
1003
|
+
effect: voucherDiscount,
|
|
1004
|
+
customRewards: z.array(customReward).optional(),
|
|
1005
|
+
validationRuleId: z.string().uuid().optional(),
|
|
1006
|
+
active: z.boolean().optional(),
|
|
1007
|
+
priority: z.number().int().optional(),
|
|
1008
|
+
exclusive: z.boolean().optional(),
|
|
1009
|
+
startDate: z.string().datetime().optional(),
|
|
1010
|
+
endDate: z.string().datetime().optional(),
|
|
1011
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
1012
|
+
});
|
|
1013
|
+
const promotionTierUpdateInput = promotionTierCreateInput.omit({ campaignId: true }).partial();
|
|
1014
|
+
const qualificationInput = z.object({
|
|
1015
|
+
customerId: z.string().uuid().optional(),
|
|
1016
|
+
order: orderInput,
|
|
1017
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
1018
|
+
filters: z.object({
|
|
1019
|
+
campaignIds: z.array(z.string().uuid()).optional(),
|
|
1020
|
+
includeSkipped: z.boolean().optional()
|
|
1021
|
+
}).optional()
|
|
1022
|
+
});
|
|
1023
|
+
const qualificationReason = z.enum([
|
|
1024
|
+
"campaign_inactive",
|
|
1025
|
+
"campaign_not_active",
|
|
1026
|
+
"promotion_inactive",
|
|
1027
|
+
"promotion_not_active",
|
|
1028
|
+
"currency_mismatch",
|
|
1029
|
+
"rule_failed",
|
|
1030
|
+
"rule_error",
|
|
1031
|
+
"no_discount_effect",
|
|
1032
|
+
"exclusivity_lost",
|
|
1033
|
+
"zero_after_running_total"
|
|
1034
|
+
]);
|
|
1035
|
+
const qualifiedPromotion = z.object({
|
|
1036
|
+
source: z.literal("promotion"),
|
|
1037
|
+
campaignId: z.string().uuid(),
|
|
1038
|
+
promotionTierId: z.string().uuid(),
|
|
1039
|
+
name: z.string(),
|
|
1040
|
+
discount: voucherDiscount,
|
|
1041
|
+
customRewards: z.array(customReward),
|
|
1042
|
+
priority: z.number().int(),
|
|
1043
|
+
exclusive: z.boolean(),
|
|
1044
|
+
metadata: z.record(z.string(), z.unknown())
|
|
1045
|
+
});
|
|
1046
|
+
const skippedPromotion = qualifiedPromotion.extend({
|
|
1047
|
+
reason: qualificationReason,
|
|
1048
|
+
message: z.string(),
|
|
1049
|
+
trace: z.unknown().optional()
|
|
1050
|
+
});
|
|
1051
|
+
const qualificationOutput = z.object({
|
|
1052
|
+
eligible: z.array(qualifiedPromotion),
|
|
1053
|
+
skipped: z.array(skippedPromotion),
|
|
1054
|
+
preview: z.object({
|
|
1055
|
+
amount: z.number().int(),
|
|
1056
|
+
finalOrder: z.object({
|
|
1057
|
+
amount: z.number().int(),
|
|
1058
|
+
currency: z.string()
|
|
1059
|
+
}),
|
|
1060
|
+
breakdown: z.array(breakdownEntry)
|
|
1061
|
+
})
|
|
1062
|
+
});
|
|
1063
|
+
//#endregion
|
|
1064
|
+
//#region ../contract/src/routes/promotions.ts
|
|
1065
|
+
const promotions = {
|
|
1066
|
+
tiers: {
|
|
1067
|
+
list: oc.route({
|
|
1068
|
+
method: "GET",
|
|
1069
|
+
path: "/promotions/tiers",
|
|
1070
|
+
summary: "List promotion tiers"
|
|
1071
|
+
}).input(paginationInput.extend({
|
|
1072
|
+
campaignId: z.string().uuid().optional(),
|
|
1073
|
+
active: z.boolean().optional()
|
|
1074
|
+
})).output(paginatedOutput(promotionTierOutput)),
|
|
1075
|
+
create: oc.route({
|
|
1076
|
+
method: "POST",
|
|
1077
|
+
path: "/promotions/tiers",
|
|
1078
|
+
summary: "Create promotion tier"
|
|
1079
|
+
}).input(promotionTierCreateInput).output(promotionTierOutput),
|
|
1080
|
+
update: oc.route({
|
|
1081
|
+
method: "PATCH",
|
|
1082
|
+
path: "/promotions/tiers/{id}",
|
|
1083
|
+
summary: "Update promotion tier",
|
|
1084
|
+
inputStructure: "detailed"
|
|
1085
|
+
}).input(z.object({
|
|
1086
|
+
params: z.object({ id: z.string().uuid() }),
|
|
1087
|
+
body: z.object({ patch: promotionTierUpdateInput })
|
|
1088
|
+
})).output(promotionTierOutput),
|
|
1089
|
+
delete: oc.route({
|
|
1090
|
+
method: "DELETE",
|
|
1091
|
+
path: "/promotions/tiers/{id}",
|
|
1092
|
+
summary: "Soft-delete promotion tier",
|
|
1093
|
+
inputStructure: "detailed"
|
|
1094
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
1095
|
+
},
|
|
1096
|
+
qualify: oc.meta(mcpMeta({
|
|
1097
|
+
expose: true,
|
|
1098
|
+
riskLevel: "safe"
|
|
1099
|
+
})).route({
|
|
1100
|
+
method: "POST",
|
|
1101
|
+
path: "/promotions/qualify",
|
|
1102
|
+
summary: "Return auto-applied promotions a cart qualifies for"
|
|
1103
|
+
}).input(qualificationInput).output(qualificationOutput)
|
|
1104
|
+
};
|
|
1105
|
+
//#endregion
|
|
741
1106
|
//#region ../contract/src/schemas/reward-type.ts
|
|
742
1107
|
const rewardTypeOutput = z.object({
|
|
743
1108
|
id: z.string().uuid(),
|
|
@@ -766,8 +1131,9 @@ const rewardTypes = {
|
|
|
766
1131
|
get: oc.route({
|
|
767
1132
|
method: "GET",
|
|
768
1133
|
path: "/reward-types/{id}",
|
|
769
|
-
summary: "Get reward type"
|
|
770
|
-
|
|
1134
|
+
summary: "Get reward type",
|
|
1135
|
+
inputStructure: "detailed"
|
|
1136
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(rewardTypeOutput),
|
|
771
1137
|
create: oc.route({
|
|
772
1138
|
method: "POST",
|
|
773
1139
|
path: "/reward-types",
|
|
@@ -776,16 +1142,18 @@ const rewardTypes = {
|
|
|
776
1142
|
update: oc.route({
|
|
777
1143
|
method: "PATCH",
|
|
778
1144
|
path: "/reward-types/{id}",
|
|
779
|
-
summary: "Update reward type"
|
|
1145
|
+
summary: "Update reward type",
|
|
1146
|
+
inputStructure: "detailed"
|
|
780
1147
|
}).input(z.object({
|
|
781
|
-
id: z.string().uuid(),
|
|
782
|
-
patch: rewardTypeUpdateInput
|
|
1148
|
+
params: z.object({ id: z.string().uuid() }),
|
|
1149
|
+
body: z.object({ patch: rewardTypeUpdateInput })
|
|
783
1150
|
})).output(rewardTypeOutput),
|
|
784
1151
|
delete: oc.route({
|
|
785
1152
|
method: "DELETE",
|
|
786
1153
|
path: "/reward-types/{id}",
|
|
787
|
-
summary: "Soft-delete reward type"
|
|
788
|
-
|
|
1154
|
+
summary: "Soft-delete reward type",
|
|
1155
|
+
inputStructure: "detailed"
|
|
1156
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
789
1157
|
};
|
|
790
1158
|
//#endregion
|
|
791
1159
|
//#region ../contract/src/schemas/segment.ts
|
|
@@ -815,8 +1183,9 @@ const segments = {
|
|
|
815
1183
|
get: oc.route({
|
|
816
1184
|
method: "GET",
|
|
817
1185
|
path: "/segments/{id}",
|
|
818
|
-
summary: "Get segment"
|
|
819
|
-
|
|
1186
|
+
summary: "Get segment",
|
|
1187
|
+
inputStructure: "detailed"
|
|
1188
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(segmentOutput),
|
|
820
1189
|
create: oc.route({
|
|
821
1190
|
method: "POST",
|
|
822
1191
|
path: "/segments",
|
|
@@ -825,16 +1194,18 @@ const segments = {
|
|
|
825
1194
|
update: oc.route({
|
|
826
1195
|
method: "PATCH",
|
|
827
1196
|
path: "/segments/{id}",
|
|
828
|
-
summary: "Update segment"
|
|
1197
|
+
summary: "Update segment",
|
|
1198
|
+
inputStructure: "detailed"
|
|
829
1199
|
}).input(z.object({
|
|
830
|
-
id: z.string().uuid(),
|
|
831
|
-
patch: segmentUpdateInput
|
|
1200
|
+
params: z.object({ id: z.string().uuid() }),
|
|
1201
|
+
body: z.object({ patch: segmentUpdateInput })
|
|
832
1202
|
})).output(segmentOutput),
|
|
833
1203
|
delete: oc.route({
|
|
834
1204
|
method: "DELETE",
|
|
835
1205
|
path: "/segments/{id}",
|
|
836
|
-
summary: "Soft-delete segment"
|
|
837
|
-
|
|
1206
|
+
summary: "Soft-delete segment",
|
|
1207
|
+
inputStructure: "detailed"
|
|
1208
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) })),
|
|
838
1209
|
preview: oc.meta(mcpMeta({
|
|
839
1210
|
expose: true,
|
|
840
1211
|
riskLevel: "safe",
|
|
@@ -886,8 +1257,9 @@ const validationRules = {
|
|
|
886
1257
|
get: oc.route({
|
|
887
1258
|
method: "GET",
|
|
888
1259
|
path: "/validation-rules/{id}",
|
|
889
|
-
summary: "Get validation rule"
|
|
890
|
-
|
|
1260
|
+
summary: "Get validation rule",
|
|
1261
|
+
inputStructure: "detailed"
|
|
1262
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(validationRuleOutput),
|
|
891
1263
|
create: oc.route({
|
|
892
1264
|
method: "POST",
|
|
893
1265
|
path: "/validation-rules",
|
|
@@ -896,164 +1268,20 @@ const validationRules = {
|
|
|
896
1268
|
update: oc.route({
|
|
897
1269
|
method: "PATCH",
|
|
898
1270
|
path: "/validation-rules/{id}",
|
|
899
|
-
summary: "Update validation rule"
|
|
1271
|
+
summary: "Update validation rule",
|
|
1272
|
+
inputStructure: "detailed"
|
|
900
1273
|
}).input(z.object({
|
|
901
|
-
id: z.string().uuid(),
|
|
902
|
-
patch: validationRuleUpdateInput
|
|
1274
|
+
params: z.object({ id: z.string().uuid() }),
|
|
1275
|
+
body: z.object({ patch: validationRuleUpdateInput })
|
|
903
1276
|
})).output(validationRuleOutput),
|
|
904
1277
|
delete: oc.route({
|
|
905
1278
|
method: "DELETE",
|
|
906
1279
|
path: "/validation-rules/{id}",
|
|
907
|
-
summary: "Soft-delete validation rule"
|
|
908
|
-
|
|
1280
|
+
summary: "Soft-delete validation rule",
|
|
1281
|
+
inputStructure: "detailed"
|
|
1282
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
909
1283
|
};
|
|
910
1284
|
//#endregion
|
|
911
|
-
//#region ../contract/src/schemas/voucher.ts
|
|
912
|
-
const voucherType = z.enum(["DISCOUNT", "GIFT_CARD"]);
|
|
913
|
-
const voucherDiscount = z.object({
|
|
914
|
-
type: z.enum(["AMOUNT", "PERCENTAGE"]),
|
|
915
|
-
amount: z.number().int().min(0).optional(),
|
|
916
|
-
percent: z.number().int().min(0).max(1e4).optional(),
|
|
917
|
-
maxDiscountAmount: z.number().int().min(0).optional(),
|
|
918
|
-
appliesTo: z.object({
|
|
919
|
-
productIds: z.array(z.string()).optional(),
|
|
920
|
-
collectionIds: z.array(z.string()).optional()
|
|
921
|
-
}).optional()
|
|
922
|
-
});
|
|
923
|
-
const customReward = z.object({
|
|
924
|
-
typeKey: z.string().min(1),
|
|
925
|
-
payload: z.record(z.string(), z.unknown())
|
|
926
|
-
});
|
|
927
|
-
const voucherOutput = z.object({
|
|
928
|
-
id: z.string().uuid(),
|
|
929
|
-
code: z.string(),
|
|
930
|
-
campaignId: z.string().uuid().nullable(),
|
|
931
|
-
type: voucherType,
|
|
932
|
-
discount: voucherDiscount.nullable(),
|
|
933
|
-
customRewards: z.array(customReward),
|
|
934
|
-
giftBalance: z.number().int().nullable(),
|
|
935
|
-
redemptionLimit: z.number().int().nullable(),
|
|
936
|
-
redemptionCount: z.number().int(),
|
|
937
|
-
priority: z.number().int(),
|
|
938
|
-
exclusive: z.boolean(),
|
|
939
|
-
active: z.boolean(),
|
|
940
|
-
startDate: z.string().datetime().nullable(),
|
|
941
|
-
endDate: z.string().datetime().nullable(),
|
|
942
|
-
customerId: z.string().uuid().nullable(),
|
|
943
|
-
metadata: z.record(z.string(), z.unknown()),
|
|
944
|
-
createdAt: z.string().datetime(),
|
|
945
|
-
updatedAt: z.string().datetime()
|
|
946
|
-
});
|
|
947
|
-
const voucherCreateInput = z.object({
|
|
948
|
-
code: z.string().min(1).optional(),
|
|
949
|
-
campaignId: z.string().uuid().optional(),
|
|
950
|
-
type: voucherType,
|
|
951
|
-
discount: voucherDiscount.optional(),
|
|
952
|
-
customRewards: z.array(customReward).optional(),
|
|
953
|
-
giftBalance: z.number().int().min(0).optional(),
|
|
954
|
-
redemptionLimit: z.number().int().min(1).optional(),
|
|
955
|
-
priority: z.number().int().optional(),
|
|
956
|
-
exclusive: z.boolean().optional(),
|
|
957
|
-
startDate: z.string().datetime().optional(),
|
|
958
|
-
endDate: z.string().datetime().optional(),
|
|
959
|
-
customerId: z.string().uuid().optional(),
|
|
960
|
-
metadata: z.record(z.string(), z.unknown()).optional()
|
|
961
|
-
});
|
|
962
|
-
const voucherUpdateInput = voucherCreateInput.omit({
|
|
963
|
-
type: true,
|
|
964
|
-
code: true
|
|
965
|
-
}).partial().extend({ active: z.boolean().optional() });
|
|
966
|
-
const voucherBulkCreateInput = z.object({
|
|
967
|
-
campaignId: z.string().uuid(),
|
|
968
|
-
count: z.number().int().min(1).max(1e5)
|
|
969
|
-
});
|
|
970
|
-
//#endregion
|
|
971
|
-
//#region ../contract/src/schemas/redemption.ts
|
|
972
|
-
const orderItem$1 = z.object({
|
|
973
|
-
productId: z.string(),
|
|
974
|
-
collectionId: z.string().optional(),
|
|
975
|
-
quantity: z.number().int().min(1),
|
|
976
|
-
unitPrice: z.number().int().min(0)
|
|
977
|
-
});
|
|
978
|
-
const orderInput = z.object({
|
|
979
|
-
amount: z.number().int().min(0),
|
|
980
|
-
currency: z.string().length(3),
|
|
981
|
-
items: z.array(orderItem$1).optional()
|
|
982
|
-
});
|
|
983
|
-
const validateInput = z.object({
|
|
984
|
-
code: z.string().min(1),
|
|
985
|
-
customerId: z.string().uuid().optional(),
|
|
986
|
-
order: orderInput.optional()
|
|
987
|
-
});
|
|
988
|
-
const redeemInput = validateInput.extend({
|
|
989
|
-
/** uuid of an `order` row created via the orders API. */
|
|
990
|
-
orderId: z.string().uuid().optional(),
|
|
991
|
-
/** Free-form integrator order reference (Shopify id, etc). */
|
|
992
|
-
externalOrderId: z.string().min(1).max(120).optional(),
|
|
993
|
-
idempotencyKey: z.string().min(1).max(128).optional()
|
|
994
|
-
});
|
|
995
|
-
const breakdownEntry = z.object({
|
|
996
|
-
voucherId: z.string().uuid(),
|
|
997
|
-
code: z.string(),
|
|
998
|
-
amount: z.number().int(),
|
|
999
|
-
type: z.enum(["AMOUNT", "PERCENTAGE"]).optional(),
|
|
1000
|
-
reason: z.enum(["exclusivity_lost", "zero_after_running_total"]).optional()
|
|
1001
|
-
});
|
|
1002
|
-
const validateOutput = z.object({
|
|
1003
|
-
valid: z.boolean(),
|
|
1004
|
-
code: z.string().optional(),
|
|
1005
|
-
message: z.string().optional(),
|
|
1006
|
-
preview: z.object({
|
|
1007
|
-
amount: z.number().int(),
|
|
1008
|
-
finalOrder: z.object({
|
|
1009
|
-
amount: z.number().int(),
|
|
1010
|
-
currency: z.string()
|
|
1011
|
-
}),
|
|
1012
|
-
breakdown: z.array(breakdownEntry)
|
|
1013
|
-
}).optional()
|
|
1014
|
-
});
|
|
1015
|
-
const redeemOutput = z.object({
|
|
1016
|
-
ok: z.boolean(),
|
|
1017
|
-
redemptionId: z.string().uuid().optional(),
|
|
1018
|
-
amount: z.number().int().optional(),
|
|
1019
|
-
finalOrder: z.object({
|
|
1020
|
-
amount: z.number().int(),
|
|
1021
|
-
currency: z.string()
|
|
1022
|
-
}).optional(),
|
|
1023
|
-
breakdown: z.array(breakdownEntry).optional(),
|
|
1024
|
-
idempotent: z.boolean().optional(),
|
|
1025
|
-
code: z.string().optional(),
|
|
1026
|
-
message: z.string().optional()
|
|
1027
|
-
});
|
|
1028
|
-
const stackRedeemInput = z.object({
|
|
1029
|
-
codes: z.array(z.string().min(1)).min(1).max(20),
|
|
1030
|
-
customerId: z.string().uuid().optional(),
|
|
1031
|
-
orderId: z.string().uuid().optional(),
|
|
1032
|
-
externalOrderId: z.string().min(1).max(120).optional(),
|
|
1033
|
-
order: orderInput,
|
|
1034
|
-
idempotencyKey: z.string().min(1).max(128).optional()
|
|
1035
|
-
});
|
|
1036
|
-
const stackEntry = z.object({
|
|
1037
|
-
voucherCode: z.string(),
|
|
1038
|
-
voucherId: z.string().uuid(),
|
|
1039
|
-
redemptionId: z.string().uuid(),
|
|
1040
|
-
amount: z.number().int()
|
|
1041
|
-
});
|
|
1042
|
-
const stackRedeemOutput = z.object({
|
|
1043
|
-
ok: z.boolean(),
|
|
1044
|
-
batchId: z.string().uuid().optional(),
|
|
1045
|
-
amount: z.number().int().optional(),
|
|
1046
|
-
finalOrder: z.object({
|
|
1047
|
-
amount: z.number().int(),
|
|
1048
|
-
currency: z.string()
|
|
1049
|
-
}).optional(),
|
|
1050
|
-
breakdown: z.array(breakdownEntry).optional(),
|
|
1051
|
-
entries: z.array(stackEntry).optional(),
|
|
1052
|
-
idempotent: z.boolean().optional(),
|
|
1053
|
-
code: z.string().optional(),
|
|
1054
|
-
message: z.string().optional()
|
|
1055
|
-
});
|
|
1056
|
-
//#endregion
|
|
1057
1285
|
//#region ../contract/src/routes/vouchers.ts
|
|
1058
1286
|
const vouchers = {
|
|
1059
1287
|
list: oc.meta(mcpMeta({
|
|
@@ -1075,8 +1303,9 @@ const vouchers = {
|
|
|
1075
1303
|
})).route({
|
|
1076
1304
|
method: "GET",
|
|
1077
1305
|
path: "/vouchers/{code}",
|
|
1078
|
-
summary: "Get voucher by code"
|
|
1079
|
-
|
|
1306
|
+
summary: "Get voucher by code",
|
|
1307
|
+
inputStructure: "detailed"
|
|
1308
|
+
}).input(z.object({ params: z.object({ code: z.string() }) })).output(voucherOutput),
|
|
1080
1309
|
create: oc.route({
|
|
1081
1310
|
method: "POST",
|
|
1082
1311
|
path: "/vouchers",
|
|
@@ -1085,16 +1314,18 @@ const vouchers = {
|
|
|
1085
1314
|
update: oc.route({
|
|
1086
1315
|
method: "PATCH",
|
|
1087
1316
|
path: "/vouchers/{code}",
|
|
1088
|
-
summary: "Update voucher"
|
|
1317
|
+
summary: "Update voucher",
|
|
1318
|
+
inputStructure: "detailed"
|
|
1089
1319
|
}).input(z.object({
|
|
1090
|
-
code: z.string(),
|
|
1091
|
-
patch: voucherUpdateInput
|
|
1320
|
+
params: z.object({ code: z.string() }),
|
|
1321
|
+
body: z.object({ patch: voucherUpdateInput })
|
|
1092
1322
|
})).output(voucherOutput),
|
|
1093
1323
|
delete: oc.route({
|
|
1094
1324
|
method: "DELETE",
|
|
1095
1325
|
path: "/vouchers/{code}",
|
|
1096
|
-
summary: "Soft-delete voucher"
|
|
1097
|
-
|
|
1326
|
+
summary: "Soft-delete voucher",
|
|
1327
|
+
inputStructure: "detailed"
|
|
1328
|
+
}).input(z.object({ params: z.object({ code: z.string() }) })).output(z.object({ ok: z.literal(true) })),
|
|
1098
1329
|
bulk: oc.route({
|
|
1099
1330
|
method: "POST",
|
|
1100
1331
|
path: "/vouchers/bulk",
|
|
@@ -1110,8 +1341,20 @@ const vouchers = {
|
|
|
1110
1341
|
})).route({
|
|
1111
1342
|
method: "POST",
|
|
1112
1343
|
path: "/vouchers/{code}/validate",
|
|
1113
|
-
summary: "Validate a voucher against an optional order context"
|
|
1114
|
-
|
|
1344
|
+
summary: "Validate a voucher against an optional order context",
|
|
1345
|
+
inputStructure: "detailed"
|
|
1346
|
+
}).input(z.object({
|
|
1347
|
+
params: z.object({ code: z.string().min(1) }),
|
|
1348
|
+
body: validateInput.omit({ code: true }).optional()
|
|
1349
|
+
})).output(validateOutput),
|
|
1350
|
+
qualify: oc.meta(mcpMeta({
|
|
1351
|
+
expose: true,
|
|
1352
|
+
riskLevel: "safe"
|
|
1353
|
+
})).route({
|
|
1354
|
+
method: "POST",
|
|
1355
|
+
path: "/vouchers/qualify",
|
|
1356
|
+
summary: "Batch-qualify customer-held voucher codes for an order"
|
|
1357
|
+
}).input(qualifyInput).output(qualifyOutput),
|
|
1115
1358
|
redeem: oc.meta(mcpMeta({
|
|
1116
1359
|
expose: true,
|
|
1117
1360
|
riskLevel: "mutating",
|
|
@@ -1119,8 +1362,12 @@ const vouchers = {
|
|
|
1119
1362
|
})).route({
|
|
1120
1363
|
method: "POST",
|
|
1121
1364
|
path: "/vouchers/{code}/redemption",
|
|
1122
|
-
summary: "Redeem a voucher"
|
|
1123
|
-
|
|
1365
|
+
summary: "Redeem a voucher",
|
|
1366
|
+
inputStructure: "detailed"
|
|
1367
|
+
}).input(z.object({
|
|
1368
|
+
params: z.object({ code: z.string().min(1) }),
|
|
1369
|
+
body: redeemInput.omit({ code: true }).optional()
|
|
1370
|
+
})).output(redeemOutput),
|
|
1124
1371
|
stackRedeem: oc.meta(mcpMeta({
|
|
1125
1372
|
expose: true,
|
|
1126
1373
|
riskLevel: "mutating",
|
|
@@ -1133,8 +1380,9 @@ const vouchers = {
|
|
|
1133
1380
|
transactions: oc.route({
|
|
1134
1381
|
method: "GET",
|
|
1135
1382
|
path: "/vouchers/{code}/transactions",
|
|
1136
|
-
summary: "Gift card balance ledger"
|
|
1137
|
-
|
|
1383
|
+
summary: "Gift card balance ledger",
|
|
1384
|
+
inputStructure: "detailed"
|
|
1385
|
+
}).input(z.object({ params: z.object({ code: z.string() }) })).output(z.object({ data: z.array(z.object({
|
|
1138
1386
|
id: z.string().uuid(),
|
|
1139
1387
|
redemptionId: z.string().uuid().nullable(),
|
|
1140
1388
|
delta: z.number().int(),
|
|
@@ -1212,8 +1460,9 @@ const webhooks$1 = {
|
|
|
1212
1460
|
get: oc.route({
|
|
1213
1461
|
method: "GET",
|
|
1214
1462
|
path: "/webhooks/{id}",
|
|
1215
|
-
summary: "Get webhook"
|
|
1216
|
-
|
|
1463
|
+
summary: "Get webhook",
|
|
1464
|
+
inputStructure: "detailed"
|
|
1465
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(webhookOutput),
|
|
1217
1466
|
create: oc.route({
|
|
1218
1467
|
method: "POST",
|
|
1219
1468
|
path: "/webhooks",
|
|
@@ -1222,29 +1471,33 @@ const webhooks$1 = {
|
|
|
1222
1471
|
update: oc.route({
|
|
1223
1472
|
method: "PATCH",
|
|
1224
1473
|
path: "/webhooks/{id}",
|
|
1225
|
-
summary: "Update webhook"
|
|
1474
|
+
summary: "Update webhook",
|
|
1475
|
+
inputStructure: "detailed"
|
|
1226
1476
|
}).input(z.object({
|
|
1227
|
-
id: z.string().uuid(),
|
|
1228
|
-
patch: webhookUpdateInput
|
|
1477
|
+
params: z.object({ id: z.string().uuid() }),
|
|
1478
|
+
body: z.object({ patch: webhookUpdateInput })
|
|
1229
1479
|
})).output(webhookOutput),
|
|
1230
1480
|
delete: oc.route({
|
|
1231
1481
|
method: "DELETE",
|
|
1232
1482
|
path: "/webhooks/{id}",
|
|
1233
|
-
summary: "Soft-delete webhook"
|
|
1234
|
-
|
|
1483
|
+
summary: "Soft-delete webhook",
|
|
1484
|
+
inputStructure: "detailed"
|
|
1485
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) })),
|
|
1235
1486
|
deliveries: oc.route({
|
|
1236
1487
|
method: "GET",
|
|
1237
1488
|
path: "/webhooks/{id}/deliveries",
|
|
1238
|
-
summary: "Recent deliveries for a webhook"
|
|
1489
|
+
summary: "Recent deliveries for a webhook",
|
|
1490
|
+
inputStructure: "detailed"
|
|
1239
1491
|
}).input(z.object({
|
|
1240
|
-
id: z.string().uuid(),
|
|
1241
|
-
limit: z.number().int().min(1).max(100).default(50)
|
|
1492
|
+
params: z.object({ id: z.string().uuid() }),
|
|
1493
|
+
query: z.object({ limit: z.number().int().min(1).max(100).default(50) })
|
|
1242
1494
|
})).output(z.object({ data: z.array(webhookDeliveryOutput) })),
|
|
1243
1495
|
replay: oc.route({
|
|
1244
1496
|
method: "POST",
|
|
1245
1497
|
path: "/webhooks/deliveries/{id}/replay",
|
|
1246
|
-
summary: "Re-enqueue a delivery (succeeded or otherwise)"
|
|
1247
|
-
|
|
1498
|
+
summary: "Re-enqueue a delivery (succeeded or otherwise)",
|
|
1499
|
+
inputStructure: "detailed"
|
|
1500
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) }))
|
|
1248
1501
|
};
|
|
1249
1502
|
const events = {
|
|
1250
1503
|
list: oc.route({
|
|
@@ -1255,8 +1508,9 @@ const events = {
|
|
|
1255
1508
|
get: oc.route({
|
|
1256
1509
|
method: "GET",
|
|
1257
1510
|
path: "/events/{id}",
|
|
1258
|
-
summary: "Get event"
|
|
1259
|
-
|
|
1511
|
+
summary: "Get event",
|
|
1512
|
+
inputStructure: "detailed"
|
|
1513
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(eventOutput)
|
|
1260
1514
|
};
|
|
1261
1515
|
//#endregion
|
|
1262
1516
|
//#region ../contract/src/routes/insights.ts
|
|
@@ -1335,26 +1589,30 @@ const users = {
|
|
|
1335
1589
|
resetPassword: oc.route({
|
|
1336
1590
|
method: "POST",
|
|
1337
1591
|
path: "/users/{id}/reset-password",
|
|
1338
|
-
summary: "Reset a staff user's password (admin)"
|
|
1339
|
-
|
|
1592
|
+
summary: "Reset a staff user's password (admin)",
|
|
1593
|
+
inputStructure: "detailed"
|
|
1594
|
+
}).input(z.object({ params: z.object({ id: z.string() }) })).output(userCreateOutput),
|
|
1340
1595
|
setRole: oc.route({
|
|
1341
1596
|
method: "PATCH",
|
|
1342
1597
|
path: "/users/{id}/role",
|
|
1343
|
-
summary: "Change a staff user's role (admin)"
|
|
1598
|
+
summary: "Change a staff user's role (admin)",
|
|
1599
|
+
inputStructure: "detailed"
|
|
1344
1600
|
}).input(z.object({
|
|
1345
|
-
id: z.string(),
|
|
1346
|
-
role: userRole
|
|
1601
|
+
params: z.object({ id: z.string() }),
|
|
1602
|
+
body: z.object({ role: userRole })
|
|
1347
1603
|
})).output(userOutput),
|
|
1348
1604
|
disable: oc.route({
|
|
1349
1605
|
method: "POST",
|
|
1350
1606
|
path: "/users/{id}/disable",
|
|
1351
|
-
summary: "Disable a staff user (admin)"
|
|
1352
|
-
|
|
1607
|
+
summary: "Disable a staff user (admin)",
|
|
1608
|
+
inputStructure: "detailed"
|
|
1609
|
+
}).input(z.object({ params: z.object({ id: z.string() }) })).output(userOutput),
|
|
1353
1610
|
enable: oc.route({
|
|
1354
1611
|
method: "POST",
|
|
1355
1612
|
path: "/users/{id}/enable",
|
|
1356
|
-
summary: "Re-enable a staff user (admin)"
|
|
1357
|
-
|
|
1613
|
+
summary: "Re-enable a staff user (admin)",
|
|
1614
|
+
inputStructure: "detailed"
|
|
1615
|
+
}).input(z.object({ params: z.object({ id: z.string() }) })).output(userOutput)
|
|
1358
1616
|
};
|
|
1359
1617
|
//#endregion
|
|
1360
1618
|
//#region ../contract/src/schemas/order.ts
|
|
@@ -1420,8 +1678,9 @@ const orders = {
|
|
|
1420
1678
|
get: oc.route({
|
|
1421
1679
|
method: "GET",
|
|
1422
1680
|
path: "/orders/{id}",
|
|
1423
|
-
summary: "Fetch one order"
|
|
1424
|
-
|
|
1681
|
+
summary: "Fetch one order",
|
|
1682
|
+
inputStructure: "detailed"
|
|
1683
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(orderOutput),
|
|
1425
1684
|
create: oc.route({
|
|
1426
1685
|
method: "POST",
|
|
1427
1686
|
path: "/orders",
|
|
@@ -1430,28 +1689,36 @@ const orders = {
|
|
|
1430
1689
|
update: oc.route({
|
|
1431
1690
|
method: "PATCH",
|
|
1432
1691
|
path: "/orders/{id}",
|
|
1433
|
-
summary: "Update an order"
|
|
1434
|
-
|
|
1692
|
+
summary: "Update an order",
|
|
1693
|
+
inputStructure: "detailed"
|
|
1694
|
+
}).input(z.object({
|
|
1695
|
+
params: z.object({ id: z.string().uuid() }),
|
|
1696
|
+
body: orderUpdateInput.omit({ id: true })
|
|
1697
|
+
})).output(orderOutput),
|
|
1435
1698
|
cancel: oc.route({
|
|
1436
1699
|
method: "POST",
|
|
1437
1700
|
path: "/orders/{id}/cancel",
|
|
1438
|
-
summary: "Cancel an order"
|
|
1439
|
-
|
|
1701
|
+
summary: "Cancel an order",
|
|
1702
|
+
inputStructure: "detailed"
|
|
1703
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(orderOutput),
|
|
1440
1704
|
fulfill: oc.route({
|
|
1441
1705
|
method: "POST",
|
|
1442
1706
|
path: "/orders/{id}/fulfill",
|
|
1443
|
-
summary: "Mark order fulfilled"
|
|
1444
|
-
|
|
1707
|
+
summary: "Mark order fulfilled",
|
|
1708
|
+
inputStructure: "detailed"
|
|
1709
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(orderOutput),
|
|
1445
1710
|
delete: oc.route({
|
|
1446
1711
|
method: "DELETE",
|
|
1447
1712
|
path: "/orders/{id}",
|
|
1448
|
-
summary: "Soft-delete an order"
|
|
1449
|
-
|
|
1713
|
+
summary: "Soft-delete an order",
|
|
1714
|
+
inputStructure: "detailed"
|
|
1715
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ ok: z.literal(true) })),
|
|
1450
1716
|
redemptions: oc.route({
|
|
1451
1717
|
method: "GET",
|
|
1452
1718
|
path: "/orders/{id}/redemptions",
|
|
1453
|
-
summary: "List redemptions attached to an order"
|
|
1454
|
-
|
|
1719
|
+
summary: "List redemptions attached to an order",
|
|
1720
|
+
inputStructure: "detailed"
|
|
1721
|
+
}).input(z.object({ params: z.object({ id: z.string().uuid() }) })).output(z.object({ data: z.array(z.object({
|
|
1455
1722
|
id: z.string().uuid(),
|
|
1456
1723
|
voucherCode: z.string(),
|
|
1457
1724
|
voucherId: z.string().uuid(),
|
|
@@ -1556,6 +1823,7 @@ const contract = {
|
|
|
1556
1823
|
customers,
|
|
1557
1824
|
segments,
|
|
1558
1825
|
campaigns,
|
|
1826
|
+
promotions,
|
|
1559
1827
|
vouchers,
|
|
1560
1828
|
validationRules,
|
|
1561
1829
|
rewardTypes,
|