Create or replace an automatic promotion rule
UPSERTS one automatic promotion rule by id: there is no separate create and update, and no PATCH. Posting an existing id REPLACES that rule wholesale — every field not in your body is destroyed, including the reward mechanics. DANGER: the API exposes NO read-modify-write path for a rule. The only reads (GET /v1/promotions, GET /v1/promotions/{id}) return a six-field directory projection with no mechanics in it, so you CANNOT fetch a live rule, edit one field and post it back. Call this only when you already hold the rule’s complete definition — because you are creating it now, or because the caller supplied every field. If you were asked to tweak an existing promotion (extend a window, change a percentage) and you do not have its full mechanics, STOP and ask; do not reconstruct plausible values to satisfy the schema, as that silently overwrites a live pricing rule. Pick the type by the mechanic you want: bogo (buy buyQuantity of buySkuId, get getQuantity of getSkuId at getDiscountBps off — 10000 bps makes it free), threshold (once skuId reaches minQuantity units, a percent or flat reward comes off that SKU’s subtotal) or bundle (buy the components together and pay bundlePriceCents). Money is integer CENTS and percentages are BASIS POINTS (1000 = 10%). validFrom/validUntil are inclusive ISO-8601 instants (Z or a UTC offset) and validUntil must not precede validFrom. Use this for an always-on offer any matching basket earns; use POST /v1/coupons instead when the reduction must be unlocked by a code the customer presents.
Authorizations
Authorization: Bearer <token>. Accepts EITHER a Keycloak access token (scopes-in-token) OR an opaque POS session token; both resolve to the same pos.* scope vocabulary the route guards enforce.
Body
- Option 1
- Option 2
- Option 3
1^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$bogo ^[0-9a-f]{64}$0 < x <= 9007199254740991^[0-9a-f]{64}$0 < x <= 90071992547409911 <= x <= 10000Response
The persisted rule, echoed back with its validity window as ISO instants.
- Option 1
- Option 2
- Option 3
The persisted rule, echoed back with its validity window as ISO instants.
1^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$bogo ^[0-9a-f]{64}$0 < x <= 9007199254740991^[0-9a-f]{64}$0 < x <= 90071992547409911 <= x <= 10000
