@offerkit/sdk 0.2.0 → 0.2.3

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/dist/index.mjs CHANGED
@@ -738,6 +738,331 @@ const referrals = {
738
738
  }).input(referralConvertInput).output(referralConvertOutput)
739
739
  };
740
740
  //#endregion
741
+ //#region ../contract/src/schemas/voucher.ts
742
+ const voucherType = z.enum(["DISCOUNT", "GIFT_CARD"]);
743
+ const voucherDiscount = z.object({
744
+ type: z.enum(["AMOUNT", "PERCENTAGE"]),
745
+ amount: z.number().int().min(0).optional(),
746
+ percent: z.number().int().min(0).max(1e4).optional(),
747
+ maxDiscountAmount: z.number().int().min(0).optional(),
748
+ appliesTo: z.object({
749
+ productIds: z.array(z.string()).optional(),
750
+ collectionIds: z.array(z.string()).optional()
751
+ }).optional()
752
+ });
753
+ const customReward = z.object({
754
+ typeKey: z.string().min(1),
755
+ payload: z.record(z.string(), z.unknown())
756
+ });
757
+ const voucherOutput = z.object({
758
+ id: z.string().uuid(),
759
+ code: z.string(),
760
+ campaignId: z.string().uuid().nullable(),
761
+ type: voucherType,
762
+ discount: voucherDiscount.nullable(),
763
+ customRewards: z.array(customReward),
764
+ giftBalance: z.number().int().nullable(),
765
+ redemptionLimit: z.number().int().nullable(),
766
+ redemptionCount: z.number().int(),
767
+ priority: z.number().int(),
768
+ exclusive: z.boolean(),
769
+ active: z.boolean(),
770
+ startDate: z.string().datetime().nullable(),
771
+ endDate: z.string().datetime().nullable(),
772
+ customerId: z.string().uuid().nullable(),
773
+ metadata: z.record(z.string(), z.unknown()),
774
+ createdAt: z.string().datetime(),
775
+ updatedAt: z.string().datetime()
776
+ });
777
+ const voucherCreateInput = z.object({
778
+ code: z.string().min(1).optional(),
779
+ campaignId: z.string().uuid().optional(),
780
+ type: voucherType,
781
+ discount: voucherDiscount.optional(),
782
+ customRewards: z.array(customReward).optional(),
783
+ giftBalance: z.number().int().min(0).optional(),
784
+ redemptionLimit: z.number().int().min(1).optional(),
785
+ priority: z.number().int().optional(),
786
+ exclusive: z.boolean().optional(),
787
+ startDate: z.string().datetime().optional(),
788
+ endDate: z.string().datetime().optional(),
789
+ customerId: z.string().uuid().optional(),
790
+ metadata: z.record(z.string(), z.unknown()).optional()
791
+ });
792
+ const voucherUpdateInput = voucherCreateInput.omit({
793
+ type: true,
794
+ code: true
795
+ }).partial().extend({ active: z.boolean().optional() });
796
+ const voucherBulkCreateInput = z.object({
797
+ campaignId: z.string().uuid(),
798
+ count: z.number().int().min(1).max(1e5)
799
+ });
800
+ //#endregion
801
+ //#region ../contract/src/schemas/redemption.ts
802
+ const orderItem$1 = z.object({
803
+ productId: z.string(),
804
+ collectionId: z.string().optional(),
805
+ quantity: z.number().int().min(1),
806
+ unitPrice: z.number().int().min(0)
807
+ });
808
+ const orderInput = z.object({
809
+ amount: z.number().int().min(0),
810
+ currency: z.string().length(3),
811
+ items: z.array(orderItem$1).optional()
812
+ });
813
+ const validateInput = z.object({
814
+ code: z.string().min(1),
815
+ customerId: z.string().uuid().optional(),
816
+ order: orderInput.optional()
817
+ });
818
+ const redeemInput = validateInput.extend({
819
+ /** uuid of an `order` row created via the orders API. */
820
+ orderId: z.string().uuid().optional(),
821
+ /** Free-form integrator order reference (Shopify id, etc). */
822
+ externalOrderId: z.string().min(1).max(120).optional(),
823
+ idempotencyKey: z.string().min(1).max(128).optional()
824
+ });
825
+ const breakdownEntry = z.object({
826
+ voucherId: z.string().uuid(),
827
+ code: z.string(),
828
+ amount: z.number().int(),
829
+ type: z.enum(["AMOUNT", "PERCENTAGE"]).optional(),
830
+ reason: z.enum(["exclusivity_lost", "zero_after_running_total"]).optional()
831
+ });
832
+ const redemptionExplanation = z.object({
833
+ code: z.enum([
834
+ "voucher_not_found",
835
+ "voucher_disabled",
836
+ "voucher_expired",
837
+ "redemption_limit_reached",
838
+ "currency_mismatch",
839
+ "gift_balance_zero",
840
+ "order_required",
841
+ "exclusivity_lost",
842
+ "zero_after_running_total",
843
+ "gift_card_stacking_unsupported"
844
+ ]),
845
+ message: z.string(),
846
+ voucherId: z.string().uuid().optional(),
847
+ voucherCode: z.string().optional(),
848
+ details: z.record(z.string(), z.union([
849
+ z.string(),
850
+ z.number(),
851
+ z.boolean(),
852
+ z.null()
853
+ ])).optional()
854
+ });
855
+ const validateOutput = z.object({
856
+ valid: z.boolean(),
857
+ code: z.string().optional(),
858
+ message: z.string().optional(),
859
+ explanations: z.array(redemptionExplanation).optional(),
860
+ preview: z.object({
861
+ amount: z.number().int(),
862
+ finalOrder: z.object({
863
+ amount: z.number().int(),
864
+ currency: z.string()
865
+ }),
866
+ breakdown: z.array(breakdownEntry)
867
+ }).optional()
868
+ });
869
+ const qualifyInput = z.object({
870
+ customerId: z.string().uuid(),
871
+ order: orderInput,
872
+ filters: z.object({
873
+ campaignIds: z.array(z.string().uuid()).max(100).optional(),
874
+ includeSkipped: z.boolean().optional()
875
+ }).optional()
876
+ });
877
+ const qualifyOutput = z.object({
878
+ eligible: z.array(z.object({
879
+ code: z.string(),
880
+ campaignId: z.string().uuid().nullable(),
881
+ discount: voucherDiscount.omit({ appliesTo: true }).nullable(),
882
+ endDate: z.string().datetime().nullable(),
883
+ preview: z.object({
884
+ amount: z.number().int(),
885
+ finalOrder: z.object({
886
+ amount: z.number().int(),
887
+ currency: z.string()
888
+ }),
889
+ breakdown: z.array(breakdownEntry)
890
+ }).optional()
891
+ })),
892
+ skipped: z.array(z.object({
893
+ code: z.string(),
894
+ campaignId: z.string().uuid().nullable(),
895
+ reason: z.string(),
896
+ message: z.string()
897
+ }))
898
+ });
899
+ const redeemOutput = z.object({
900
+ ok: z.boolean(),
901
+ redemptionId: z.string().uuid().optional(),
902
+ amount: z.number().int().optional(),
903
+ finalOrder: z.object({
904
+ amount: z.number().int(),
905
+ currency: z.string()
906
+ }).optional(),
907
+ breakdown: z.array(breakdownEntry).optional(),
908
+ idempotent: z.boolean().optional(),
909
+ code: z.string().optional(),
910
+ message: z.string().optional(),
911
+ explanations: z.array(redemptionExplanation).optional()
912
+ });
913
+ const stackRedeemInput = z.object({
914
+ codes: z.array(z.string().min(1)).min(1).max(20),
915
+ customerId: z.string().uuid().optional(),
916
+ orderId: z.string().uuid().optional(),
917
+ externalOrderId: z.string().min(1).max(120).optional(),
918
+ order: orderInput,
919
+ idempotencyKey: z.string().min(1).max(128).optional()
920
+ });
921
+ const stackEntry = z.object({
922
+ voucherCode: z.string(),
923
+ voucherId: z.string().uuid(),
924
+ redemptionId: z.string().uuid(),
925
+ amount: z.number().int()
926
+ });
927
+ const stackRedeemOutput = z.object({
928
+ ok: z.boolean(),
929
+ batchId: z.string().uuid().optional(),
930
+ amount: z.number().int().optional(),
931
+ finalOrder: z.object({
932
+ amount: z.number().int(),
933
+ currency: z.string()
934
+ }).optional(),
935
+ breakdown: z.array(breakdownEntry).optional(),
936
+ entries: z.array(stackEntry).optional(),
937
+ idempotent: z.boolean().optional(),
938
+ code: z.string().optional(),
939
+ message: z.string().optional(),
940
+ explanations: z.array(redemptionExplanation).optional()
941
+ });
942
+ //#endregion
943
+ //#region ../contract/src/schemas/promotion.ts
944
+ const promotionTierOutput = z.object({
945
+ id: z.string().uuid(),
946
+ campaignId: z.string().uuid(),
947
+ name: z.string(),
948
+ description: z.string().nullable(),
949
+ effect: voucherDiscount,
950
+ customRewards: z.array(customReward),
951
+ validationRuleId: z.string().uuid().nullable(),
952
+ active: z.boolean(),
953
+ priority: z.number().int(),
954
+ exclusive: z.boolean(),
955
+ startDate: z.string().datetime().nullable(),
956
+ endDate: z.string().datetime().nullable(),
957
+ metadata: z.record(z.string(), z.unknown()),
958
+ createdAt: z.string().datetime(),
959
+ updatedAt: z.string().datetime()
960
+ });
961
+ const promotionTierCreateInput = z.object({
962
+ campaignId: z.string().uuid(),
963
+ name: z.string().min(1),
964
+ description: z.string().optional(),
965
+ effect: voucherDiscount,
966
+ customRewards: z.array(customReward).optional(),
967
+ validationRuleId: z.string().uuid().optional(),
968
+ active: z.boolean().optional(),
969
+ priority: z.number().int().optional(),
970
+ exclusive: z.boolean().optional(),
971
+ startDate: z.string().datetime().optional(),
972
+ endDate: z.string().datetime().optional(),
973
+ metadata: z.record(z.string(), z.unknown()).optional()
974
+ });
975
+ const promotionTierUpdateInput = promotionTierCreateInput.omit({ campaignId: true }).partial();
976
+ const qualificationInput = z.object({
977
+ customerId: z.string().uuid().optional(),
978
+ order: orderInput,
979
+ metadata: z.record(z.string(), z.unknown()).optional(),
980
+ filters: z.object({
981
+ campaignIds: z.array(z.string().uuid()).optional(),
982
+ includeSkipped: z.boolean().optional()
983
+ }).optional()
984
+ });
985
+ const qualificationReason = z.enum([
986
+ "campaign_inactive",
987
+ "campaign_not_active",
988
+ "promotion_inactive",
989
+ "promotion_not_active",
990
+ "currency_mismatch",
991
+ "rule_failed",
992
+ "rule_error",
993
+ "no_discount_effect",
994
+ "exclusivity_lost",
995
+ "zero_after_running_total"
996
+ ]);
997
+ const qualifiedPromotion = z.object({
998
+ source: z.literal("promotion"),
999
+ campaignId: z.string().uuid(),
1000
+ promotionTierId: z.string().uuid(),
1001
+ name: z.string(),
1002
+ discount: voucherDiscount,
1003
+ customRewards: z.array(customReward),
1004
+ priority: z.number().int(),
1005
+ exclusive: z.boolean(),
1006
+ metadata: z.record(z.string(), z.unknown())
1007
+ });
1008
+ const skippedPromotion = qualifiedPromotion.extend({
1009
+ reason: qualificationReason,
1010
+ message: z.string(),
1011
+ trace: z.unknown().optional()
1012
+ });
1013
+ const qualificationOutput = z.object({
1014
+ eligible: z.array(qualifiedPromotion),
1015
+ skipped: z.array(skippedPromotion),
1016
+ preview: z.object({
1017
+ amount: z.number().int(),
1018
+ finalOrder: z.object({
1019
+ amount: z.number().int(),
1020
+ currency: z.string()
1021
+ }),
1022
+ breakdown: z.array(breakdownEntry)
1023
+ })
1024
+ });
1025
+ //#endregion
1026
+ //#region ../contract/src/routes/promotions.ts
1027
+ const promotions = {
1028
+ tiers: {
1029
+ list: oc.route({
1030
+ method: "GET",
1031
+ path: "/promotions/tiers",
1032
+ summary: "List promotion tiers"
1033
+ }).input(paginationInput.extend({
1034
+ campaignId: z.string().uuid().optional(),
1035
+ active: z.boolean().optional()
1036
+ })).output(paginatedOutput(promotionTierOutput)),
1037
+ create: oc.route({
1038
+ method: "POST",
1039
+ path: "/promotions/tiers",
1040
+ summary: "Create promotion tier"
1041
+ }).input(promotionTierCreateInput).output(promotionTierOutput),
1042
+ update: oc.route({
1043
+ method: "PATCH",
1044
+ path: "/promotions/tiers/{id}",
1045
+ summary: "Update promotion tier"
1046
+ }).input(z.object({
1047
+ id: z.string().uuid(),
1048
+ patch: promotionTierUpdateInput
1049
+ })).output(promotionTierOutput),
1050
+ delete: oc.route({
1051
+ method: "DELETE",
1052
+ path: "/promotions/tiers/{id}",
1053
+ summary: "Soft-delete promotion tier"
1054
+ }).input(z.object({ id: z.string().uuid() })).output(z.object({ ok: z.literal(true) }))
1055
+ },
1056
+ qualify: oc.meta(mcpMeta({
1057
+ expose: true,
1058
+ riskLevel: "safe"
1059
+ })).route({
1060
+ method: "POST",
1061
+ path: "/promotions/qualify",
1062
+ summary: "Return auto-applied promotions a cart qualifies for"
1063
+ }).input(qualificationInput).output(qualificationOutput)
1064
+ };
1065
+ //#endregion
741
1066
  //#region ../contract/src/schemas/reward-type.ts
