publ betaReference (MCP guide)
Open publ console

discount_handoff

[Migration map](../reference/tool-name-migration.md).

# discount_handoff

Current configuration support: existing profile edits and one discount-code activation
can use [discount_settings_apply](discount_settings_apply.md). This handoff remains
an optional visit to the original screen and the route for coupon issuance and
withdrawal. The later creation extension in that writer is described below. A handoff is never a completed configuration save.

This implementation is a dependency of the documentation branch.

Observe the selected discount/coupon target or creation policy, then direct a person to the exact console location. The tool performs no offer or financial mutation.

## Input and permission

Requires OWNER or DELEGATEE with MANAGE_DISCOUNTS on the selected channel. Hosted source scope is channel-only. channelCode, family and opaque targetId use the [reader's rules](publ_discounts_get.md#access-and-identity). All objects are strict; there are no arbitrary URLs, financial values, selected coupon rows, requestId, revision or dryRun inputs.

| Input | Contract |
| --- | --- |
| channelCode | Required selected channel code. |
| family | Required discount-code or coupon-template. |
| action | Required discriminated object. |
| action.kind | create/edit for either family; activation for discount-code only; issue/withdraw for coupon-template only. |
| action.targetId | Required except for create; observed dcid or cotid matching the family. |

```json
{"channelCode":"selected-channel-code","family":"discount-code","action":{"kind":"create"}}
```

```json
{"channelCode":"selected-channel-code","family":"discount-code","action":{"kind":"activation","targetId":"observed-discount"}}
```

```json
{"channelCode":"selected-channel-code","family":"coupon-template","action":{"kind":"issue","targetId":"observed-template"}}
```

```json
{"channelCode":"selected-channel-code","family":"coupon-template","action":{"kind":"withdraw","targetId":"observed-template"}}
```

## Creation policy

create observes two stable, unfiltered family list pages with page 1/limit 100 and the current selected-channel plan. NORMAL_ESSENTIAL allows creation only when that family's unfiltered total is zero. NORMAL_BUSINESS, NORMAL_FROM_ENTERPRISE, NORMAL_PRO, ENTERPRISE_TRIAL, ENTERPRISE_ENTERPRISE, PARTNER_TRIAL, PARTNER_PARTNER and INTERNAL_INTERNAL are eligible under the shared higher-plan rule. FREE_TRIAL, NORMAL_STARTER, NORMAL_STARTER_2023_04 and NORMAL_BASIC are denied; unknown/missing plans cannot establish capacity.

creationPolicy contains currentPlan, canCreate, existingCount, sourceRule, capacityReserved:false and countInterpretation. This output does not contain a separate policyVerified flag. sourceRule describes Essential-or-higher with one record per family. The unfiltered total is a conservative interpretation of the original button's displayed-page length, not an atomic capacity reservation or exact UI count equivalence.

Denied/unknown creation policy returns DISCOUNT_PLAN_ACTION and the exact channel-plan destination. Otherwise the handoff returns DISCOUNT_HUMAN_ACTION. Existing target observation/edit handoffs do not inherit creation eligibility. No purchase, upgrade or quota bypass is performed.

## Console actions and result

| Action | Destination relative to the selected family root |
| --- | --- |
| create | /create, or channel-plan when blocked. |
| edit | /{targetId}/edit |
| activation | /{targetId}, discount-code only. |
| issue | /{targetId}/coupons, coupon-template only. |
| withdraw | /{targetId}/coupons, coupon-template only. |

Family roots are the reader's `/discounts/discount-codes` and `/discounts/coupon-templates` routes. Parent IDs and content are read twice, then rechecked with selected channel/operator/credentials before returning an existing-target destination. No fictional modal URL or generic console fallback is generated; demo omits live navigation.

Valid handoffs return action_required, writesPerformed:false, action, requiredAction.kind open_console and verification {confirmed:false,targetObserved:true,financialActionPerformed:false}. creationPolicy appears for create only. An exact destination is not a submitted wizard or completed offer change.

sourceGuidance for issue explains that the console checks connected benefits and may warn when none exist, while its Later path can still open issuance. For withdraw it directs the person to choose current rows within the template; the source blocks USED, WITHDRAWN and EXPIRED rows. The handoff does not preselect or independently validate any final coupon selection.

Identity, page and context errors follow the [reader's consistency rules](publ_discounts_get.md#consistency-and-limits). No mutation, duplicate-code check POST, coupon generation, withdrawal, activation or job is performed. Task status/resume and mutation retries do not apply. Existing coupon balances, customer eligibility, delivery, console click-through and hosted operation require separate evidence.

## Settings writer boundary

Its revision comes from the matching profile read, and recovery uses discount-settings. This handoff's input and effects remain unchanged: edit/activation/create are console visits when this handoff is chosen; coupon issue and withdrawal still require human action. A returned edit URL opens the source edit route, while the writer's verified destination is the selected profile.

The later writer extension supports new creation as described below. Batch activation, coupon activation, empty-description clearing on existing offers, issuance and withdrawal remain outside it. Creation-plan/capacity checks above belong to this handoff and are not the existing-target edit permission gate. No handoff result is a completed save, financial transaction or issuance/withdrawal.

## Source-supported creation alternative

This handoff remains an optional human route and performs no creation or duplicate-code POST. Its creationPolicy is not the writer's creationRevision; prepare the exact draft through the getter before using the writer.

The writer uses complete bounded inventory, source duplicate validation, optional pre-existing segment conditions and separate discount-create recovery. Sent creation never repeats, and missing authoritative identity is not inferred. New coupon templates use the reachable RANDOM flow but are not coupon issuance. The original handoff input, destinations and financial boundaries remain unchanged.

## Inputs

| Field | Required here | Contract |
| --- | --- | --- |
| `family` | Yes | `discount-code`, `coupon-template` |
| `action` | Yes | one of: `create`, `edit`, `activation`, `issue`, `withdraw` (by `kind`) |
| `action.targetId` | Yes | When `action.kind` is `edit` or `activation` or `issue` or `withdraw`: string; pattern `^[A-Za-z0-9_-]{1,128}$` |