ticket_discount_settings_apply — save one existing discount policy operation
Save the exact operation prepared by ticket_discount_settings_get: metadata, salesTerms, tierPrice, tierLink, tierUnlink or delete. Deletion shares these public methods and uses the internal commerce-ticket-policy-lifecycle recovery task.
Inputs
| Field | Required here | Contract |
|---|---|---|
channelCode |
Yes | string; min length 1; max length 512; The exact channelCode selected through channel discovery. Do not supply a URL, channel numeric ID, account ID or credentials. |
pAppCode |
Yes | E00003 |
eventId |
Yes | string; min length 1; max length 160; pattern ^[A-Za-z0-9_-]+$; Exact resource identifier returned by this operation, never a title, unrelated row ID or URL. |
policyId |
No | string; min length 1; max length 160; pattern ^[A-Za-z0-9_-]+$; Exact resource identifier returned by this operation, never a title, unrelated row ID or URL. |
operation |
Yes | one of: tierPrice, tierLink, tierUnlink, metadata, salesTerms, delete (by kind) |
operation.eventPolicyTierPriceId |
Yes | When operation.kind is tierPrice: string; min length 1; max length 160; pattern ^[A-Za-z0-9_-]+$; Exact resource identifier returned by this operation, never a title, unrelated row ID or URL. |
operation.payment |
Yes | When operation.kind is tierPrice: one of: free, paid (by kind) |
operation.payment.amount |
Yes | When operation.kind is tierPrice and operation.payment.kind is paid: string; pattern ^(0|[1-9]\d{0,9})(\.\d{1,2})?$ |
operation.tierLabelId |
Yes | When operation.kind is tierLink: string; min length 1; max length 160; pattern ^[A-Za-z0-9_-]+$; Exact resource identifier returned by this operation, never a title, unrelated row ID or URL. |
operation.payment |
Yes | When operation.kind is tierLink: one of: free, paid (by kind) |
operation.payment.amount |
Yes | When operation.kind is tierLink and operation.payment.kind is paid: string; pattern ^(0|[1-9]\d{0,9})(\.\d{1,2})?$ |
operation.eventPolicyTierPriceIds |
Yes | When operation.kind is tierUnlink: array; items: string; min length 1; max length 160; pattern ^[A-Za-z0-9_-]+$; Exact resource identifier returned by this operation, never a title, unrelated row ID or URL.; min items 1; max items 100 |
operation.confirmation |
Yes | When operation.kind is tierUnlink: UNLINK_TIER_PRICES |
operation.settings |
Yes | When operation.kind is metadata: object |
operation.settings.title |
No | When operation.kind is metadata: string; min length 3; max length 50; pattern ^[^\r\n]*$ |
operation.settings.presentationalTitle |
No | When operation.kind is metadata: string; min length 3; max length 50; pattern ^[^\r\n]*$ |
operation.settings.labelIds |
No | When operation.kind is metadata: array; items: string; min length 1; max length 160; pattern ^[A-Za-z0-9_-]+$; Exact resource identifier returned by this operation, never a title, unrelated row ID or URL.; max items 10 |
operation.settings |
Yes | When operation.kind is salesTerms: object |
operation.settings.languages |
No | When operation.kind is salesTerms: array; items: ko, en, ja, zh-cn, zh-tw, fr, es, de, ru, pt, it, ar, hi, bn, vi, id, th, tr, fa, pl, others; min items 1; max items 21 or null |
operation.settings.codes |
No | When operation.kind is salesTerms: array; items: string; pattern ^[a-z0-9]{2,20}$; min items 1; max items 10 or null |
operation.settings.purchaseUnit |
No | When operation.kind is salesTerms: integer; minimum 1; maximum 20 or null |
operation.settings.minimumQuantity |
No | When operation.kind is salesTerms: integer; minimum 2; maximum 100 or null |
operation.policyIds |
Yes | When operation.kind is delete: array; items: string; min length 1; max length 160; pattern ^[A-Za-z0-9_-]+$; Exact resource identifier returned by this operation, never a title, unrelated row ID or URL.; min items 1; max items 100 |
operation.confirmation |
Yes | When operation.kind is delete: DELETE_DISCOUNT_POLICIES |
requestId |
Yes | string; pattern ^[A-Za-z0-9_-]{8,80}$; Reuse this requestId and the exact original input after interruption. Never change requestId to retry an uncertain write. |
expectedRevision |
Yes | string; pattern ^[a-f0-9]{64}$; Exact revision returned by the matching read for this channel, installed pApp and resource. |
dryRun |
No | boolean; default false |
Business IDs use 1–160 ASCII letters, digits, underscores or hyphens. Objects are strict; callers cannot supply raw transport forms, currency, credentials, origin, headers, consumer, installation or a URL. Current channel, credential version, actor, installation, consumer, currency and permission must remain bound. OWNER or a permitted MANAGE_P_APPS delegate can configure the exact E00003 installation; content-only delegates and account scope cannot.
Changing an operation or target requires fresh exact preparation. Preserve an uncertain task's saved input instead of replacing its revision or requestId. A plain inspection revision cannot prepare a different change.
Plan examples
These complete inputs are dry runs with illustrative IDs and a placeholder revision. Use the revision returned for the identical getter operation. The price example assumes KRW and a matching base price of at least 20,000. For a new authorized change, inspect the plan, then repeat the same fields with dryRun:false. This sequence does not permit replaying an uncertain sent operation.
Change the management name and clear policy label links, preserving the public name:
{
"channelCode": "L2NoYW5uZWxzLzk4",
"pAppCode": "E00003",
"eventId": "event-example",
"policyId": "discount-example",
"operation": {
"kind": "metadata",
"settings": {
"title": "Early booking",
"labelIds": []
}
},
"requestId": "discount-metadata-example-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true
}
Set language, unit and minimum-quantity conditions while disabling code restriction:
{
"channelCode": "L2NoYW5uZWxzLzk4",
"pAppCode": "E00003",
"eventId": "event-example",
"policyId": "discount-example",
"operation": {
"kind": "salesTerms",
"settings": {
"languages": [
"en",
"ko"
],
"codes": null,
"purchaseUnit": 2,
"minimumQuantity": 4
}
},
"requestId": "discount-terms-example-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true
}
Change an existing override amount, preserving its tier assignment:
{
"channelCode": "L2NoYW5uZWxzLzk4",
"pAppCode": "E00003",
"eventId": "event-example",
"policyId": "discount-example",
"operation": {
"kind": "tierPrice",
"eventPolicyTierPriceId": "override-example",
"payment": {
"kind": "paid",
"amount": "20000"
}
},
"requestId": "discount-price-example-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true
}
Link an existing global tier label at a free override price:
{
"channelCode": "L2NoYW5uZWxzLzk4",
"pAppCode": "E00003",
"eventId": "event-example",
"policyId": "discount-example",
"operation": {
"kind": "tierLink",
"tierLabelId": "tier-label-example",
"payment": {
"kind": "free"
}
},
"requestId": "discount-link-example-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true
}
Remove one active override link:
{
"channelCode": "L2NoYW5uZWxzLzk4",
"pAppCode": "E00003",
"eventId": "event-example",
"policyId": "discount-example",
"operation": {
"kind": "tierUnlink",
"eventPolicyTierPriceIds": [
"override-example"
],
"confirmation": "UNLINK_TIER_PRICES"
},
"requestId": "discount-unlink-example-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true
}
Effects and preservation
This section describes nondelete settings. Selected-policy deletion has its own complete-inventory proof and internal task type.
All writes use BOOK_AND_TICKET_V3 on the selected API origin. Metadata sends one PATCH-form POST to /seller/api/v3/events/{eventId}/event-policies/dispatch with exactly one target policy, both final names and the complete final management-label selection. Omitted settings are filled from verified current data. labelIds:[] clears this policy's links without deleting global labels.
Sales terms use the same single-policy POST with the complete merged salesTerms object. Omitted and unknown conditions are preserved. Null languages/codes become the source wildcard array; null purchaseUnit/minimumQuantity become -1, as does purchaseUnit:1. Caller wildcard strings and empty arrays are invalid. The four settings map respectively to UX_BTS_USE_DISCOUNT_POLICY_BY_LOCALE_LANGUAGE, UX_BTS_SHOW_DISCOUNT_POLICY_BY_CODE, UX_BTS_ENABLE_PURCHASE_DISCOUNT_POLICY_BY_QUANTITY_UNIT and UX_BTS_ENABLE_PURCHASE_DISCOUNT_POLICY_BY_QUANTITY_MINIMUM. Codes are source display/purchase conditions, not coupon issuance. Metadata and sales-term payloads cannot be mixed or used for bulk policy changes.
Tier operations POST to /seller/api/v3/events/{eventId}/event-policies/{policyId}/tier-prices/dispatch: PATCH one override, CREATE one link, or DELETE selected overrides. Free means amount "0"; paid values obey currency/channel limits and cannot exceed a matching base tier price. The inherited-price projection must be present and agree with the base policy for price edits/linking. No percentage rule or automatic recalculation is provided. Base prices and global tier labels are preserved.
Linking requires fewer than 500 override rows and no existing row for the selected global label, including deleted rows. Completion needs exactly one valid receipt with a new numeric ID and eventPolicyTierPriceId, matching consumer, label, amount/currency, active status and optional parent IDs. Its IDs cannot collide with existing override or base-price IDs. The returned business ID must be durably stored before readback and is exposed as createdTierPriceId; label/title matches, counts and list differences cannot substitute for it.
Unlinking accepts 1–100 distinct active override IDs. Readback accepts each selected row's absence or DELETED status with a valid deletion timestamp and otherwise unchanged business fields. Partial removal never triggers another delete. It does not delete base prices or global labels, nor prove what a customer will pay after removal.
Two stable readbacks must match the requested change and preserve other returned business data: referenced default policy and base prices, unselected overrides, event/refund fields, policy identity, other returned policies and operation-specific management/tier catalogs. Generated insertedAt/updatedAt values are ignored and monetary representations normalized. Collateral changes or unstable dependencies stay unconfirmed. Omitted projections, separately loaded translations, schedules and issued tickets are outside that proof. There is no atomic snapshot, backend compare-and-swap or causal attribution.
Results and recovery
| Result | Meaning |
|---|---|
| COMMERCE_OPERATION_PLAN | Dry-run plan with writesPerformed:false. A new dry run creates no journal; request:null and noop:true mean no change. A dry run of an existing task can be historical. |
| COMMERCE_OPERATION_COMPLETE | phase:done after matching readback or a verified no-op. Fresh completion has verification.confirmed:true; atomicSnapshot and causalityVerified remain false. |
| COMMERCE_OPERATION_PENDING | A sent effect is unresolved. Retain taskId, phase, lastCode, saveAcknowledged and journalPersistence. Pending is not a background retry promise. |
| COMMERCE_JOURNAL_PERSISTENCE_FAILED | Persistence is uncertain; do not claim durable completion or change requestId to retry. |
For nondelete settings the existing task type is commerce-ticket-discount-settings, with prepared, sent and done phases. writesPerformed describes the stored task: false for no-op/prepared, true after acknowledgement and null for an unacknowledged sent effect. It does not count writes performed by the current status/resume call. Empty metadata/terms settings and equal existing values make no business write.
For nondelete settings use task_status for saved historical progress and task_resume to observe sent work. Shared-host resume never dispatches a business write. Prepared work requires identical original apply after revalidation. Once sent, neither resume nor identical apply dispatches another save. REQUEST_ID_CONFLICT means the supplied fields differ from the saved request; keep the original fields. Resolve pre-dispatch COMMERCE_CONFLICT with current state instead of bypassing context checks.
Tier-link recovery needs the durable exact new-price receipt. Missing identity stays pending with lastCode:COMMERCE_CREATE_UNKNOWN; it cannot be adopted by matching label or list difference. Other operations edit known policy/override identities, so matching exact-policy observation may resolve a lost acknowledgement without proving causality. Partial names/conditions/removals and collateral changes remain pending. Completed apply replay, status and completed resume are historical, with current verification false and no current verified link; use a fresh getter for current values.
There is no automatic rollback, relink or original-ID restoration. Resolve uncertainty first.
Console destination
Use the single labeled URL returned by the host, with its configured origin and exact channel. These paths describe the contract; do not reconstruct a destination from an unverified result.
| Current view or operation | Path below /channels/{channelCode}/p-apps/E00003/ | Destination |
|---|---|---|
| Valid list page | events/{eventId}/discount-policies?page={page}&limit=100 | Exact requested list page; no current link for an out-of-range page. |
| Plain selected-policy read or metadata | events/{eventId}/discount-policies?page=1&limit=100 | Surviving parent list. The editor is a modal: select the same observed policy there. |
| delete | events/{eventId}/discount-policies?page=1&limit=100 | Surviving parent event discount list. No selected deleted-detail URL. |
| salesTerms | events/{eventId}/discount-policies/{policyId}/terms-setting | Exact policy conditions screen. |
| tierPrice, tierLink or tierUnlink | events/{eventId}/discount-policies/{policyId}/tiered-pricing-settings | Exact override pricing screen. |
Screen ID is channel.ticket-discount-settings. The host preserves only its source-derived view hint and validates event/default/discount relationships, request target and result identity before returning navigation. Metadata/plain reads expose consoleDestination:parent and a navigationReason; plans, no-ops and fresh verified completions retain their operation's destination. Pending, errors and historical results have no current verified link.
Limits
Policy creation/copy, translations, global-label edits, coupon generation/issuance, payment/refund/settlement execution and automatic restoration are outside this method. Configuration completion does not prove customer discount evaluation, current schedules, issued tickets or visitor rights. See the discount workflow for the preparation sequence and separate verification gates.
Delete selected discount policies
After preparing view.kind delete, send the unchanged channel/event and operation:{kind:"delete",policyIds:["observed-discount"],confirmation:"DELETE_DISCOUNT_POLICIES"}, the preparation revision as expectedRevision, and one stable requestId. Omit root policyId. dryRun previews without saving.
One DELETE dispatch removes only the selected active discount policies. Completion requires their absence from the complete active inventory, preserving the default, unselected policies, event and refund data. Selected embedded projections may disappear or become valid DELETED records; collateral changes remain pending. All discounts may be selected when the 1–100 selection limit is satisfied, but the default cannot. A new preparation rejects an already absent selection; it is not a deletion no-op. No automatic deletion reversal is available. This does not prove effects on existing schedules, issued tickets or buyer discounts.
Use the returned taskType commerce-ticket-policy-lifecycle with task_status and task_resume. The same canonical store is used by apply and recovery. Sent uncertainty is observed without another dispatch; prepared work requires the identical original apply. Historical status has no current verified link. Fresh preparation/completion returns the surviving event discount list at page 1, limit 100.
Deletion uses one POST to /seller/api/v3/events/{eventId}/event-policies/dispatch with DELETE and the exact selected policyIds as targetIds. The shared host routes this existing public method to the canonical commerce-ticket-policy-lifecycle instance and store also used by status/resume. Do not guess a task type from the public method name or use commerce-ticket-discount-settings for its deletion task. A separate public lifecycle method pair is not registered.
Readback must prove selected policies absent from the complete active inventory. If selected embedded event-policy projections remain, each must be DELETED with a valid deletion timestamp and otherwise unchanged business fields; disappearance is also allowed. The default, unselected policies and all other returned event/refund fields must be preserved. Partial removal, drift, failed/incomplete inventory reads and collateral changes remain pending. This proves active-inventory removal, not physical erasure. It differs from deleteSchedule, which needs an explicit DELETED schedule detail rather than a 404.
A fresh completion has noop:false and current confirmation after stable readback. Prepared recovery requires identical original apply through this public method; sent recovery only observes and never sends a second DELETE. Matching inventory observation may resolve a lost acknowledgement without proving causality. Completed replay/status/resume are historical with no current verified destination. Do not replace an uncertain task/request ID. There is no automatic recreation or restoration of deleted policy IDs.
Fresh preparation, dry-run and confirmed completion return channel.ticket-discount-settings with consoleDestination:parent and the surviving event discount list at page 1, limit 100; never a deleted policy detail. Historical/pending/error responses have no current verified link.
Complete deletion dry run; omit root policyId and copy the exact deletion-preparation revision:
{
"channelCode": "L2NoYW5uZWxzLzk4",
"pAppCode": "E00003",
"eventId": "event-example",
"operation": {
"kind": "delete",
"policyIds": [
"discount-example"
],
"confirmation": "DELETE_DISCOUNT_POLICIES"
},
"requestId": "ticket-discount-delete-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true
}