Read and change voucher applications through GraphQL
- Availability
- Beta
- Last verified
- Last verified Sep 9, 2026
Use the voucher-application contract on /graphql when integrating target selection and removal for an order. Application IDs identify individual acceptances; voucher numbers are input and display values, not durable application identities.
Before you start
Use the correct account header and either an authenticated staff user with order-update permission or the order's valid access token. Plain order IDs do not grant anonymous access. Every application must belong to the specified order and account.
Read the accepted target
query VoucherApplications($id: IdOrNumberOrToken!) {
request(id: $id) {
id
voucherApplications {
id
voucherId
voucherNumberSnapshot
voucherType
voucherConfigurationId
voucherConfigurationRevision
acceptedAt
position
scopeKind
priceTransferPolicy
state
invalidationReason
productId
product {
id
name
}
selectedRequestItemId
selectedPricingId
replacesApplicationId
allocation {
status
unallocatableReason
invalidationReason
allocatedAmount
targetPricingId
}
actions {
remove {
available
reason
}
reselect {
available
reason
}
}
}
}
}Supply {"id":"<order access token>"} as variables. The list includes retained history, ordered by immutable position. voucherNumberSnapshot, voucher type, configuration identity/revision and accepted target describe the acceptance; later configuration changes do not rewrite them. product provides current display data for that accepted product ID. Nullable item/pricing references may disappear when the referenced cart row is deleted.
state is ACTIVE, INVALIDATED, or REMOVED. Orders may be extended indefinitely; there is no closed application state. A canceled order retains history but cannot use its applications.
Interpret the preview
allocation is a read-only preview using the local voucher ledger and current cart in application order, regardless of voucher type. It is not a balance reservation, remote balance confirmation, or permission to finalize checkout. Keep using the existing checkout flow and its authoritative validation.
allocatedAmount is the positive deduction as a Money value: {"amount":400,"currency":"EUR"} means €4.00. It is zero for an unallocatable application and null for invalid acceptance evidence. targetPricingId identifies the cart pricing used by the preview, when allocated to a product. It does not change the accepted target. Preview deductions may differ from the legacy checkout calculation; do not substitute them for the payable amount or show them as confirmed payments.
The preview is null for removed applications and canceled orders. Otherwise:
| Status | Meaning and client action |
|---|---|
ALLOCATED | Eligible value is present in this preview. Do not treat this as completed redemption. |
UNALLOCATABLE | Keep the application active. Use unallocatableReason to explain the temporary condition. |
INVALIDATED | Use invalidationReason; do not silently retry or infer a replacement from the current voucher configuration. |
Unallocatable reasons are BALANCE_UNAVAILABLE, ZERO_BALANCE, NO_ELIGIBLE_PRODUCT, NO_ELIGIBLE_VALUE, and CART_VALUE_EXHAUSTED. Missing value or balance does not invalidate acceptance. When balance is unavailable, explain that it cannot currently be confirmed and must be checked before checkout. A missing selected product can be resolved by changing the product or removing the voucher before financial use.
Invalidation reasons are TARGET_RESELECTED, LEGACY_VOUCHER_MISSING, LEGACY_SCOPE_AMBIGUOUS, SNAPSHOT_UNSUPPORTED, and SNAPSHOT_CORRUPT. Show retained replacements for TARGET_RESELECTED; route invalid legacy or snapshot evidence to staff reconciliation.
Check available actions
Use actions.remove and actions.reselect to decide whether to offer an action. Each returns available and a nullable typed reason. Guests using the order access token and authorized staff use the same allocation and lifecycle rules. Re-selection applies only to GOODS vouchers; a VALUE application does not have a product target to change.
Availability is advisory, not authorization or a reservation. The mutation checks permissions and lifecycle again under lock, and validates the supplied target, legacy projection and pricing ownership. An available re-selection does not promise that every product is a supported target. Removed or invalidated history offers no new action; an identical removal retry can still succeed idempotently.
These actions do not stop future funding or refund an earlier redemption. Do not present removal or target changes as substitutes for those operations. Checking the preview or action availability does not dispatch a debit or confirm a pending payment.
Remove an application
mutation RemoveVoucherApplication($input: RequestRemoveVoucherApplicationInput!) {
requestRemoveVoucherApplication(input: $input) {
application {
id
state
}
request {
id
voucherApplications {
id
state
}
}
errorCodes
errors {
key
message
}
}
}Variables: {"input":{"id":"<order access token>","applicationId":"<application UUID>"}}.
Success returns the retained record with state: REMOVED, removes its legacy cart entry and releases its pricing ownership atomically. Repeating removal of the same removed record succeeds without another change. Removing invalidated history is an invalid state transition.
Reselect a GOODS target
For an unambiguous existing cart target, supply its eligible product ID. To create a new voucher-linked target item, also supply requestItem using the existing RequestItemInput shape. Then submit:
mutation ReselectVoucherApplication($input: RequestReselectVoucherApplicationInput!) {
requestReselectVoucherApplication(input: $input) {
application {
id
productId
position
state
replacesApplicationId
}
request {
id
voucherApplications {
id
state
position
allocation {
status
allocatedAmount
targetPricingId
}
}
}
errorCodes
errors {
key
message
}
}
}Variables: {"input":{"id":"<order access token>","applicationId":"<old application UUID>","productId":"<eligible product UUID>","idempotencyKey":"<unique command key>"}}.
For a new target item, add "requestItem":{"offerableId":"<eligible product UUID>","offerableType":"PRODUCT","pricings":[{"priceOriginId":"<eligible product UUID>","priceOriginType":"PRODUCT","quantity":1}]} inside input. Its product must match productId. This creates a cart item; it does not relabel or consume an existing item. Do not use it to silently add duplicate quantities. An ambiguous existing target that cannot be represented by the legacy calculator returns INVALID_TARGET without changing the order.
Re-selection is a new acceptance of the same voucher identity using its current configuration. The old record becomes INVALIDATED with TARGET_RESELECTED; the replacement is appended at the end of priority and links back through replacesApplicationId. Cart changes, acceptance and pricing ownership commit together or roll back together.
When re-selection moves a voucher-adjusted price to another existing item, the previous item returns to its ordinary configured price. The voucher-adjusted price belongs only to the new target. Financially locked items must not be repriced; if ordinary pricing cannot be determined safely, the command is rejected without changing the cart.
Select the refreshed request.voucherApplications.allocation in the mutation response for every voucher, not just the replacement. Moving the replacement to the end can increase another voucher's deduction and reduce its own. Never imply that changing the target preserves priority.
Reuse the same key, predecessor ID, product ID and complete item input when retrying the same command. The server returns the persisted replacement without applying it again, even if its lifecycle has subsequently changed. Reusing a key for another command returns IDEMPOTENCY_CONFLICT. Keys are unique within the account; use a new key for a genuinely new selection.
Handle rejected commands
Use errorCodes for program logic and errors for field details. FINANCIAL_USE_STARTED requires cancellation, refund or reconciliation—not removal or re-selection. Pending and ambiguous financial operations also block these commands. REQUEST_NOT_EDITABLE requires an editable order; INVALID_STATE_TRANSITION means the requested lifecycle change is not allowed.
FEATURE_DISABLED means these writes are unavailable for the account. APPLICATION_NOT_FOUND does not reveal records from another order or account. INVALID_TARGET requires an eligible existing cart product. PROJECTION_MISMATCH and INVALID_APPLICATION require staff investigation; do not repair history automatically. ACTIVE_APPLICATION_EXISTS indicates an already active acceptance.