publ betaReference (MCP guide)
Open publ console

promotion_campaign_apply

# promotion_campaign_apply

Edit the selected existing promotion campaign's profile or activation state. Requires OWNER or a delegate with MANAGE_PROMOTION in the exact selected-channel resource. Existing campaign editing has no creation-plan gate and never buys or upgrades a plan. Use [promotion_benefit_apply](promotion_benefit_apply.md) for existing benefit profile and activation editing. [promotion_campaign_create](promotion_campaign_create.md) creates an active campaign with one initial ORDER_DISCOUNT benefit. Use [promotion_benefit_create](promotion_benefit_create.md) for an additional ORDER_DISCOUNT or DISCOUNT and [the benefit discount action](promotion_benefit_apply.md#existing-connected-product-discount) for an existing connected product. This existing editor moves no funds.

1. Call promotions_get with view `{kind:"profile",pcid}` using an observed opaque
   pcid. Copy campaignRevision from this exact profile.
2. Supply channelCode, pcid, requestId, expectedRevision, action and optional dryRun.
3. Use the same task ID/input for recovery. Read the profile again for current state.

## Actions

- `profile`: optional title and/or complete schedule. Title trims surrounding
  whitespace and accepts1–50 UTF-16 code units. Schedule requires startAt and endAt;
  each non-null time is an ISO8601 instant with Z or an explicit UTC offset.
  endAt:null removes the end. Requested end must be after start (a conservative
  bridge guard). No agent, host or browser timezone is inferred. Equivalent instants
  count as unchanged. Omitted fields retain their stored values; an unreadable
  stored start cannot be copied into a profile save without a replacement schedule.
- `activation`: active:true or false. Only this pcid is sent in the source batch
  array. Title, schedule, benefits and products are preserved. Activating a campaign
  may change visitor offer availability; saving a flag does not verify eligibility.

Profile dispatch is PATCH `/seller/api/v1/promotion-campaigns/{pcid}` with the
complete `{title,isActivated,startAt,endAt?}` body. Activation dispatch is POST to
`/bulk-activate` or `/bulk-deactivate` with `{pcids:[pcid]}`. Both bind the selected
channel bearer and x-publ-channel-id and require source200/data acknowledgement.
Profile receipts are checked, then fresh repeated GETs independently verify the
known campaign. Benefits, prices, segments and products must remain unchanged;
updatedAt is observed and embedded campaign titles may follow a campaign rename.
Only projected source UI fields are compared; unknown fields are not coverage.

## Planning, result and recovery

dryRun makes no business write and creates no journal. A matching saved task's
dryRun returns historical planning, not fresh verification. Unchanged settings
complete without dispatch. A successful fresh result includes promotionCampaign,
campaignRevision, stored verification and one exact configured-console URL at
`/channels/{channelCode}/promotion-campaign/campaigns/{pcid}/profile`.

Keep requestId and taskType:`promotion-campaign`. Task input, target, operator,
permissions, original snapshot and planned body are checked. A prepared task can
continue only through the identical apply input; resume merely observes it. Sent
requests are never resent, even if transport or acknowledgement is uncertain.
Resume reads the same known campaign and may complete after observing the exact
requested values and preserved fields. It cannot prove causality. Unsupported
receipt, changed values or context remain pending/error, without another save.
Completed replay is historical with no current console link. No automatic rollback.

The source has no atomic compare-and-swap. A preflight revision does not eliminate a race with another editor.

For an existing connected relationship, use [promotion_benefit_apply product-disconnect](promotion_benefit_apply.md#disconnect-an-existing-product-relationship) with the exact products-page revision and connected row ID. This removes only the relationship, verifies complete bounded absence and preserves the underlying product. Eligible new connections now use the bounded product-connect flow below; restorationSupported remains false, with no automatic relinking or rollback. A handoff is still GET-only/action_required and never claims the change happened. Financial execution remains a human-console action.

To connect eligible existing products to an existing DISCOUNT benefit, read [product-options](publ_promotions_get.md#product-connection-options) and use [product-connect](promotion_benefit_apply.md#connect-existing-products-to-a-discount-benefit) with connectionRevision and explicit values. The active-campaign prerequisite and five supported families apply; category model ambiguity, fixed-price CP conversion and deleted-product reconnection remain gaps. This creates relationships only. Additional DISCOUNT creation uses [promotion_benefit_create](promotion_benefit_create.md#add-a-discount-benefit-with-optional-initial-products) with its own options/creation revisions and Business-or-higher gate, permitting inactive parents. Neither flow creates underlying products, restores previous identities or executes finance.

## Inputs

| Field | Required here | Contract |
| --- | --- | --- |
| `pcid` | Yes | string; pattern `^[A-Za-z0-9_-]{1,128}$` |
| `action` | Yes | one of: `profile`, `activation` (by `kind`) |
| `action.title` | No | When `action.kind` is `profile`: string; min length 1; max length 50 |
| `action.schedule` | No | When `action.kind` is `profile`: object |
| `action.schedule.startAt` | Yes | When `action.kind` is `profile`: 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`; Explicit ISO8601 instant with Z or UTC offset. No host or agent timezone is inferred. |
| `action.schedule.endAt` | Yes | When `action.kind` is `profile`: 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 |
| `action.active` | Yes | When `action.kind` is `activation`: boolean |
| `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. |