promotion_benefit_create
Add one ORDER_DISCOUNT or DISCOUNT benefit to an observed existing promotion campaign. Requires OWNER or MANAGE_PROMOTION and an observed Business-or-higher plan. Essential permits an initial campaign but does not permit this additional-benefit operation. The existing campaign may be inactive; adding a benefit does not activate it. Use the connected server's discovery to check availability; source implementation is not evidence that the running service has been updated.
- Read
promotions_getand select an observed opaquepcid. - Submit the complete benefit below with
dryRun: true. Dates must fit within the selected campaign; supply your intended timezone as an offset orZ. - Copy the returned
creationRevisionintoexpectedRevision, setdryRun: false, and submit the exact same values and stablerequestId.
{
"channelCode": "selected-channel-code",
"pcid": "observed-campaign",
"requestId": "add-order-benefit-001",
"dryRun": true,
"benefit": {
"kind": "ORDER_DISCOUNT",
"alias": "Seasonal offer",
"memberDisplayText": "Order offer",
"schedule": {"startAt": "2026-11-01T00:00:00+09:00", "endAt": "2026-11-30T23:59:59+09:00"},
"target": {"kind": "ALL"},
"discountAmount": "100",
"benefitLimitOption": "ONE_TIME"
}
}
The example dates and amount are illustrative. Use the selected campaign period
and observed channel currency. KRW/JPY allow whole multiples of100 from100 to
10,000,000; USD allows0.01 to10,000 at cent precision. No conversion or rounding
is performed. Alias uses4–20 UTF-16 units and display text1–14. Use UNLIMITED for
unlimited application count. An unlimited period uses endAt: null only when the
parent is unlimited. A segment target uses
{"kind":"SEGMENT","distinctId":"observed-segment"} and must be freshly
verified for the channel. There are no product-selection, trigger-override or
activation fields for this bounded order-discount operation.
The plan reads the parent, existing benefits, plan, currency and audience twice.
Saving rechecks them before one POST. The authoritative receipt supplies the new
pbid; two parent/detail reads must agree and preserve existing settings and
benefits. Source activation is retained without an additional activation call.
Successful verification covers stored configuration, not visitor checkout or
benefit delivery. This operation does not buy a service plan or transfer money.
If uncertain, retain the task ID and use task_status/task_resume with
taskType: "promotion-benefit-create". Neither dispatches creation. Missing
receipt identity requires inspecting the console; do not create again under a
new ID or infer identity from a title/list difference. A prepared task needs the
identical original call. Completed history is unconfirmed on replay; use a fresh
promotions_get benefit detail for current values.
Open the one returned console link: the selected add-benefit screen for a current plan, or the exact new benefit detail after verified completion. Historical or uncertain results do not fabricate a destination. GRANT and LIMITED_TIME creation and unsupported product configuration use the source handoff where still unsupported. No benefit deletion or automatic restoration is promised.
Add a discount benefit with optional initial products
Confirm current connected discovery. Initial campaign creation still accepts only ORDER_DISCOUNT; this extension adds one benefit to an existing campaign.
The strict DISCOUNT object uses the common alias, memberDisplayText, schedule, target and required benefitLimitOption above, plus these fields. It has no benefit-level discountAmount, caller-supplied pbid, activation, trigger, repeatability, notice or automatic sales-stop controls. Source expression/execution triggers are EXPRESSION_VIEW and EXECUTION_PURCHASE. Explicit ONE_TIME or UNLIMITED controls the application limit; the source wizard's UNLIMITED default is not an omitted-input default. GRANT repeatability and LIMITED_TIME controls are separate contracts.
| Field | Contract |
|---|---|
| kind | DISCOUNT. |
| productType | SubscriptionProduct, ChannelActionProduct, PAppUnlockProduct, TicketEvent or CommerceProduct. |
| mode | FIXED_PRICE for the three digital families; RATE_DISCOUNT or AMOUNT_DISCOUNT for TicketEvent/CommerceProduct. |
| products | Required array of zero to100 unique {targetId,value} objects. Empty initial selection is permitted. |
| products[].targetId | The observed choice.distinctId, not a numeric selector ID or existing relationship row ID. |
| products[].value | Explicit plain decimal string, up to40 characters, in the selected mode. No sign, exponent, rounding, currency conversion or automatic price inference. |
| selection | Required for a nonempty products array: exact page/limit/search from benefit-options plus its64-character lowercase hexadecimal optionsRevision. Omit for an empty selection unless intentionally binding an observed selector. If present, it must remain current even with no products. |
For a nonempty selection, first call promotions_get benefit-options using only pcid, productType and selector query on the selected channel. The new benefit has no pbid yet. Choose only selectable rows from that exact page and retain their distinctId values. Preserve the query and optionsRevision in the create dry run. Review sourceWrite/sourceDependencies and copy the separately returned creationRevision to expectedRevision for actual submission with the same requestId and values. optionsRevision, connectionRevision, benefitRevision and creationRevision are not interchangeable. A dry run creates no journal or reserved campaign slot.
Values follow the connected-product discount rules: FIXED_PRICE is the final price bounded by the channel minimum and verified original selling price; RATE_DISCOUNT is a percentage, not a fractional ratio; AMOUNT_DISCOUNT is the reduction in observed channel currency. The channel-specific percentage exception and exact payment units still apply. A selectable choice does not prove its requested value is valid. Category/CommerceProductCategory mapping and fixed-price CP conversion remain unsupported. Products must already exist; this method does not create a product.
This empty initial selection needs no options read. Replace the channel, parent and dates with intended observed values:
{
"channelCode": "selected-channel-code",
"pcid": "observed-campaign",
"requestId": "add-discount-empty-001",
"dryRun": true,
"benefit": {
"kind": "DISCOUNT",
"alias": "Member offer",
"memberDisplayText": "Member price",
"schedule": {"startAt": "2026-11-01T00:00:00+09:00", "endAt": "2026-11-30T23:59:59+09:00"},
"target": {"kind": "ALL"},
"benefitLimitOption": "UNLIMITED",
"productType": "SubscriptionProduct",
"mode": "FIXED_PRICE",
"products": []
}
}
This selected ticket example requires a fresh matching benefit-options read.
{
"channelCode": "selected-channel-code",
"pcid": "observed-campaign",
"requestId": "add-ticket-discount-001",
"dryRun": true,
"benefit": {
"kind": "DISCOUNT",
"alias": "Ticket offer",
"memberDisplayText": "Ticket offer",
"schedule": {"startAt": "2026-11-01T00:00:00+09:00", "endAt": "2026-11-30T23:59:59+09:00"},
"target": {"kind": "ALL"},
"benefitLimitOption": "ONE_TIME",
"productType": "TicketEvent",
"mode": "RATE_DISCOUNT",
"products": [{"targetId": "observed-ticket-event", "value": "10"}],
"selection": {"page": 1, "limit": 15, "optionsRevision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
}
}
Inputs
| Field | Required here | Contract |
|---|---|---|
pcid |
Yes | string; pattern ^[A-Za-z0-9_-]{1,128}$ |
benefit |
Yes | one of: ORDER_DISCOUNT, DISCOUNT, GRANT, LIMITED_TIME (by kind) |
benefit.alias |
Yes | When benefit.kind is ORDER_DISCOUNT: string; min length 4; max length 20 |
benefit.memberDisplayText |
Yes | When benefit.kind is ORDER_DISCOUNT: string; min length 1; max length 14 |
benefit.schedule |
Yes | When benefit.kind is ORDER_DISCOUNT: object |
benefit.schedule.startAt |
Yes | When benefit.kind is ORDER_DISCOUNT: string; pattern ^(?:(?:\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)))$; format date-time |
benefit.schedule.endAt |
Yes | When benefit.kind is ORDER_DISCOUNT: string; pattern ^(?:(?:\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)))$; format date-time or null |
benefit.target |
Yes | When benefit.kind is ORDER_DISCOUNT: one of: ALL, SEGMENT (by kind) |
benefit.target.distinctId |
Yes | When benefit.kind is ORDER_DISCOUNT and benefit.target.kind is SEGMENT: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
benefit.discountAmount |
Yes | When benefit.kind is ORDER_DISCOUNT: string; pattern ^(0|[1-9]\d{0,7})(\.\d{1,2})?$; Plain decimal amount in the freshly observed channel base currency. No currency conversion or rounding. |
benefit.benefitLimitOption |
Yes | When benefit.kind is ORDER_DISCOUNT: ONE_TIME, UNLIMITED |
benefit.alias |
Yes | When benefit.kind is DISCOUNT: string; min length 4; max length 20 |
benefit.memberDisplayText |
Yes | When benefit.kind is DISCOUNT: string; min length 1; max length 14 |
benefit.schedule |
Yes | When benefit.kind is DISCOUNT: object |
benefit.schedule.startAt |
Yes | When benefit.kind is DISCOUNT: string; pattern ^(?:(?:\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)))$; format date-time |
benefit.schedule.endAt |
Yes | When benefit.kind is DISCOUNT: string; pattern ^(?:(?:\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)))$; format date-time or null |
benefit.target |
Yes | When benefit.kind is DISCOUNT: one of: ALL, SEGMENT (by kind) |
benefit.target.distinctId |
Yes | When benefit.kind is DISCOUNT and benefit.target.kind is SEGMENT: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
benefit.benefitLimitOption |
Yes | When benefit.kind is DISCOUNT: ONE_TIME, UNLIMITED |
benefit.productType |
Yes | When benefit.kind is DISCOUNT: SubscriptionProduct, ChannelActionProduct, PAppUnlockProduct, TicketEvent, CommerceProduct |
benefit.mode |
Yes | When benefit.kind is DISCOUNT: FIXED_PRICE, RATE_DISCOUNT, AMOUNT_DISCOUNT |
benefit.products |
Yes | When benefit.kind is DISCOUNT: array; max items 100 |
benefit.products[].targetId |
Yes | When benefit.kind is DISCOUNT: string; pattern ^[A-Za-z0-9_-]{1,128}$; targetId of a selectable choice from the options view. |
benefit.products[].value |
Yes | When benefit.kind is DISCOUNT: string; max length 40; pattern ^(0|[1-9]\d*)(\.\d+)?$; FIXED_PRICE (membership plans, content category products, paid-access products): the new sale price in the channel currency. RATE_DISCOUNT: the percentage. AMOUNT_DISCOUNT: the amount taken off. |
benefit.alias |
Yes | When benefit.kind is GRANT: string; min length 4; max length 20 |
benefit.schedule |
Yes | When benefit.kind is GRANT: object |
benefit.schedule.startAt |
Yes | When benefit.kind is GRANT: string; pattern ^(?:(?:\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)))$; format date-time |
benefit.schedule.endAt |
Yes | When benefit.kind is GRANT: string; pattern ^(?:(?:\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)))$; format date-time or null |
benefit.target |
Yes | When benefit.kind is GRANT: one of: ALL, SEGMENT (by kind) |
benefit.target.distinctId |
Yes | When benefit.kind is GRANT and benefit.target.kind is SEGMENT: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
benefit.executionTrigger |
No | When benefit.kind is GRANT: EXECUTION_SIGNUP, EXECUTION_SIGNIN, EXECUTION_PURCHASE; default EXECUTION_SIGNUP |
benefit.coupons |
No | When benefit.kind is GRANT: array; max items 100; default [] |
benefit.coupons[].targetId |
Yes | When benefit.kind is GRANT: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
benefit.alias |
Yes | When benefit.kind is LIMITED_TIME: string; min length 4; max length 20 |
benefit.schedule |
Yes | When benefit.kind is LIMITED_TIME: object; Sales period. The end is always required. |
benefit.schedule.startAt |
Yes | When benefit.kind is LIMITED_TIME: string; pattern ^(?:(?:\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)))$; format date-time |
benefit.schedule.endAt |
Yes | When benefit.kind is LIMITED_TIME: string; pattern ^(?:(?:\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)))$; format date-time |
benefit.target |
No | When benefit.kind is LIMITED_TIME: object; default {"kind":"ALL"} |
benefit.target.kind |
Yes | When benefit.kind is LIMITED_TIME: ALL |
benefit.noticeStartAt |
No | When benefit.kind is LIMITED_TIME: string; pattern ^(?:(?:\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)))$; format date-time |
benefit.productType |
Yes | When benefit.kind is LIMITED_TIME: SubscriptionProduct, ChannelActionProduct, PAppUnlockProduct |
benefit.products |
No | When benefit.kind is LIMITED_TIME: array; max items 100; default [] |
benefit.products[].targetId |
Yes | When benefit.kind is LIMITED_TIME: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
benefit.autoToggleOnBenefitPeriod |
No | When benefit.kind is LIMITED_TIME: boolean; default true |
confirmToken |
No | string; pattern ^ct[a-f\d]{32}$; The confirmToken from this tool's preview result. Omit it to preview; repeat the same input with it to execute. |