promotions_get
This implementation is a dependency of the documentation branch.
Read campaign lists, a campaign profile, benefits, one benefit, its target products or eligible product-connection/initial-benefit options. All business requests are GET. Use promotion_campaign_apply for campaign profile/activation and promotion_benefit_apply for benefit profile/activation, supported connected-product discounts, eligible product connections or relationship disconnection, promotion_campaign_create for a new active campaign with one initial ORDER_DISCOUNT benefit, and the separate handoff for remaining configuration and notification reader for coupon notification settings. Campaigns are distinct from conversion links and legacy promotion links.
Access and input
Requires OWNER or DELEGATEE with MANAGE_PROMOTION on the selected channel; conversion-link or generic marketing permission alone is insufficient. Hosted source rules expose this tool only on the exact selected-channel resource. These source rules are not deployed authorization evidence.
channelCode is 1–512 characters, excluding literal dot/double dot, slash, backslash, ASCII controls and DEL. pcid and pbid are observed opaque strings matching [A-Za-z0-9_-]{1,128}; the exact reserved values create, next and add are rejected. Numeric row IDs are not path identifiers. Strict objects reject unsupported fields.
| Input | Contract |
|---|---|
| channelCode | Required selected channel code. |
| view | Optional; defaults to campaigns, page 1, limit 15. |
| view.kind | campaigns, profile, benefits, benefit, products, product-options or benefit-options. |
| view.pcid | Required for all views except campaigns. |
| view.pbid | Required only for benefit, products and product-options. |
| view.page | campaigns, benefits, products, product-options and benefit-options only; positive safe integer, default 1. |
| view.limit | Those five paginated views only; 15, 30, 50 or 100, default 15. |
| view.search | campaigns, product-options and benefit-options only; string up to 500 characters, without trimming. |
| view.productType | Required only for benefit-options; one of its five supported families. |
| view.isActivated | campaigns and benefits only; optional boolean. |
Replace illustrative identities with values returned by authorized reads.
{"channelCode":"selected-channel-code","view":{"kind":"campaigns","page":1,"limit":15,"search":"summer","isActivated":true}}
{"channelCode":"selected-channel-code","view":{"kind":"profile","pcid":"observed-campaign"}}
{"channelCode":"selected-channel-code","view":{"kind":"benefits","pcid":"observed-campaign","page":1,"limit":30,"isActivated":false}}
{"channelCode":"selected-channel-code","view":{"kind":"benefit","pcid":"observed-campaign","pbid":"observed-benefit"}}
{"channelCode":"selected-channel-code","view":{"kind":"products","pcid":"observed-campaign","pbid":"observed-benefit","page":1,"limit":50}}
Returned data
Success has code PROMOTION_SCREEN, writesPerformed:false, screenId channel.promotions and contentTrust untrusted_user_content. Payloads differ by view:
| View | Payload |
|---|---|
| campaigns | promotionCampaigns, pagination and coverage. |
| profile | promotionCampaign and campaignRevision. |
| benefits | campaign {id,pcid,title}, benefits, pagination and coverage. |
| benefit | campaign {id,pcid,title}, benefit and benefitRevision. |
| products | campaign {id,pcid,title}, benefit {id,pbid,benefitType,discountMethod,targetProductType}, targetProducts, pagination, coverage, baseCurrency and productRevision on the current currency-capable adapter. |
| product-options | campaign/benefit selection, mode, productType, baseCurrency, choices, pagination, connectionRevision, activeCampaignRequired, selectionBound, inventoryBound, scope and limitations. |
| benefit-options | campaign selection, productType, modes, baseCurrency, canCreate, plan, optionsRevision, pagination, choices, initialProductsRequired:false, selectionBound:100 and activationChangePerformed:false. No pbid or existing benefit selection. |
pagination contains currentPage, limit and totalEntries. The returned page and limit, expected row count, unique identities and requested activation filter are checked. For views that return coverage, it is {mode:one-page,completeInventory:false,nextPage}; nextPage is an integer or null. Follow pages explicitly. There is no product-type/search filter for products or search filter for benefits. Product-options supplies scope and bounds instead of coverage/nextPage. Benefit-options also uses pagination without coverage/nextPage; inspect the returned pagination and request another page explicitly.
Campaign projections contain id, pcid, title, isActivated, startAt, benefits, insertedAt and updatedAt, with optional channelId/endAt. Benefits contain id, pcid, pbid, isActivated, benefitType, memberDisplayText, description, activationTarget, targetSegments, startAt and targetProducts. Optional source fields include alias, expressionTrigger, executionTrigger, endAt, noticeStartAt, discountMethod, discountPrice, targetProductType, isRepeatable, benefitLimitOption, benefitOverlapOption, autoToggleOnBenefitPeriod and an embedded promotionCampaign. benefitType is DISCOUNT, ORDER_DISCOUNT, GRANT or LIMITED_TIME. Legacy activationTarget MEMBER_ALL is normalized to ALL, including embedded benefits; SEGMENT remains SEGMENT.
Target products expose id, type, targetId, isActivated, isDeleted, name, detail, pAppCode and price, with optional channelId, isPublic, insertedAt, expiresAt, subscriptionProductType, subscriptionProductExpiryPeriodInDays, contentsCount, segmentCount, segmentTitle, discountedPrice, discountPrice, discountRatio, categories and productCount. Supported type values are None, SubscriptionProduct, ChannelActionProduct, PAppUnlockProduct, TicketEvent, CouponTemplate, DiscountCode, ConversionLink, CommerceProduct and CommerceProductCategory. Returned fields are projections; unspecified properties are stripped. Nested benefits, segments/products and categories are bounded at 5,000; page rows are bounded at 100.
Price is {amount,currency}; amount retains its source string or number and currency remains a source string. No rounding, minor-unit conversion, exchange conversion, discount calculation or entitlement decision is performed. Nullable and optional source fields remain distinct. Stored links/HTML are untrusted data and are never fetched or executed.
Consistency and limitations
Selected campaign and benefit records are read twice, then reread after nested data. Complete projected content must agree. All explicit channelId values must match the selected channel; some source DTOs omit channelId, so the result separately discloses credential/header/request binding. Parent pcid/pbid, embedded numeric campaign identity and unique nested IDs are checked. Selected channel, operator and credentials are rechecked before success. These checks do not constitute an atomic snapshot.
PROMOTION_PAGE_UNVERIFIED, PROMOTION_CAMPAIGN_UNVERIFIED, PROMOTION_BENEFIT_UNVERIFIED and PROMOTION_PRODUCTS_UNVERIFIED reject malformed observations. PROMOTION_CHANNEL_MISMATCH, PROMOTION_CAMPAIGN_MISMATCH, PROMOTION_BENEFIT_MISMATCH and PROMOTION_SEGMENTS_UNVERIFIED reject foreign or ambiguous identities. PROMOTION_CHANGED rejects changed parent content. Authentication/context failures must not be bypassed with a different target or guessed ID.
verification.confirmed means the bounded source observation passed checks; atomicSnapshot and visitorBenefitVerified are false; pricingRecalculated is false in views that return it. A visible active offer does not prove a customer receives it. This reader creates no task, offer, notification or reservation.
Navigation
The console root is /channels/{channelCode}/promotion-campaign. campaigns links to that root; profile to /campaigns/{pcid}/profile; benefits to /campaigns/{pcid}/benefits; benefit to /campaigns/{pcid}/benefits/{pbid}; products adds /target-product to the benefit route. Segments are encoded. Demo omits live destinations. Canonical console routes are separate from the API environment and are not proof of browser or visitor behavior.
Current selected-page behavior
The current result also includes the parsed view; page, limit and supported filters are preserved in its single console address for ordinary paginated views. Product-options opens the product-add wizard; benefit-options opens the campaign add-benefit wizard. Neither encodes modal page/search state or productType in URL parameters. The address is checked against the observed page and selected parent. This supersedes the historical contract's bare-path navigation. Empty first pages remain valid; a page beyond the observed last page returns PROMOTION_PAGE_OUT_OF_RANGE without fetching another page. Request page1 explicitly to refresh. No additional input or financial write was introduced.
Current existing-campaign editing
The earlier mandatory handoff boundary for campaign metadata/schedule/activation is superseded. promotions_get view profile now returns campaignRevision for promotion_campaign_apply, which saves the selected existing campaign profile or activation while preserving benefits and product prices. A handoff remains available for people. Existing benefit profile/activation uses promotion_benefit_apply. The new promotion_campaign_create supports one initial ORDER_DISCOUNT benefit in an active new campaign. Use promotion_benefit_create for an additional ORDER_DISCOUNT or DISCOUNT in an existing campaign, with Business-or-higher required. Existing connected-product discounts and relationship disconnection use the actions below. Money movement stays human-operated.
Current connected-product selection
The current currency-capable adapter reads the complete selected campaign, benefit, exact products page and channel base currency twice, compares them, then rechecks parents and channel/operator context. It returns productRevision, baseCurrency and the benefit's discountMethod/targetProductType alongside the page. If currency cannot be verified, the read fails with PROMOTION_CURRENCY_UNVERIFIED; an older adapter without this capability does not supply a usable product revision. Confirm current connected discovery and result fields.
Use promotion_benefit_apply with action.kind:"product-discount", the connected row's numeric id as targetProductId, and the exact returned page/limit. The row's targetId is not the connected-row identity. Copy productRevision, not benefitRevision or campaignRevision. This revision binds parent content, exact page, currency and operator; another page or any changed selection requires a fresh read. The products view input is unchanged; product-options below is the separate selector view.
Row types must agree with the selected benefit. Present embedded rows must agree with the page; absent embedded projections are not synthesized. Empty first pages can be valid but contain no editable row. An out-of-range page is rejected without fetching a replacement. Follow pages explicitly; the revision does not cover unseen pages atomically or establish visitor eligibility, checkout calculation or customer benefit delivery.
{"channelCode":"selected-channel-code","view":{"kind":"products","pcid":"observed-campaign","pbid":"observed-benefit","page":1,"limit":15}}
The same page revision also prepares product-disconnect. This reader still returns one page; the writer separately obtains a complete stable inventory of up to 1000 relationships before removing one link. Its internal inventory does not change the public revision contract. Disconnecting a link is not underlying product deletion and does not require an active or non-deleted underlying product. Use the action-specific benefit/product combinations, not the discount editor's narrower eligibility rules.
After a confirmed removal, follow the writer's observed products page and new revision. If the original page collapsed, that result explicitly identifies the freshly read surviving page 1. The reader itself still rejects out-of-range requests without automatic replacement. Eligible new connections now use product-options and product-connect below; unsupported families and automatic restoration remain separate.
Product connection options
Use view:{kind:"product-options",pcid,pbid,page,limit,search?} on the selected channel with MANAGE_PROMOTION. It reads the source entity selector, complete bounded existing relationships, parents and channel base currency twice, then rechecks the parent/context. It sends GET only and does not reserve choices, create a relationship or obtain permission for a business change. Strict inputs reject unsupported fields.
The selected existing benefit must be DISCOUNT with a supported product family and stored mode. product-connect supports SubscriptionProduct, ChannelActionProduct and PAppUnlockProduct with FIXED_PRICE, or TicketEvent and CommerceProduct with RATE_DISCOUNT/AMOUNT_DISCOUNT. Unsupported family/mode, duplicate selector IDs, explicit foreign channel/model or invalid selector pagination fails with no automatic replacement selection. An explicit SubscriptionProduct subtype must be ONCE_FINITE, ONCE_INFINITE, SUBSCRIPTION_30DAYS, SUBSCRIPTION_90DAYS, SUBSCRIPTION_180DAYS or SUBSCRIPTION_365DAYS; another explicit subtype contradicts the source selector and fails the read.
The result gives connectionRevision, mode, productType, baseCurrency, activeCampaignRequired:true, selectionBound:100, inventoryBound:1000 and scope:"one-selector-page". Choices preserve source id, distinctId, title, targetProductModel and optional source price/channel/flags/type fields, adding selectable, alreadyLinked and reason. Copy a choice's distinctId to the writer's targetProducts[].targetId; numeric selector id and connected-row id are different identities. The revision binds the observed selector query/content and parent/currency/context snapshot. The complete inventory is also rechecked and bound to the writer's durable plan; these are repeated observations, not an atomic inventory.
Each choice reports the first applicable unavailable reason below; null means selectable at that observation. Choice eligibility does not validate a requested value, reserve capacity or guarantee a later write.
| reason | Meaning |
|---|---|
| CAMPAIGN_INACTIVE | Adding requires an active campaign. The options remain readable, with choices unavailable. |
| ALREADY_LINKED | The target already has a relationship, including a deleted-product relationship; no implicit duplicate or price update. |
| PRODUCT_DELETED | The candidate is marked deleted. |
| POINT_PRICE_CONVERSION_UNVERIFIED | FIXED_PRICE would require unverified CP conversion. |
| ORIGINAL_PRICE_UNVERIFIED | FIXED_PRICE lacks a valid original price in the channel base currency. |
| INVENTORY_BOUND | The existing inventory already reaches the bridge's 1000-row verification bound. A multi-choice request must also fit the resulting total. |
These bounds are bridge verification limits, not source plan or business quotas. Commerce category Category/CommerceProductCategory ambiguity and CP fixed-price conversion remain explicit connection gaps. Other benefit types and new DISCOUNT benefit creation are outside this connection flow.
The single console link opens /channels/{channelCode}/promotion-campaign/campaigns/{pcid}/benefits/{pbid}/target-product/add. Search and selector page/limit are in-memory modal state; the link deliberately has no invented modal query parameters. Its navigationExplanation describes this limitation. After a write, use the returned observed products-tab destination; added rows can be on later pages.
{"channelCode":"selected-channel-code","view":{"kind":"product-options","pcid":"observed-campaign","pbid":"observed-benefit","page":1,"limit":15,"search":"summer"}}
New discount benefit options
Supply pcid, productType and exact page/limit/search. There is no pbid before creation, and strict input rejects it. product-options above remains the separate existing-benefit selector with connectionRevision.
{"channelCode":"selected-channel-code","view":{"kind":"benefit-options","pcid":"observed-campaign","productType":"TicketEvent","page":1,"limit":15}}
The reader observes parent, plan, currency and the selected source entity page twice, then rechecks parent/context. It returns GET-only PROMOTION_SCREEN, writesPerformed:false and the fields listed above. Business-or-higher determines canCreate; an inactive campaign is permitted and is not activated. initialProductsRequired:false allows an empty products array in the creator without reading options. selectionBound:100 is a bridge verification bound for one selector page, not a business capacity limit or reservation.
SubscriptionProduct, ChannelActionProduct and PAppUnlockProduct expose modes:[FIXED_PRICE]; TicketEvent and CommerceProduct expose modes:[RATE_DISCOUNT,AMOUNT_DISCOUNT]. Candidate identities, subtype/channel/model/pagination checks use the same source entity selectors. Choices contain observed id, distinctId, title, targetProductModel and optional source fields with selectable and reason. Copy choice.distinctId to benefit.products[].targetId. Existing relationship row IDs are not creation targets. This view has no alreadyLinked check against another benefit, no activeCampaignRequired flag and no1000-row existing-relationship inventory.
Each choice uses the first applicable reason: PLAN_UNAVAILABLE, PRODUCT_DELETED, POINT_PRICE_CONVERSION_UNVERIFIED for fixed-price CP, or ORIGINAL_PRICE_UNVERIFIED for an absent/invalid/foreign-currency fixed-price original. Null means selectable at observation, not that every requested value is valid. Category/CommerceProductCategory mapping and CP conversion remain unavailable.
optionsRevision binds parent, plan, currency, productType, exact selector query/content and current operator context. Carry it in benefit.selection together with page/limit/search for a nonempty selection; changed selection or dependencies require a fresh read. The create dry run then supplies a separate creationRevision for expectedRevision. No task, benefit, product or reservation is created by this read. It opens the selected campaign add-benefit wizard without a fabricated new-benefit address or selector-state URL.
Inputs
| Field | Required here | Contract |
|---|---|---|
view |
No | one of: campaigns, profile, benefits, benefit, products, product-options, campaign-options, benefit-options, coupon-options (by kind); default {"kind":"campaigns","page":1,"limit":15} |
view.page |
No | When view.kind is campaigns: integer; greater than 0; default 1 |
view.limit |
No | When view.kind is campaigns: 15 or 30 or 50 or 100; default 15 |
view.search |
No | When view.kind is campaigns: string; max length 500 |
view.isActivated |
No | When view.kind is campaigns: boolean |
view.pcid |
Yes | When view.kind is profile: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.pcid |
Yes | When view.kind is benefits: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.page |
No | When view.kind is benefits: integer; greater than 0; default 1 |
view.limit |
No | When view.kind is benefits: 15 or 30 or 50 or 100; default 15 |
view.isActivated |
No | When view.kind is benefits: boolean |
view.pcid |
Yes | When view.kind is benefit: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.pbid |
Yes | When view.kind is benefit: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.pcid |
Yes | When view.kind is products: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.pbid |
Yes | When view.kind is products: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.page |
No | When view.kind is products: integer; greater than 0; default 1 |
view.limit |
No | When view.kind is products: 15 or 30 or 50 or 100; default 15 |
view.pcid |
Yes | When view.kind is product-options: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.pbid |
Yes | When view.kind is product-options: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.page |
No | When view.kind is product-options: integer; greater than 0; default 1 |
view.limit |
No | When view.kind is product-options: 15 or 30 or 50 or 100; default 15 |
view.search |
No | When view.kind is product-options: string; max length 500 |
view.productType |
Yes | When view.kind is campaign-options: SubscriptionProduct, ChannelActionProduct, PAppUnlockProduct, TicketEvent, CommerceProduct |
view.benefitType |
No | When view.kind is campaign-options: DISCOUNT, LIMITED_TIME |
view.page |
No | When view.kind is campaign-options: integer; greater than 0; default 1 |
view.limit |
No | When view.kind is campaign-options: 15 or 30 or 50 or 100; default 15 |
view.search |
No | When view.kind is campaign-options: string; max length 500 |
view.pcid |
Yes | When view.kind is benefit-options: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.productType |
Yes | When view.kind is benefit-options: SubscriptionProduct, ChannelActionProduct, PAppUnlockProduct, TicketEvent, CommerceProduct |
view.benefitType |
No | When view.kind is benefit-options: DISCOUNT, LIMITED_TIME |
view.page |
No | When view.kind is benefit-options: integer; greater than 0; default 1 |
view.limit |
No | When view.kind is benefit-options: 15 or 30 or 50 or 100; default 15 |
view.search |
No | When view.kind is benefit-options: string; max length 500 |
view.pcid |
No | When view.kind is coupon-options: string; pattern ^[A-Za-z0-9_-]{1,128}$ |
view.page |
No | When view.kind is coupon-options: integer; greater than 0; default 1 |
view.limit |
No | When view.kind is coupon-options: 15 or 30 or 50 or 100; default 15 |
view.search |
No | When view.kind is coupon-options: string; max length 500 |