742
1067
  const rewardTypeOutput = z.object({
743
1068
  id: z.string().uuid(),
@@ -908,152 +1233,6 @@ const validationRules = {
908
1233
  }).input(z.object({ id: z.string().uuid() })).output(z.object({ ok: z.literal(true) }))
909
1234
  };
910
1235
  //#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
1236
  //#region ../contract/src/routes/vouchers.ts
1058
1237
  const vouchers = {
1059
1238
  list: oc.meta(mcpMeta({
@@ -1112,6 +1291,14 @@ const vouchers = {
1112
1291
  path: "/vouchers/{code}/validate",
1113
1292
  summary: "Validate a voucher against an optional order context"
1114
1293
  }).input(validateInput).output(validateOutput),
1294
+ qualify: oc.meta(mcpMeta({
1295
+ expose: true,
1296
+ riskLevel: "safe"
1297
+ })).route({
1298
+ method: "POST",
1299
+ path: "/vouchers/qualify",
1300
+ summary: "Batch-qualify customer-held voucher codes for an order"
1301
+ }).input(qualifyInput).output(qualifyOutput),
1115
1302
  redeem: oc.meta(mcpMeta({
1116
1303
  expose: true,
1117
1304
  riskLevel: "mutating",
@@ -1556,6 +1743,7 @@ const contract = {
1556
1743
  customers,
1557
1744
  segments,
1558
1745
  campaigns,
1746
+ promotions,
1559
1747
  vouchers,
1560
1748
  validationRules,
1561
1749
  rewardTypes,