# Error codes

832 error codes across 56 modules, generated from the platform source rather than maintained by hand.

A processor response code isn't an API error code. An error code means the request was refused before or instead of being processed, and the error reference documents it. A processor response code means the request reached a processor and the processor answered, which covers an approval as much as a decline. [See the processor response-code reference.](https://devportal.demo.winkpg.io/docs/processor-response-codes.md)

## Error response shapes

Every failed request returns the same envelope: a single error object. Which of its fields are populated depends on why the request failed, so these are the four shapes a caller has to handle. The bodies are illustrative and carry no real data.

### 409 Business rule refused the request

The request was understood and rejected by a platform rule. This is the shape that carries a code from the catalog above, so branch on error.code rather than on the message text. The HTTP status is the one listed against the code.

```json
{
  "error": {
    "code": "Transactions:OperationNotAllowed",
    "message": "The operation 'Void' is not allowed on a Sale transaction with result 'Approved' and settlement state 'Settled'.",
    "details": null,
    "data": {
      "correlationId": "0123456789abcdef0123456789abcdef"
    },
    "validationErrors": null
  }
}
```

### 400 Request failed validation

One or more fields were rejected before any rule ran. error.code is null on the envelope and the causes are in validationErrors, one entry per failure. An entry carries code when the validator declared a publishable one, and memberPaths when the full path says more than members does (an element inside a collection, for example). Both keys are omitted when they would add nothing.

```json
{
  "error": {
    "code": null,
    "message": "Your request is not valid!",
    "details": "The following errors were detected during validation.",
    "data": {
      "correlationId": "0123456789abcdef0123456789abcdef"
    },
    "validationErrors": [
      {
        "message": "'Amount' must be greater than 0.",
        "members": ["amount"]
      },
      {
        "message": "The card has expired.",
        "members": ["expirationDate"],
        "code": "CardData:CardExpired"
      },
      {
        "message": "'Value' must not be empty.",
        "members": ["value"],
        "memberPaths": ["customFields[2].value"]
      }
    ]
  }
}
```

### 404 Resource not found

The id in the path names no record the caller can see. Most resources answer with a null error.code and a message naming the resource type and the id; a resource that publishes a not-found code in the catalog above carries it in error.code instead, so check for one before matching on the message.

```json
{
  "error": {
    "code": null,
    "message": "There is no entity Transaction with id = 7d1f3a52-0c4e-4b8a-9e21-5a6b7c8d9e00!",
    "details": null,
    "data": {
      "correlationId": "0123456789abcdef0123456789abcdef"
    },
    "validationErrors": null
  }
}
```

### 403 Caller is not authorized

The credential was accepted and does not carry what the operation requires. The code is the framework's, not a platform code, so it is worth matching on explicitly. Each operation page lists what it requires.

```json
{
  "error": {
    "code": "Volo.Authorization:010001",
    "message": "Authorization failed! Given policy has not granted.",
    "details": null,
    "data": {
      "correlationId": "0123456789abcdef0123456789abcdef"
    },
    "validationErrors": null
  }
}
```

### 500 Unexpected server fault

Something failed that no rule anticipated. There is no code to branch on and the message is deliberately generic: the diagnostic detail stays in the platform's logs rather than going to the caller. Quote error.data.correlationId (the same value as the X-Correlation-Id response header) when you contact support: it's what finds the failure in those logs. Retrying an unsafe operation after this risks a duplicate, so reconcile before you retry.

```json
{
  "error": {
    "code": null,
    "message": "An internal error occurred during your request!",
    "details": null,
    "data": {
      "correlationId": "0123456789abcdef0123456789abcdef"
    },
    "validationErrors": null
  }
}
```

## Codes by module

error.code carries one of these values, so a caller can branch on the cause without parsing message text. The status column is what this instance returns when the code is raised. A code with no message defined is listed rather than hidden: it still reaches a caller over the wire.

### Account

| Code | Status | Message |
| --- | --- | --- |
| `AccountPro:0001` | 403 | You must upload an image! |

### Account Updater

| Code | Status | Message |
| --- | --- | --- |
| `AccountUpdater:MerchantIdRequired` | 400 | A merchant is required for account updater queries. A submission query omitted the merchant. Submissions are partitioned by MerchantId, so the query is refused rather than run as a cross-partition scan. |
| `AccountUpdater:RunsAreReadOnly` | 403 | Account updater runs are read-only and cannot be created, changed, or deleted. A write was attempted against a run. Runs are read-only audit records of what the provider did, so create, update and delete are all refused. |
| `AccountUpdater:SubmissionsAreReadOnly` | 403 | Account updater submissions are read-only and cannot be created, changed, or deleted. A write was attempted against a submission. Submissions are read-only audit records, so create, update and delete are all refused. |
| `AccountUpdater:UseContTokenPaging` | 400 | This list must be paged with a continuation token. Use the continuation list endpoint. The caller used the skip/take GetListAsync overload. Runs and submissions are Cosmos DB-backed, so listing them goes through GetContinuationListAsync instead. |

### Accounting

| Code | Status | Message |
| --- | --- | --- |
| `Accounting:ConnectionAlreadyExists` | 409 | An accounting connection for this provider already exists for the merchant. |
| `Accounting:ConnectionNotFound` | 404 | The requested accounting connection was not found. |
| `Accounting:DuplicateProviderKey` | 409 | More than one accounting provider is registered for the same key. |
| `Accounting:InvalidOAuthState` | 400 | The authorization response could not be validated. Please start the connection again. |
| `Accounting:NoActiveConnection` | 409 | No active accounting connection is configured for this merchant. |
| `Accounting:OAuthExchangeFailed` | 429 | Could not complete authorization with the accounting provider. |
| `Accounting:SyncRecordNotRetryable` | 409 | This sync record is not in a state that can be retried. |
| `Accounting:UnknownProvider` | 400 | No accounting provider is registered for the requested key. |

### Announcements

| Code | Status | Message |
| --- | --- | --- |
| `Announcements:AnnouncementNotFound` | 404 | The announcement was not found. |
| `Announcements:BodyRequired` | 400 | The announcement body is required. |
| `Announcements:CannotModifyAnnouncementOutsideAccess` | 404 | You do not have permission to modify this announcement. Cannot modify an announcement you do not have access to. |
| `Announcements:CannotTargetResellerOutsideHierarchy` | 403 | You can only target resellers within your hierarchy. Cannot target a reseller above or outside your hierarchy. |
| `Announcements:HostScopeRequiresNullTenant` | 400 | Host-level announcements cannot have a tenant specified. Host scope requires null TenantId. |
| `Announcements:InsufficientScopeAccess` | 403 | You do not have access to the specified scope. User does not have access to the specified reseller or merchant scope. |
| `Announcements:InvalidScopeConfiguration` | 400 | Invalid scope configuration. Please verify the reseller or merchant selection. Invalid scope configuration. Check ResellerId/MerchantId/ResellerPathPrefix based on scope. |
| `Announcements:MerchantScopeRequiresMerchantId` | 400 | Merchant-scoped announcements require a merchant to be selected. Merchant scope requires MerchantId. |
| `Announcements:ResellerScopeRequiresResellerId` | 400 | Reseller-scoped announcements require a reseller to be selected. Reseller scope requires ResellerId and ResellerPathPrefix. |
| `Announcements:RolesAudienceRequiresRoleNames` | 400 | At least one role must be selected when targeting by roles. Target audience requires at least one role name when type is Roles. |
| `Announcements:StartDateMustBeBeforeEndDate` | 400 | The start date must be before the end date. |
| `Announcements:TitleRequired` | 400 | The announcement title is required. |
| `Announcements:TooManyRoleNames` | 400 | Too many roles specified in the target audience. Too many role names in the target audience. |
| `Announcements:TooManyUserIds` | 400 | Too many users specified in the target audience. Too many user IDs in the target audience. |
| `Announcements:UserAnnouncementStateNotFound` | 404 | User announcement state not found. The user announcement state was not found. |
| `Announcements:UsersAudienceRequiresUserIds` | 400 | At least one user must be selected when targeting by users. Target audience requires at least one user ID when type is Users. |

### Api Key Authorization

| Code | Status | Message |
| --- | --- | --- |
| `KEY_ENVIRONMENT_MISMATCH` | 401 | The key is in service but belongs to the other environment: a sk_test_ key against a merchant that is now trading live, or a sk_live_ key against one that is not. |
| `KEY_EXPIRED` | 401 | The key was in service but has passed its expiry. Renew it. |
| `KEY_INVALID` | 401 | The presented value did not match any key. A malformed value is answered the same way. |
| `KEY_REVOKED` | 401 | The key has been revoked (or otherwise taken out of service) and will never be accepted again. Mint a new key; the value will not start working again. |
| `KEY_SCOPE_INSUFFICIENT` | 403 | The key authenticated but is scoped, and none of the scopes it holds covers the operation it addressed. Nothing is wrong with the credential itself: grant the key the scope the operation needs, or call it with one that already holds it. |
| `KEY_SUSPENDED` | 401 | The key has been put out of service temporarily and is not being accepted right now. It has not been revoked: an administrator can reinstate the same value, so the remedy is to ask them rather than to mint a replacement. |
| `TRIAL_EXPIRED` | 401 | The key is in service and stamped for the right environment, but the merchant it belongs to is on a time-limited plan whose deadline has passed. |

### Authorization

| Code | Status | Message |
| --- | --- | --- |
| `Volo.Authorization:010001` | 403 | Authorization failed! Given policy has not granted. |
| `Volo.Authorization:010002` | 403 | Authorization failed! Given policy has not granted: {PolicyName} |
| `Volo.Authorization:010003` | 403 | Authorization failed! Given policy has not granted for given resource: {ResourceName} |
| `Volo.Authorization:010004` | 403 | Authorization failed! Given requirement has not granted for given resource: {ResourceName} |
| `Volo.Authorization:010005` | 403 | Authorization failed! Given requirements has not granted for given resource: {ResourceName} |

### Campaigns

| Code | Status | Message |
| --- | --- | --- |
| `Campaigns:CampaignCapReached` | 409 | This campaign has reached its limit and is no longer accepting payments. The campaign has reached a cap that closes it, so the payment was refused before any charge was attempted. |
| `Campaigns:CampaignComparisonTooManyCampaigns` | 400 | You can compare at most {Maximum} campaigns at once, and {Requested} were named. A comparison named more campaigns than the surface will report on at once. |
| `Campaigns:CampaignIsArchived` | 400 | That campaign is archived and cannot take on new payment links. An archived campaign cannot take on new members. |
| `Campaigns:CampaignNotAvailableForMerchant` | 400 | That campaign is not available to this merchant. The referenced campaign is not available to the merchant of the resource trying to join it. |
| `Campaigns:CampaignNotFound` | 404 | That campaign could not be found. The campaign was not found, or is not visible to the caller. |
| `Campaigns:IllegalStatusTransition` | 409 | A campaign cannot move from {CurrentStatus} to {RequestedStatus}. The requested status transition is not legal from the campaign's current status. |
| `Campaigns:LimitMustBePositive` | 400 | A campaign limit or goal must be greater than zero. A cap or goal amount was set to a value that is not positive. |
| `Campaigns:MerchantIdIsImmutable` | 409 | A campaign cannot be moved to a different merchant after it is created. A campaign's owning merchant cannot be changed after it is created. |
| `Campaigns:StartMustBeBeforeEnd` | 400 | The campaign start must fall before the campaign end. The scheduled start must fall before the scheduled end. |

### Cli Access

| Code | Status | Message |
| --- | --- | --- |
| `WinkPG.CliAccess:AuthenticationContextMissing` | 403 | Your sign-in did not satisfy the authentication context this instance requires for restricted operations. Sign in again and complete the extra verification. |
| `WinkPG.CliAccess:CallerNotOperator` | 403 | Only a CLI operator signed in with Microsoft Entra ID can request an access grant. Sign in with winkpg login and try again. |
| `WinkPG.CliAccess:GrantExhausted` | 403 | The access grant has been used as many times as it allows. Request a new one. |
| `WinkPG.CliAccess:GrantExpired` | 403 | The access grant has expired. Request a new one. |
| `WinkPG.CliAccess:GrantIdentityMismatch` | 403 | The access grant was issued to someone else. |
| `WinkPG.CliAccess:GrantMerchantMismatch` | 403 | The access grant was issued for a different merchant. |
| `WinkPG.CliAccess:GrantMissing` | 403 | This operation needs an access grant. Request one and send its id in the X-WinkPG-Cli-Grant header. |
| `WinkPG.CliAccess:GrantNotFound` | 403 | The access grant was not found. Request a new one. |
| `WinkPG.CliAccess:GrantOperationMismatch` | 403 | The access grant was issued for a different operation. |
| `WinkPG.CliAccess:GrantRestrictionMismatch` | 403 | The access grant was issued for a different kind of restricted operation. |
| `WinkPG.CliAccess:GrantRevoked` | 403 | The access grant was revoked. |
| `WinkPG.CliAccess:MerchantUndetermined` | 403 | The merchant this operation acts for could not be determined from the request, so no access grant can be checked against it. |
| `WinkPG.CliAccess:OperatorRoleMissing` | 403 | Your sign-in does not carry the restricted access operator role. Ask an administrator to assign it, then sign in again. |
| `WinkPG.CliAccess:RestrictedCommandsDisabled` | 403 | Restricted CLI operations are turned off on this instance. |
| `WinkPG.CliAccess:SignInNotFresh` | 403 | Restricted operations need a recent interactive sign-in. Sign in again and retry within the allowed window. |

### Core

| Code | Status | Message |
| --- | --- | --- |
| `WinkPG:EntityLocked` | 409 | This {EntityType} is locked and cannot be modified. Unlock it first to make changes. A write was attempted against an entity an administrator has locked. Raised by ThrowIfLocked for any entity implementing IHasEntityLock. |
| `WinkPG:LockReasonTooLong` | 400 | Lock reason cannot exceed {MaxLength} characters. A lock was requested with a reason longer than MaxLockReasonLength characters. |

### Customers

| Code | Status | Message |
| --- | --- | --- |
| `Customers:ContractNotActiveForPause` | 409 | Only an active contract can be paused. This contract is already paused or has been stopped. A pause was requested for a contract that is not active: it is already paused, suspended by the billing engine, or deactivated. Only a running contract can be paused; anything else would relabel a stop that had a different cause. |
| `Customers:ContractNotPaused` | 409 | This contract is not paused. Use the active toggle to reactivate a suspended or deactivated contract. A resume was requested for a contract that is not paused. Resume is the inverse of pause, and reactivating a suspended or manually deactivated contract stays the form's active toggle so the operator makes that decision knowingly. |
| `Customers:ContractPaymentTokenNotAccessible` | 404 | The stored payment method on this contract belongs to a merchant this customer cannot be charged under. A contract create or update supplied a stored-payment-method handle that resolves to a token vaulted at a merchant the contract's customer does not belong to, and which the customer's merchant may not charge under the reseller token-sharing rules. Rejected so a caller cannot bind a contract to another merchant's stored credential. |
| `Customers:ContractPaymentTokenUnresolved` | 404 | The stored payment method on this contract could not be found. It may have been removed or invalidated. A contract create or update supplied a stored-payment-method handle (PaymentTokenReference) that does not resolve to a live payment token: the handle is unknown, or the token behind it has been invalidated. Rejected at write time so the caller gets a deterministic error rather than a contract that silently fails on its first billing run. |
| `Customers:ContractPlanInactive` | 409 | The selected plan is no longer accepting new subscriptions. A contract named a plan that is not accepting new subscriptions. An inactive plan keeps pricing the contracts already on it, but nothing new may join it. |
| `Customers:ContractPlanMerchantImmutable` | 400 | A plan cannot be moved to a different merchant. Create it in the destination merchant's catalog instead. A contract plan update named a different owning merchant. The merchant is the plan's Cosmos partition key and cannot change; moving a plan between merchants means creating it in the destination catalog and re-subscribing its contracts. |
| `Customers:ContractPlanNameAlreadyExists` | 409 | A plan with this name already exists for this merchant. Choose a different name. A contract plan create or rename supplied a name already held by another plan under the same merchant. Names are what the contract form's plan picker shows, so two live plans sharing one is how a contract ends up subscribed to the wrong price. |
| `Customers:ContractPlanScheduledPriceNotInFuture` | 400 | A scheduled price change must take effect in the future. Choose a later date, or apply the change immediately. A scheduled contract plan price change named an effective moment that has already passed. A scheduled change that is already due is an immediate change with the wrong label, and the two reach different subscribers, so the caller is asked which one they meant rather than having it guessed for them. |
| `Customers:ContractPlanUnresolved` | 404 | The selected plan could not be found. It may have been deleted, or it belongs to a different merchant. A contract named a plan that could not be read: it does not exist, it belongs to another merchant, or it has been deleted. Rejected at write time so the contract cannot be saved pointing at a price the billing engine will not be able to resolve. |
| `Customers:ContractResumeScheduleExhausted` | 409 | The schedule has no billing date from today on, so there is nothing to resume onto. Edit the schedule first. A resume was requested but the contract's schedule has no occurrence from today on, so there is nothing to resume billing onto. The schedule needs editing first. |
| `Customers:DuplicateCustomerName` | 400 | Customer name must be unique within the same merchant. A customer create or rename supplied a name already held by another customer under the same merchant. Raised as a validation rejection (HTTP 400) against the name member, not as a business exception: the create path has answered the standard validation envelope for as long as the rule has existed and a live integration reads it. |
| `Customers:InvalidParentMerchant` | 403 | The selected parent merchant is not valid for this customer. The customer named a parent merchant that does not exist, is inactive, or is outside the caller's merchant scope. |
| `Customers:ParentMerchantCannotChange` | 403 | A customer cannot be moved to a different merchant. A customer's parent merchant was changed after creation. The owning merchant is fixed for the life of the customer, because the records filed under it are scoped by it. |
| `Customers:RecurringBillingRunNotFound` | 404 | The recurring billing run could not be found. The requested recurring-billing run does not exist, or is outside the caller's scope. |
| `Customers:RecurringBillingTriggerFailed` | 429 | The recurring billing run could not be started. Please try again. Starting the recurring-billing workflow failed. The underlying fault is logged with its exception; the caller gets a single deterministic code rather than orchestrator internals. |
| `Customers:SandboxRecurringBillingCohortTooLarge` | 400 | This merchant has {count} active contracts, and an on-demand run handles at most {maximum} in one request. They will bill on their own schedule. The merchant holds more active contracts than the accelerator will bill in one request. The run is synchronous and every contract in it is a processor round trip, so the cohort is bounded rather than allowed to hold a request open indefinitely; the scheduled run has no such bound. |
| `Customers:SandboxRecurringBillingEvidenceUnknown` | 403 | Running recurring billing on demand is for the sandbox only, and this request did not state which environment it was for. Authenticate with a test API key (sk_test_); a key issued before environments existed has to be rotated first. An accelerated recurring billing run was requested by a caller carrying no environment stamp, or one the platform does not mint: a cookie session, or an API key issued before environments existed. "We cannot tell" has one safe answer on a path that charges a card. |
| `Customers:SandboxRecurringBillingInProgress` | 409 | An on-demand recurring billing run is already going for this merchant. It finishes in the request that started it, so wait for that response rather than retrying this one. An accelerated recurring billing run was requested for a merchant that already has one running. The second caller is refused rather than queued: the first is synchronous and about to answer, and by the time it does there is nothing left for the second to bring forward. |
| `Customers:SandboxRecurringBillingMerchantUnknown` | 403 | This API key is not tied to a merchant, so there are no contracts to bill. Use a key issued for the sandbox merchant whose contracts you want to run. An accelerated recurring billing run was requested by a caller whose credential names no merchant. The run acts on the caller's own merchant and takes no merchant argument, so without the claim there is nothing to bill. Raised by an admin or reseller key, which is not tied to a merchant. |
| `Customers:SandboxRecurringBillingProductionKeyRefused` | 403 | Running recurring billing on demand is for the sandbox only. This request used a live API key, and when a production contract bills is scheduled by the platform. Use a test key (sk_test_) against a sandbox merchant. An accelerated recurring billing run was requested with a key stamped for the live world. Refused: when a production contract bills is a scheduling decision the platform owns, and bringing it forward would charge a real card. |
| `Customers:SandboxRecurringBillingProductionMerchantRefused` | 403 | This test API key belongs to a merchant that is no longer in the sandbox, so recurring billing cannot be run on demand for it. Its contracts bill on their own schedule. An accelerated recurring billing run was requested with a test key whose merchant is no longer in the sandbox. Reported apart from SandboxRecurringBillingProductionKeyRefused because the remedy differs: a key is stamped once at mint time and nothing re-stamps it, so a merchant that has gone live (or been suspended) still authenticates an old test key. |

### Developer Portal

| Code | Status | Message |
| --- | --- | --- |
| `DeveloperPortal:InvitationIssueRateLimited` | 429 | Too many invitations have been sent for this merchant in a short time. Try again later, once the sending limit resets. The merchant's invitation-issue budget for the current window is spent. Retrying later succeeds with no configuration change, which is why the code maps to 429. |

### Entity Templater

| Code | Status | Message |
| --- | --- | --- |
| `EntityTemplates:InvalidTemplateContent` | 409 | This template's saved content could not be read. Save a new template from a current record. The stored template payload could not be deserialized into the target entity. Raised instead of letting a JsonException escape as an unhandled 500. |

### Entity Templates

| Code | Status | Message |
| --- | --- | --- |
| `EntityTemplates:DefaultTemplateEntityTypeNotSupported` | 400 | This kind of record cannot have a default template. The entity type has no default-template support: it is not in the DefaultTemplateEntityTypes catalog, so there is no setting to store a default in. |
| `EntityTemplates:DefaultTemplateNotVisibleToScope` | 400 | This template is not available to the records that would use it, so it cannot be the default here. Choose a template this level owns, one published by a reseller above it, or a published platform template. The template is not one the entities created under the target scope will be able to load (for example another merchant's private template proposed as a reseller default). |
| `EntityTemplates:DefaultTemplateScopeNotAllowed` | 400 | A default template for this kind of record cannot be set at this level. A default for this entity type cannot be stored at the requested scope (a hosted payment page has no platform-wide default). |
| `EntityTemplates:DefaultTemplateTemplaterUnavailable` | 409 | Templates for this kind of record cannot be checked here, so one cannot be set as the default. No templater is registered for the entity type in this host, so the template cannot be validated and therefore cannot be designated a default. Raised instead of storing a pointer nothing can check. |
| `EntityTemplates:DuplicateTemplateName` | 409 | A template with this name already exists here. Choose a different name. Another template with the same name already exists under the same owner (reseller, merchant or platform). A name is unique within its owning rung; the same name under a different owner is allowed. |
| `EntityTemplates:EmptyTemplateContent` | 409 | This template has no saved content, so there is nothing to create from it. The template exists but stores no content, so there is nothing to create from. |
| `EntityTemplates:IncompatibleSchemaVersion` | 409 | This template was saved by an older version and can no longer be applied. Save a new template from a current record. The template was written under a schema version this templater cannot read. |
| `EntityTemplates:MerchantTemplateCannotBePublished` | 400 | A merchant's template is private to that merchant and cannot be published. A merchant-owned template cannot be published: there is nobody below a merchant to publish to. Only a reseller-owned or platform template has an audience. |
| `EntityTemplates:TemplateEntityTypeMismatch` | 400 | This template is for a different kind of record, so it cannot be applied here. The template names a different entity type than the service asked to apply it. Security relevant: it is what stops a template's JSON being deserialized into a foreign entity type. |
| `EntityTemplates:TemplatesUnavailable` | 409 | Templates are not available here. The host serving this call does not compose the EntityTemplates module, so there is no template store to read or write. Raised by NullEntityTemplateCrudAppService rather than letting a caller believe a template was saved. |
| `EntityTemplates:TemplatingDisabled` | 409 | Templates are not switched on for this account yet. Templating is switched off here (the EntityTemplates.Enabled feature). Raised by the consumer templater methods and by every catalogue application service entry point, for every rung, so the call answers with a coded business error rather than a 500 or a silent no-op. No rung bypasses the feature. |
| `EntityTemplates:TemplatingNotSupportedForContracts` | 400 | Templates are not available for contracts. Templating is not implemented for contracts. Raised instead of letting the generated route fall through to an unhandled failure, so the published surface answers with a coded business error rather than a 500. |

### Favorites

| Code | Status | Message |
| --- | --- | --- |
| `Favorites:EntityTypeRequired` | 400 | The favorite entity type is required. |
| `Favorites:FavoriteNotFound` | 404 | The favorite was not found. |
| `Favorites:LabelRequired` | 400 | The favorite label is required. |
| `Favorites:MaxPerUserReached` | 403 | Maximum number of favorites reached. |
| `Favorites:RouteRequired` | 400 | The favorite route is required. |
| `Favorites:UnknownEntityType` | 400 | Unknown favorite entity type. |

### Feature Management

| Code | Status | Message |
| --- | --- | --- |
| `Volo.Abp.FeatureManagement:InvalidFeatureValue` | 403 | {0} feature value is not valid! |

### Features

| Code | Status | Message |
| --- | --- | --- |
| `Volo.Feature:010001` | 403 | Feature is not enabled: {FeatureName} |
| `Volo.Feature:010002` | 403 | Required features are not enabled. All of these features must be enabled: {FeatureNames} |
| `Volo.Feature:010003` | 403 | Required features are not enabled. At least one of these features must be enabled: {FeatureNames} |

### File Management

| Code | Status | Message |
| --- | --- | --- |
| `FileManagement:0001` | 403 | '{DirectoryName}' is not a valid folder name for a folder. |
| `FileManagement:0002` | 403 | '{FileName}' is not a valid file name. |
| `FileManagement:0003` | 403 | Already exists a folder with the name '{DirectoryName}' |
| `FileManagement:0004` | 403 | You cannot move a folder to under to its child folder. |
| `FileManagement:0005` | 403 | Already exists a file with the name '{FileName}' |
| `FileManagement:0006` | 403 | Directory not found! |
| `FileManagement:0007` | 403 | Not enough storage size! Your total storage size is {StorageSize} and remaining {RemainingSize}. |

### Gdpr

| Code | Status | Message |
| --- | --- | --- |
| `Volo.Abp.Gdpr:010001` | 403 | You have previously requested to download personal data. Once the given request time period has passed, you can create a new one. |
| `Volo.Abp.Gdpr:010002` | 403 | Your personal data is still being prepared. You can download it at {GdprDataReadyTime}. |

### Global Features

| Code | Status | Message |
| --- | --- | --- |
| `Volo.GlobalFeature:010001` | 403 | The '{ServiceName}' service needs to enable '{GlobalFeatureName}' feature. |

### Hosted Payment Page

| Code | Status | Message |
| --- | --- | --- |
| `HostedPaymentPage:Ach:WebAuthorizationUnavailable` | 400 | The bank debit authorization could not be recorded, so the payment was not submitted. Please try again. An internet-initiated (WEB) ACH debit submit could not capture the NACHA single-debit authorization attestation: the consumer IP or the rendered authorization text was missing or blank. Fail-closed: the submit is refused rather than taking an unsubstantiated debit that could not be defended on a return or dispute. Distinct from a processor decline; a support agent seeing this code knows the debit never reached the processor. |
| `HostedPaymentPage:HppSession:AmountModeNotApplicable` | 400 | An amount mode does not apply to a card-capture session. A session-create request specified a Suggested or Locked amount mode against a resolved card-capture purpose (SaveCard / SaveCardWithInitialCharge) or a save-card-only session. Amount mode is a chargeable-Payment axis (those flows take a zero-dollar verification or a plan-driven charge, not a base-amount charge), so a specified amount mode is contradictory and rejected. Mirrors the transport-layer validator check for the page-dependent case the validator cannot see. |
| `HostedPaymentPage:HppSession:BlockedField` | 403 | The field '{fieldKey}' cannot be pre-filled for security reasons. A blocked field key (e.g. card_number, cvv) was included in the pre-fill data. |
| `HostedPaymentPage:HppSession:BoundCustomerRejected` | 400 | The customer supplied for this session could not be used. A session-create request supplied a trusted CustomerId that could not be bound: it does not exist, is inactive / soft-deleted, or belongs to a different merchant than the session's HPP configuration. Rejected at session-create time so a session can never be bound to a customer the merchant does not own or that cannot be charged. The reason data field carries "Missing" (not found / soft-deleted / cross-merchant; the partition-scoped lookup does not distinguish these so they share the reason, avoiding a cross-merchant enumeration oracle) or "Inactive". |
| `HostedPaymentPage:HppSession:CampaignInactive` | 409 | This payment link belongs to a campaign that is not currently accepting payments. The link resolves, but the campaign it belongs to is not accepting payments: it is a draft, paused, ended, archived, outside its scheduled window, or no longer resolvable at all. |
| `HostedPaymentPage:HppSession:Cancelled` | 409 | This payment session was cancelled. Session has already been cancelled (by the parent page via the iframe two-way command channel). Terminal status; distinct from Revoked (admin-initiated) for audit/reporting clarity. |
| `HostedPaymentPage:HppSession:CaptureModeNotApplicable` | 400 | Authorize (delayed capture) is only available for a payment session, not for a Save Payment Method session. A session-create request supplied a CaptureMode = Authorize override against a resolved card-capture purpose (SaveCard / SaveCardWithInitialCharge). Capture timing is a Payment-only axis (those purposes set their own transaction shape), so an explicit Authorize override is contradictory and rejected, mirroring the page-save validator. An inherited page capture mode is neutralized to Sale rather than rejected. |
| `HostedPaymentPage:HppSession:CatalogUnavailable` | 409 | The products on this page could not be priced. Check that each product is still active in the catalog. A session was created against a page that sells catalog products, but the catalog could not be read (no lookup is wired in this host, or none of the page's products resolve to an active entry). Fails closed: a link that cannot be priced is not minted. |
| `HostedPaymentPage:HppSession:Completed` | 409 | This payment link has taken all the payments it was set up to accept. A reusable payment link has taken every completion its cap allowed, so it accepts no further payers. Deliberately distinct from HppSessionConsumed: that code says one payer spent a single-use link, this one says a shared link reached the number of payments the merchant capped it at. Both answer 409, and both are terminal. |
| `HostedPaymentPage:HppSession:ConfigInactive` | 409 | The hosted payment page is not currently active. The referenced HPP configuration exists but is not active. |
| `HostedPaymentPage:HppSession:ConfigNotFound` | 404 | The hosted payment page was not found or is not available for the requesting merchant. Verify the page id and that the request was issued under the owning merchant's credentials. The referenced hosted payment page could not be resolved for the caller: it does not exist, or it belongs to another merchant. The two conditions are deliberately indistinguishable on the wire so a caller cannot probe which foreign page ids exist; the server-side session-create diagnostic log carries the distinction for operators. |
| `HostedPaymentPage:HppSession:ConsentCaptureBeforeConsume` | 409 | Consent capture cannot run until the session's transaction has been consumed. Consent capture was attempted against a session that has not yet transitioned to Consumed: the transaction must have been successfully claimed via ConsumeAsync before consent evidence can be linked to it. |
| `HostedPaymentPage:HppSession:ConsentCaptureMismatch` | 400 | Consent capture input does not match the session's recorded state. Capture input identifiers (merchant, transaction, tenant) do not match the referenced session's recorded state. Prevents consent records from being mis-linked across entities if an upstream value is tampered with or swapped. |
| `HostedPaymentPage:HppSession:ConsentCustomerUnresolvable` | 404 | A customer could not be resolved for this session, so the payment method cannot be saved. A consent / tokenize-on-payment session reached capture time without a resolvable customer: the session carried no CustomerId and either no cardholder email was collected on the card-entry surface, or the supplied email could not be used to find an existing customer or mint a new one. The HPP coordinator catches this and either fails the success flow (when consent is required) or skips the tokenize/consent step (when consent is optional) per the per-session IsConsentRequired outcome contract. |
| `HostedPaymentPage:HppSession:ConsentNotRequested` | 409 | This payment session was not configured to capture stored-credential consent. Capture of a stored-credential consent was requested for a session that did not declare RequestedCredentialStorage; this rejects a capture attempt against a vanilla one-off payment session. |
| `HostedPaymentPage:HppSession:ConsentRequired` | 400 | You must authorize future stored-credential charges before this payment can be processed. The session declared RequestedCredentialStorage but the cardholder did not tick the consent box before submitting. Server-side enforcement barrier complementing the client's disable-submit gate. |
| `HostedPaymentPage:HppSession:Consumed` | 409 | This payment session has already been used. The session has already been consumed by a prior transaction. |
| `HostedPaymentPage:HppSession:CreateIdempotencyInProgress` | 409 | A session with this idempotency key is already being created. Retry shortly; the retry returns the original session. A session create arrived with an idempotencyKey whose original create is still running. Answered immediately rather than holding the request open on another caller's result. Retry shortly; the retry replays the original once it completes. |
| `HostedPaymentPage:HppSession:CreateIdempotencyKeyReused` | 409 | This idempotency key was already used for a different session request. Use a new key, or resend the original request unchanged. The idempotencyKey on a session create matched a key this caller already used for a different request. The gateway refuses rather than replaying a session that does not match what was asked for, and nothing is created. Send a fresh key when the request changes. |
| `HostedPaymentPage:HppSession:CreateIdempotencySessionGone` | 409 | This idempotency key can no longer be resolved to a session, so the original response cannot be returned. Use a new idempotency key to open a new session. An idempotencyKey can no longer be resolved to a session, so the original response cannot be returned again. The gateway refuses rather than opening a second session under a key whose first one may already have been paid. Use a new key to open a new session. |
| `HostedPaymentPage:HppSession:CreateIdempotencyStoreUnavailable` | 429 | The idempotency check could not be completed. Please retry the request. The deduplication store behind create idempotency could not be consulted, so the create was refused rather than run unprotected and risk a second payment link. Retry the request. |
| `HostedPaymentPage:HppSession:CurrencyNotSupported` | 400 | This merchant does not accept payments in the requested currency. Omit the currency to use the merchant's own currency, or send that code. A session-create request named a currency the owning merchant does not present in. The platform charges in the merchant's own configured currency and nothing else, so a well-formed but foreign code is refused here rather than accepted and then quietly ignored at charge time. Omit the field to take the merchant's currency, or send exactly that code. |
| `HostedPaymentPage:HppSession:CustomerEmailVisibilityNotAllowed` | 400 | The customer email visibility requested for this session is not allowed. Bind a customerId to the session, or request a visibility the merchant's email rule and this session type permit, or omit it to use the page setting. A session-create request supplied a customerEmailVisibility override the resolved session cannot honour, so the session is refused rather than created with a field the payer could never satisfy. Two causes: the session is an anonymous card-capture session (save-card, save-card-with-initial-charge, or save-card-only with no bound customerId), where the entered email is the only customer-resolution anchor and so may be neither hidden nor optional; or the override contradicts the merchant's Virtual Terminal EmailAddress rule, which requires the email (admitting only Required) or disables it (admitting only Hidden). The remedy is on the request: bind a customerId, or send a visibility the merchant and the session shape allow, or omit the override and inherit the page. |
| `HostedPaymentPage:HppSession:Disabled` | 403 | HPP sessions are not enabled for this tenant. HPP sessions are disabled for this tenant via settings. |
| `HostedPaymentPage:HppSession:Expired` | 409 | This payment link has expired. Create a new payment session to send a fresh link. Session exists and is still Pending, but its ExpiresAt has passed. Distinct from HppSessionNotFound (session document gone, typically TTL-evicted from Cosmos) so admin-side flows like resending a payment link surface a clear "this link is expired, create a new session" message instead of the ambiguous NotFound. |
| `HostedPaymentPage:HppSession:ExpiryOutOfRange` | 400 | The session expiry must be between {minSeconds} and {maxSeconds} seconds. The requested expiry (TTL) is outside the allowed bounds. |
| `HostedPaymentPage:HppSession:FeeAmountModeNotApplicable` | 400 | A tax or shipping amount mode does not apply to a card-capture session. A session-create request declared a taxAmountMode or shippingAmountMode against a resolved card-capture purpose (SaveCard / SaveCardWithInitialCharge) or a save-card-only session. Those flows take no base-amount charge, so there is no tax or shipping line for a mode to govern. Only the inherit value (omitting the property) is accepted there. |
| `HostedPaymentPage:HppSession:FeeAmountModeRequiresPrefill` | 400 | A suggested or locked tax or shipping amount needs a tax or shipping value of zero or more. A session-create request declared a Suggested or Locked taxAmountMode or shippingAmountMode without a parseable, non-negative tax or shipping prefilled field to carry the value. Zero is accepted (no tax, free shipping). |
| `HostedPaymentPage:HppSession:FieldValueInvalid` | 400 | The value for field '{fieldKey}' exceeds the maximum allowed length. A pre-filled field value exceeds the maximum allowed length or is otherwise invalid. |
| `HostedPaymentPage:HppSession:InitialChargeRequiresCardOnlyPage` | 400 | A Save Payment Method with Initial Charge session requires a page that accepts card only. A session-create request resolved to the SaveCardWithInitialCharge purpose (via a page default or a session override) against a page whose payment methods are not card-only. The initial charge is vaulted as a card and the recurring contract bills that card, so a non-card tender (ACH, wallet) would leave the shopper charged with no contract created. Rejected fail-closed at session-create, mirroring the page-save card-only gate for this purpose. |
| `HostedPaymentPage:HppSession:InvalidField` | 400 | The field key '{fieldKey}' is not valid for this hosted payment page. A pre-filled field key is not recognized as a well-known key or merchant custom field. |
| `HostedPaymentPage:HppSession:NotFound` | 404 | The payment session was not found or has expired. The requested session was not found (likely expired via TTL). |
| `HostedPaymentPage:HppSession:ParentOriginNotAllowed` | 403 | The requested parent origin is not one of this page's allowed embedding domains. The supplied ParentOrigin host is not present in the resolved page's AllowedEmbeddingDomains allowlist. Returned by session-create when an integrator requests postMessage emission to an origin the page is not configured to embed in. Prevents merchant-A from creating a session that posts events into merchant-B's parent. |
| `HostedPaymentPage:HppSession:Paused` | 409 | This payment link is paused and is not accepting payments right now. |
| `HostedPaymentPage:HppSession:PaymentLinkBaseUrlNotConfigured` | 403 | Payment links cannot be sent because the application's public URL is not configured. Contact your administrator. The HPP public base URL could not be resolved: the dedicated HostedPaymentPage:PublicBaseUrl key is set to something that is not a valid absolute HTTPS URL, or it is unset and the fallback App:SelfUrl is missing / not a valid absolute HTTPS URL. Returned by payment-link paths (session-create, send-link, QR code) that build outbound URLs server-side from configuration rather than trusting a client-supplied base URL. |
| `HostedPaymentPage:HppSession:ProductSelectionInvalid` | 400 | A product selection is not valid for this page: the product is not offered, a required product was left out, or the quantity is outside its limits. A product selection (a quantity override at session create, or the payer's choices at submit) broke a rule: the product is not on the order, a required product was left out, the page fixed the quantity, or the quantity is outside the product's bounds. |
| `HostedPaymentPage:HppSession:RecurringPlanNotApplicable` | 400 | A recurring plan can only be supplied for a Save Payment Method with Initial Charge session. A session-create request supplied a RecurringPlan override but the session's resolved purpose is not SaveCardWithInitialCharge (a recurring plan is only meaningful on that purpose). Rejected rather than silently ignored so the caller learns the override had no applicable target. |
| `HostedPaymentPage:HppSession:RecurringPlanUnavailable` | 403 | The recurring plan for this payment session is no longer available. A Save-Card-with-Initial-Charge session references a reusable recurring plan that, at resolve time, no longer exists, is inactive, or belongs to another merchant. The session is unusable because the initial charge amount and the contract terms cannot be resolved. Surfaced fail-closed when the public page resolves the session. |
| `HostedPaymentPage:HppSession:ReleaseNotAllowed` | 409 | This payment session can no longer be released. A request to release a prematurely-consumed session was refused because the session was not in the exact state the consume left it in: its transaction had already been dispatched to the orchestrator, it is no longer Consumed (Pending, Revoked, or Cancelled), it completed via a direct vault token, or its recorded transaction id no longer matches the one being released. Fail-closed by design: refusing leaves the session consumed, which is the pre-existing behavior, whereas releasing a session whose charge may have started would let the same link be paid twice. Never surfaced to a payer: the release is best-effort and the caller logs this rather than propagating it. |
| `HostedPaymentPage:HppSession:RequestedCredentialStorageInvalid` | 400 | RequestedCredentialStorage contains undefined flag bits outside the valid mask. RequestedCredentialStorage supplied at session construction contains bits outside the defined StoredCredentialUsage mask. Session-creation input-validation fault: distinct from HppSessionConsentCaptureMismatch, which flags capture-time cross-entity mismatches. |
| `HostedPaymentPage:HppSession:ResellerHostedPageDisabled` | 403 | Hosted payment pages are turned off for this reseller, so a new payment session cannot be started for this page. Turn on the Hosted Payment Page feature for the reseller and try again. A session-create request targeted a page whose owning merchant sits beneath a reseller with the "Hosted Payment Page" master switch (ResellerFeatures.HostedPage) turned off. Fail-closed: a reseller that cannot be resolved is treated as switched off, because a session that cannot prove the switch is on is refused rather than allowed. Complements ResellerHostedPageDisabled, which refuses configuring the page in the first place; this one covers pages that already existed when the switch was turned off. |
| `HostedPaymentPage:HppSession:ReusableNotApplicable` | 400 | This payment link cannot be reused: a link for one named customer, an invoice link, and a card-capture link each accept a single payer. Reuse was requested for a session whose shape cannot support it. A link bound to one customer, an invoice link, and a card-capture link (save-card-only, or one carrying a recurring plan) each describe a single named payer or a single obligation, so a second payer arriving on the same link would either vault a credential against the wrong customer or pay an invoice twice. Refused at create rather than resolved at payment time, so the contradiction is never stored. |
| `HostedPaymentPage:HppSession:Revoked` | 409 | This session has already been revoked. The session has already been revoked. |
| `HostedPaymentPage:HppSession:SaveCardOnlyConflictsWithPagePurpose` | 400 | This hosted payment page saves a card, so a session cannot ask for the save-card flow to be turned off. A session was requested with SaveCardOnly = false against an HPP instance whose PagePurpose is SaveCard. The instance purpose derives the save-card flow, so an explicit contradicting per-session flag is rejected rather than silently ignored. (A null or matching flag is fine: the flow is derived from the instance.) |
| `HostedPaymentPage:HppSession:SaveCardOnlyNotSupportedByProcessor` | 403 | Save Payment Method (no-charge) sessions are not available for this page: none of its offered payment methods can be stored without a charge for this merchant. A session requested the save-card-only (zero-dollar verification, no charge) flow, but the merchant's processor does not advertise SupportsZeroDollarVerification. Fail-closed: the session is rejected at creation rather than offering a no-charge save path the processor cannot fulfil. The save-card-only affordance is also not surfaced in the HPP UI for these merchants; this guards the explicit-API attempt. |
| `HostedPaymentPage:HppSession:SmsNotSupported` | 403 | SMS delivery is not yet supported for payment links. |
| `HostedPaymentPage:Interaction:BeaconInvalid` | 400 | The interaction event could not be recorded. An anonymous interaction beacon was rejected: the payload shape was invalid, the event type was not one the client may emit (only Selected / Started are accepted), or the referenced session / page could not be resolved or was terminal. Returned to the controller, which maps it to a 400 with an empty body so the anonymous caller learns nothing about which check failed. |
| `HostedPaymentPage:LockedFieldChange` | 403 | This page is locked. Its purpose, owning merchant, and active state cannot be changed while it is locked. Unlock the page first to change these. A field the hosted payment page configuration locks was changed. A locked field is fixed by the page's owner and may not be overridden by the request that opens the session. |
| `HostedPaymentPage:PageProductCurrencyMismatch` | 400 | A product on this page is priced in a different currency than the merchant's. A hosted page charges in the merchant's currency. A page named a catalog product priced in a currency other than the merchant's. A hosted page charges in the merchant currency, so a product priced in another one has no price on it. |
| `HostedPaymentPage:PageProductNotFound` | 400 | A product on this page could not be found in the merchant's catalog, or is no longer active. A page named a catalog product that does not exist, is inactive, or belongs to a different biller than the page's merchant. Refused at page save so a page never references a product a payer could not be sold. |
| `HostedPaymentPage:PageProductQuantityInvalid` | 400 | A product's quantity settings on this page fall outside the catalog's limits for that product. Adjust the default, minimum or maximum quantity, or use whole units. A page's quantity rules for a product sit outside the catalog's own: a page floor below the catalog minimum, a page ceiling above the catalog maximum, a default quantity outside the effective bounds, or a fractional quantity on a product sold in whole units. Refused at page save so the quantities a page shows are the quantities it charges for, never a clamped copy. |
| `HostedPaymentPage:ResellerHostedPageDisabled` | 403 | Hosted payment pages are turned off for this reseller, so a page cannot be created for its merchants. Turn on the Hosted Payment Page feature for the reseller and try again. A hosted payment page was created or updated for a merchant whose reseller has the "Hosted Payment Page" master switch (ResellerFeatures.HostedPage) turned off. The switch governs whether merchants beneath a reseller may create and process with hosted payment pages, so page configuration is refused at the application service (the endpoint is reachable over the HTTP API regardless of what the UI renders). Fail-closed on an unresolvable merchant or reseller. |
| `HostedPaymentPage:SaveCardPurposeNotSupportedByProcessor` | 403 | This page cannot be set to save a card: the merchant's processor does not support zero-dollar card verification. An HPP instance was saved with PagePurpose = SaveCard, but the owning merchant's processor does not advertise SupportsZeroDollarVerification. Surfaced at instance-configuration time so the merchant learns the page cannot work before any session is ever created against it; complements the fail-closed session-create gate (HppSessionSaveCardOnlyNotSupportedByProcessor). |
| `HostedPaymentPage:Tax:AmountMismatch` | 409 | The tax on this payment has changed. Review the updated total and pay again. The tax on a submit disagrees with the tax the server calculated for the page and the address. Either the page showed a stale figure or the submit was altered; the consumed link is released so the payer can review the recalculated amount and pay again. |
| `HostedPaymentPage:Tax:CouldNotBeCalculated` | 409 | Tax could not be calculated for this payment, so it was not taken. Check the address and try again. A page whose tax the server calculates could not calculate it for this payment: the page's tax rate has been retired, or the merchant's tax provider could not answer for the address. The payment is refused rather than charged without tax, and the consumed link is released so the payer can try again. |
| `HostedPaymentPage:Tax:ModeNotAvailable` | 409 | This tax setting is not available for the page's merchant. Choose one of the merchant's active tax rates, or set up a tax provider for the merchant first. A page was saved in a tax mode its merchant cannot support: the chosen tax rate is not one of the merchant's active rates, or the merchant has no tax provider for a calculated tax. |
| `HostedPaymentPage:Template:SourceNotSaved` | 409 | Save the page before saving it as a template. Save as Template was asked for a page that has not been created yet. A template is keyed on the page it was saved from, so the page has to exist first. |
| `Hpp:Webhook:BlockedIpRange` | 400 | The webhook host '{Host}' resolves to an address range that is not allowed. The webhook host resolved to a loopback, link-local, or RFC 1918 private address. Every resolved address must be outside the blocked ranges, which is what stops DNS rebinding through a second A record. |
| `Hpp:Webhook:HttpsOnly` | 400 | The webhook URL must use HTTPS. The supplied webhook URL uses a scheme other than HTTPS. |
| `Hpp:Webhook:InvalidUrl` | 400 | The webhook URL is not a valid absolute URL. The supplied webhook URL is not a well-formed absolute URI. |
| `Hpp:Webhook:UnresolvableHost` | 400 | The webhook host '{Host}' could not be resolved. The webhook host did not resolve to any address, so it cannot be checked against the blocked ranges. Fail-closed: an unresolvable host is refused rather than attempted. |

### Identity

| Code | Status | Message |
| --- | --- | --- |
| `Volo.Abp.Identity:010001` | 403 | You can not delete your own account! |
| `Volo.Abp.Identity:010002` | 403 | Can not set more than {MaxUserMembershipCount} organization unit for a user! |
| `Volo.Abp.Identity:010003` | 403 | Can not change password of an externally logged in user! |
| `Volo.Abp.Identity:010004` | 403 | There is already an organization unit with name {0}. Two units with same name can not be created in same level. |
| `Volo.Abp.Identity:010005` | 403 | Static roles can not be renamed. |
| `Volo.Abp.Identity:010006` | 403 | Static roles can not be deleted. |
| `Volo.Abp.Identity:010007` | 403 | You can't change your two factor setting. |
| `Volo.Abp.Identity:010008` | 403 | It's not allowed to change two factor setting. |
| `Volo.Abp.Identity:010009` | 403 | You can not delegate yourself. |
| `Volo.Abp.Identity:010010` | 403 | Invalid external login provider |
| `Volo.Abp.Identity:010011` | 403 | External login provider authenticate failed |
| `Volo.Abp.Identity:010012` | 403 | Local user already exists |
| `Volo.Abp.Identity:010013` | 403 | No user found in the file. |
| `Volo.Abp.Identity:010014` | 403 | Invalid import file format. |
| `Volo.Abp.Identity:010015` | 403 | Reached maximum allowed user count! This tenant is allowed to have a maximum of {MaxUserCount} users. |
| `Volo.Abp.Identity:010021` | 403 | Name exist: '{0}'. |

### Invoicing

| Code | Status | Message |
| --- | --- | --- |
| `Invoicing:Biller:ForeignBillerNotAllowed` | 404 | You cannot act as this biller. Choose a biller you have access to. |
| `Invoicing:CreditNote:AlreadyApplied` | 409 | This credit note has already been applied to an invoice. |
| `Invoicing:CreditNote:ExceedsBalance` | 409 | The credit note amount is greater than the balance due on the invoice. |
| `Invoicing:CreditNote:InvalidInvoiceStatus` | 409 | A credit note cannot be applied to an invoice in its current status. |
| `Invoicing:CreditNote:NotEditable` | 409 | A credit note in {status} status cannot be edited. |
| `Invoicing:CreditNote:NotFound` | 404 | The credit note could not be found. |
| `Invoicing:CustomField:DuplicateKey` | 400 | The custom field {fieldKey} appears more than once in this request. The same key appears more than once in one request. |
| `Invoicing:CustomField:InvalidValue` | 400 | The value for {fieldLabel} is not valid. {detail} A value does not satisfy its definition: not one of the dropdown options, longer than the text limit, failing the text pattern, not a boolean, not a calendar date, or not a number. |
| `Invoicing:CustomField:PatternInvalid` | 400 | The validation pattern configured for {fieldLabel} could not be evaluated. Correct the custom field definition before using it on an invoice. The definition's text pattern could not be evaluated: it does not compile, or matching it exceeded the evaluation timeout. The definition needs correcting, not the value. |
| `Invoicing:CustomField:Required` | 400 | A value is required for {fieldLabel}. An active definition marked required has no value on the invoice. |
| `Invoicing:CustomField:UnknownKey` | 400 | The custom field {fieldKey} is not defined for this biller. A submitted key matches no definition, active or inactive, for the invoice's biller. |
| `Invoicing:CustomFieldDefinition:KeyAlreadyExists` | 409 | A custom field with the key {key} already exists for this biller. Choose a different key. A definition with the same key, compared case-insensitively, already exists for the biller. The stored spelling travels in the exception data so the caller knows which one it collided with. |
| `Invoicing:CustomFieldDefinition:LimitReached` | 409 | This biller already holds the maximum of {maxDefinitions} custom field definitions. Delete one before adding another. The biller already holds MaxCustomFieldDefinitionsPerBiller definitions, active or inactive, so no further definition can be created for it. |
| `Invoicing:Deposit:DueDateAfterBalanceDueDate` | 400 | The deposit due date ({depositDueDate}) cannot be later than the balance due date ({balanceDueDate}). |
| `Invoicing:Deposit:ExceedsTotal` | 400 | The deposit amount ({depositAmount}) must be less than the invoice total ({total}). |
| `Invoicing:Deposit:MustBePositive` | 400 | The deposit amount must be greater than zero. |
| `Invoicing:Deposit:PaymentRequired` | 400 | This invoice requires its deposit of {requiredAmount} to be paid first. The amount supplied was {providedAmount}. |
| `Invoicing:Export:RowLimitExceeded` | 400 | The export exceeds the maximum of {MaxRowCount} rows. Narrow the filters and try again. |
| `Invoicing:Feature:NotEnabled` | 403 | The invoicing feature this request needs is not enabled for your account. |
| `Invoicing:Invoice:AlreadySent` | 409 | This invoice has already been sent. |
| `Invoicing:Invoice:CannotCancelNonDraft` | 409 | Only a draft invoice can be cancelled. |
| `Invoicing:Invoice:CannotEditLocked` | 409 | A locked invoice cannot be edited. Unlock it first, then try again. |
| `Invoicing:Invoice:InvalidStatusTransition` | 409 | An invoice cannot move from {currentStatus} to {targetStatus}. |
| `Invoicing:Invoice:IsLocked` | 409 | This invoice is locked and cannot be changed. |
| `Invoicing:Invoice:NotFound` | 404 | The invoice could not be found. |
| `Invoicing:Invoice:NoteLimitReached` | 409 | This invoice already holds the maximum number of notes. No further note can be added. The invoice already holds MaxNotesPerInvoice internal notes, so no further note can be appended to it. |
| `Invoicing:Invoice:PossibleDuplicate` | 409 | An invoice with the same customer, amount, and date already exists. Confirm this is not a duplicate before sending it. |
| `Invoicing:Limit:MonthlyInvoiceLimitReached` | 403 | The monthly invoice limit for this plan has been reached. |
| `Invoicing:LineItem:CustomPriceNotAllowed` | 400 | The unit price for {productName} must be its catalog price of {limit}. This product does not allow a custom price. A line item's unit price differs from the catalog price of a product that does not allow a custom price. |
| `Invoicing:LineItem:DescriptionRequired` | 400 | Each line item needs a description. |
| `Invoicing:LineItem:FractionalQuantityNotAllowed` | 400 | The quantity for {productName} must be a whole number. This product does not allow fractional quantities. A line item carries a fractional quantity for a product that only sells whole units. |
| `Invoicing:LineItem:InvalidQuantity` | 400 | The line item quantity must be greater than zero. |
| `Invoicing:LineItem:InvalidUnitPrice` | 400 | The line item unit price cannot be negative. |
| `Invoicing:LineItem:MaxExceeded` | 400 | This invoice has reached the maximum number of line items. |
| `Invoicing:LineItem:NotFound` | 404 | The line item could not be found on this invoice. |
| `Invoicing:LineItem:ProductUnavailable` | 400 | The product {productName} is not available for this biller. It may have been removed, deactivated or archived. A line item references a product this biller cannot sell: it does not exist, belongs to another biller, has been deleted, deactivated or archived. |
| `Invoicing:LineItem:QuantityAboveMaximum` | 400 | The quantity for {productName} cannot be more than {limit}. A line item's quantity is above the product's maximum order quantity. |
| `Invoicing:LineItem:QuantityBelowMinimum` | 400 | The quantity for {productName} must be at least {limit}. A line item's quantity is below the product's minimum order quantity. |
| `Invoicing:Number:Conflict` | 409 | A unique invoice number could not be generated after {MaxAttempts} attempts. Please try again. |
| `Invoicing:Number:SequenceNotFound` | 404 | The invoice number sequence for this biller could not be found. |
| `Invoicing:Payment:BelowMinimum` | 400 | The payment amount is below the minimum this invoice accepts. |
| `Invoicing:Payment:ExceedsBalance` | 409 | The payment amount is greater than the balance due on this invoice. |
| `Invoicing:Payment:InvoiceAlreadyPaid` | 409 | This invoice is already paid in full. |
| `Invoicing:Payment:PartialNotAllowed` | 403 | This invoice does not accept partial payments. Pay the full balance due. |
| `Invoicing:PaymentPlan:AlreadyExists` | 409 | This invoice already has a payment plan. |
| `Invoicing:PaymentPlan:AutoCollectRequiresToken` | 400 | Automatic collection requires a stored payment method on the payment plan. |
| `Invoicing:PaymentPlan:InstallmentNotFound` | 404 | The installment could not be found on this payment plan. |
| `Invoicing:PaymentPlan:InstallmentNotPending` | 409 | Installment {sequenceNumber} is {status} and cannot be paid. Only a pending installment can be paid. |
| `Invoicing:PaymentPlan:InstallmentsMustSumToBalance` | 400 | The installment amounts must add up to the invoice balance due. |
| `Invoicing:PaymentPlan:InvalidInstallmentCount` | 400 | A payment plan must have between {min} and {max} installments, but {count} were requested. |
| `Invoicing:PaymentPlan:InvalidInvoiceStatus` | 409 | A payment plan cannot be created for an invoice in {status} status. |
| `Invoicing:PaymentPlan:NoBalance` | 409 | This invoice has no outstanding balance to schedule. |
| `Invoicing:PaymentPlan:NotFound` | 404 | The payment plan could not be found. |
| `Invoicing:Portal:NotAuthenticated` | 403 | Your invoice portal session is no longer valid. Open the invoice link again to continue. |
| `Invoicing:Product:NotFound` | 404 | The product could not be found. |
| `Invoicing:RecurringSchedule:InvalidBiller` | 400 | The selected biller is not valid for this recurring invoice schedule. |
| `Invoicing:RecurringSchedule:InvalidStatusTransition` | 409 | A recurring invoice schedule cannot move from {from} to {to}. |
| `Invoicing:RecurringSchedule:NoLineItems` | 400 | A recurring invoice schedule needs at least one line item. |
| `Invoicing:RecurringSchedule:NoRecurrence` | 400 | A recurring invoice schedule needs a recurrence pattern. |
| `Invoicing:RecurringSchedule:NotFound` | 404 | The recurring invoice schedule could not be found. |
| `Invoicing:Refund:AmountInvalid` | 400 | A refund amount must be greater than zero. |
| `Invoicing:Refund:ExceedsAmountPaid` | 409 | A refund cannot exceed the amount collected against the invoice. |
| `Invoicing:Refund:ExecutorUnavailable` | 409 | Refunds are not available in this environment. |
| `Invoicing:Refund:ManualReversalRequiresReason` | 400 | A reason is required to reverse a manually recorded payment. |
| `Invoicing:Refund:NoRefundablePayments` | 409 | This invoice has no collected payment to refund. |
| `Invoicing:Refund:NotAllowedForStatus` | 409 | This invoice cannot be refunded in its current status. |
| `Invoicing:Refund:PartialFailure` | 409 | Some payments were returned before the refund failed. Review the invoice's payment history before retrying. |
| `Invoicing:Refund:RejectedByGateway` | 409 | The payment platform did not complete the refund. Check the transaction before trying again. |
| `Invoicing:Refund:RequiresMerchantScope` | 409 | This invoice is not billed by a merchant, so its gateway payment cannot be refunded. |
| `Invoicing:Report:RowLimitExceeded` | 400 | The report exceeds the maximum of {MaxRowCount} rows. Narrow the filters and try again. |
| `Invoicing:TaxRate:NotFound` | 404 | The tax rate could not be found. |
| `Invoicing:TaxRateLookup:BillerNotMerchant` | 400 | Only a merchant biller can look up a tax rate from a tax provider. Only a merchant biller can hold a tax provider binding; any other biller type is refused. |
| `Invoicing:TaxRateLookup:NoDestinationAddress` | 409 | No recipient address was supplied and the recipient has no address on file, so there is no destination address to look up a rate for. No destination address: the request carried no recipient address and the recipient has none on file. |
| `Invoicing:TaxRateLookup:NoOriginAddress` | 409 | The merchant has no default shipping origin and no biller address was supplied, so there is no origin address to look up a rate for. No origin address: the merchant has no default shipping origin and the request carried no biller address. |
| `Invoicing:TaxRateLookup:ProviderFailed` | 409 | The tax provider could not return a rate ({errorCodes}). The provider answered with a failure; its normalised error codes travel in the exception data. |
| `Invoicing:TaxRateLookup:ProviderUnavailable` | 409 | No tax provider is available for this merchant. Check the merchant's tax provider binding and that the provider is enabled. The merchant has no enabled binding, the provider's gate is closed, or the provider is not deployed here. |
| `Invoicing:Template:CannotDeleteDefault` | 409 | The default invoice template cannot be deleted. Make another template the default first. |
| `Invoicing:Template:NotFound` | 404 | The invoice template could not be found. |
| `WinkPG.Invoicing:PaymentLinkBaseUrlNotConfigured` | 403 | The invoice payment link could not be built because the hosted payment page base address is not configured. Contact support. A payment link was requested but the deployment has no public base URL configured, so no link can be built that a payer could reach. |
| `WinkPG.Invoicing:PaymentLinkNotAvailable` | 403 | A payment link is not available for this invoice. A payment link was requested for an invoice whose current state carries none: it is not in a payable status, or its hosted payment page is not available. |

### Language Management

| Code | Status | Message |
| --- | --- | --- |
| `Volo.Abp.LanguageManagement:010001` | 403 | Culture name {CultureName} already exists. |

### Merchant Billing

| Code | Status | Message |
| --- | --- | --- |
| `MerchantBilling:BillingRunAlreadyRunning` | 409 | A billing run is already in progress for this reseller. |
| `MerchantBilling:ContractCancelled` | 409 | This recurring billing contract has been cancelled and can no longer be charged. An operation that requires a live contract (e.g. recording a charge dispatch) was attempted against a Cancelled contract. A cancelled contract is terminal: a new recurring agreement must be created rather than reusing the cancelled row. |
| `MerchantBilling:DuplicateBillingRunForPeriod` | 409 | A completed billing run already exists for reseller '{0}' in period '{1}'. |
| `MerchantBilling:DuplicateGroupInPlan` | 400 | SKU group '{0}' already has a rate entry in this price plan. The plan already has a catch-all rate entry for this SKU group. |
| `MerchantBilling:DuplicateSkuInPlan` | 400 | SKU '{0}' already exists in this price plan. |
| `MerchantBilling:InvalidSkuCode` | 400 | SKU code '{0}' is not a valid registered SKU. |
| `MerchantBilling:InvalidSkuGroupCode` | 400 | SKU group '{0}' is not a recognized billing group. The referenced billing-group code is not a registered SKU billing group. |
| `MerchantBilling:MerchantAlreadyHasActivePlan` | 409 | Merchant '{0}' already has an active price plan assignment. |
| `MerchantBilling:PaymentMethodCallbackKindFieldMissing` | 400 | A '{0}' payment method requires field '{1}' on the capture callback. A kind-specific field required by the HPP callback payload was missing (e.g. card brand/exp on a Card kind, or ACH account type on an Ach kind). Raised from the app service after the FluentValidation pass; it defends against bypassed / regressed validation surfacing as a 500 NRE instead of a controlled business error. |
| `MerchantBilling:PaymentMethodCaptureIntentCancelled` | 409 | The capture link was cancelled and can no longer be completed. Send a new link from the merchant's payment-method page. The capture-intent was cancelled (revoked) by an operator before it was consumed. The emailed link's hosted page can no longer be completed: both the token-based consume and the event-driven consume reject a cancelled intent so cancellation cannot be raced by a completion. |
| `MerchantBilling:PaymentMethodCaptureIntentConsumed` | 409 | The capture-intent token has already been used. Restart the capture from the merchant's payment-method page. The capture-intent token was already consumed (single-use). Raised on a replay and on the loser of a concurrent-callback race (the atomic ETag consume picks one winner). |
| `MerchantBilling:PaymentMethodCaptureIntentExpired` | 409 | The capture-intent token has expired. Restart the capture from the merchant's payment-method page. The capture-intent token expired before the callback was submitted. |
| `MerchantBilling:PaymentMethodCaptureIntentInvalid` | 400 | The capture-intent token is not recognized. Restart the capture from the merchant's payment-method page. The capture-intent token did not resolve to any issued intent under the reseller (unknown or tampered token). Distinct from PaymentMethodCaptureIntentConsumed so a genuine replay is told apart from a never-issued token. |
| `MerchantBilling:PaymentMethodCaptureIntentMismatch` | 400 | The capture-intent token does not match this capture request. Restart the capture from the merchant's payment-method page. The capture-intent token resolved, but its bound (merchant / collection merchant / kind) does not match the callback payload, e.g. an operator replaying merchant A's token on a callback addressed to sibling merchant B under the same reseller. The mismatched field is supplied in the exception data under Field. |
| `MerchantBilling:PaymentMethodCaptureIntentRequired` | 400 | The capture callback is missing its capture-intent token. Restart the capture from the merchant's payment-method page. The capture completion did not carry a capture-intent token. The token is issued on InitiateCaptureAsync and is mandatory on CompleteCaptureAsync; a completion without it cannot be proven to correspond to an Initiate invocation. |
| `MerchantBilling:PaymentMethodCaptureLinkNotFound` | 404 | There is no capture link to resend for this merchant. Send a new link first. A resend was requested for a merchant that has no prior send-a-link capture link to resend or reissue. The operator must send a fresh link from the capture controls first. |
| `MerchantBilling:PaymentMethodCaptureNotCompleted` | 409 | The payment capture has not completed yet. Finish entering the payment details on the hosted page, then try again. The operator-UI completion call (CompleteCaptureAsync) resolved an HPP session that has not yet been consumed by a successful tokenization: the cardholder has not finished, or the session was cancelled/revoked/expired. The UI should keep waiting or restart the capture. |
| `MerchantBilling:PaymentMethodCollectionMerchantMismatch` | 400 | The supplied payment-method collection merchant does not match the reseller's configured collection merchant. The supplied CollectionMerchantId on a payment-method create/replace does not match the reseller's configured CollectionMerchantId. Rejected to prevent storing a token vaulted at the wrong collection merchant. |
| `MerchantBilling:PaymentMethodKindNotAllowed` | 403 | The reseller's billing preferences do not permit '{0}' payment methods. The payment-method MerchantBillingPaymentMethodKind is not permitted by the reseller's AllowCardPayments / AllowAchPayments allowlist. |
| `MerchantBilling:PaymentMethodMissingCollectionMerchant` | 400 | Configure a collection merchant on the reseller's billing preferences before capturing a payment method. The reseller has not yet configured a CollectionMerchantId, so there is no collection merchant to vault a payment-method token under. |
| `MerchantBilling:PaymentMethodMissingHpp` | 400 | Configure a card capture Hosted Payment Page on the reseller's billing preferences before capturing a payment method. The reseller has no HppPageId configured, so the operator UI cannot launch the HPP tokenization capture flow. Mirrors the resolver's MissingHpp outcome on the Invoicing path: the recurring path needs the same HPP page because that's where the tokenization actually happens. |
| `MerchantBilling:PaymentMethodNotFound` | 404 | No active stored payment method exists for this merchant. No active payment method exists for the (reseller, merchant) pair. |
| `MerchantBilling:PaymentMethodTokenNotFound` | 404 | The payment token referenced by the capture callback could not be resolved. The HPP capture callback referenced a PaymentTokenId that does not resolve to an existing token (or the token is not active). Rejected so we never persist a billing row pointing at a vault entry that doesn't exist. |
| `MerchantBilling:PaymentMethodTokenVaultMismatch` | 400 | The payment token is not vaulted under the expected collection merchant. The HPP capture callback claimed the token is vaulted at one collection merchant, but the underlying PaymentToken.MerchantId points at a different merchant. Rejected to prevent a recurring charge from being drafted against a processor account that does not actually hold the token. |
| `MerchantBilling:PricePlanInUse` | 409 | Cannot delete price plan '{0}' because it is assigned to one or more merchants. |
| `MerchantBilling:PricePlanNameAlreadyExists` | 409 | A price plan with name '{0}' already exists for this reseller. |
| `MerchantBilling:PricePlanNotFound` | 404 | Price plan not found. |
| `MerchantBilling:PricingStrategyNotAllowedForSku` | 400 | Pricing strategy '{0}' is not allowed for SKU '{1}'. |
| `MerchantBilling:RateEntryTargetAmbiguous` | 400 | A rate entry cannot target both a SKU and a SKU group. A rate entry targets both a SKU code and a SKU group (mutually exclusive). |
| `MerchantBilling:RateEntryTargetRequired` | 400 | Each rate entry must target a SKU or a SKU group. A rate entry targets neither a SKU code nor a SKU group (both empty). |
| `MerchantBilling:ResellerMerchantBillingDisabled` | 403 | Merchant billing is turned off for this reseller, so a price plan cannot be created for it. Turn on the Merchant Billing feature for the reseller and try again. A price plan was created (or cloned) for a reseller whose "Merchant Billing" master switch (ResellerFeatures.MerchantBilling) is turned off. The switch governs the creation of price plans and the generation of invoices for merchants under a reseller, so the create is refused at the application service (the endpoint is reachable over the HTTP API regardless of what the UI renders). Fail-closed on an unresolvable reseller. |
| `MerchantBilling:TiersNotContiguous` | 400 | Pricing tiers must be contiguous and non-overlapping. |
| `MerchantBilling:TiersRequiredForTieredPricing` | 400 | At least one pricing tier is required when using Tiered pricing strategy. |

### Merchants

| Code | Status | Message |
| --- | --- | --- |
| `Merchants:ActiveProcessorProfileRequiresPaymentType` | 409 | An active processor profile must have at least one payment type selected. An active processor profile declares no payment types, so it is active and routes nothing. |
| `Merchants:ApmWebhookRegistrationNotPerMerchant` | 400 | This processor's webhooks are registered once for the platform, so the profile has no webhook URL of its own to rotate. A webhook registration rotation was asked of a processor profile whose provider registers its webhook once for the platform, so the profile has no URL of its own to rotate. |
| `Merchants:CannotDeactivateLastActiveProfileForPaymentType` | 409 | This is the last active profile for its payment type. Deactivating it would leave the merchant unable to process that payment type. The profile is the last active one for its payment type, so deactivating it would leave the merchant unable to process that payment type. |
| `Merchants:CannotDeactivateProcessorWithUnsettledTransactions` | 409 | This processor account cannot be deactivated while it still has unsettled transactions. The processor account still has unsettled transactions, so it cannot be deactivated. |
| `Merchants:CannotDeleteProcessorWithUnsettledTransactions` | 409 | This processor account cannot be deleted while it still has unsettled transactions. The processor account still has unsettled transactions, so it cannot be deleted. |
| `Merchants:CannotPromoteSuspendedMerchant` | 409 | This merchant is suspended, so it cannot be promoted to production. Extend the trial to return it to the sandbox first, then promote it. The promotion action was run against a suspended merchant, which is not a promotable state. |
| `Merchants:CannotUseLoopbackProfileOutsideSandbox` | 409 | A merchant outside the Sandbox environment cannot have an active loopback processor profile. The loopback processor is a simulator and never contacts a card network. The save would leave a merchant outside the Sandbox environment carrying an active loopback simulator processor profile, which would let a real payment be answered by the simulator. |
| `Merchants:CardAcceptancePolicyIssuerCountriesInvalid` | 400 | Card acceptance policy issuer countries are not valid for the selected mode. The allow list and deny list modes need at least one country and the other modes need none, and every code must be a distinct two-letter ISO 3166-1 alpha-2 country, for example US. A card acceptance policy's issuer country list disagrees with the issuer country mode it was sent with, or carries a code that is not a two-letter ISO 3166-1 alpha-2 country. |
| `Merchants:CardAcceptancePolicyRefusesEverything` | 400 | Card acceptance policy refuses both credit and debit cards, which refuses very nearly every card. Accept at least one of credit or debit. Prepaid is an overlay on those two rails, so accepting it does not change this. A card acceptance policy refuses both card rails, so it would refuse very nearly every card. |
| `Merchants:CustomFieldsNotEnabled` | 403 | Custom field configuration is not enabled for this merchant. The merchant attempted to self-manage its custom fields while ConfigurationLimits.AllowCustomFieldConfig is off. |
| `Merchants:NoCurrentMerchant` | 400 | No merchant is associated with the current user. The self-service routes resolve the merchant from the signed-in user, so an API key cannot reach them. Read these settings from the merchant route that takes a merchant id in the path; updating them there needs the merchant update permission. The caller's identity resolves to no merchant, so a merchant-scoped operation cannot run. |
| `Merchants:OrderDataDefaultsNotEnabled` | 403 | Order data defaults are not enabled for this merchant. The merchant attempted to self-manage its order-data defaults while ConfigurationLimits.AllowOrderDataDefaultsConfig is off. |
| `Merchants:PricePlanAssignmentBlockedForCollectionMerchant` | 403 | This merchant is the Merchant Billing collection merchant for its reseller, so it cannot be assigned a Price Plan: a collection merchant cannot bill itself. Clear the Price Plan selection, or choose a different collection merchant on the reseller's billing preferences first. A price plan was assigned to a collection merchant, which bills through its reseller rather than through a plan of its own. |
| `Merchants:PricePlanAssignmentInvalidPlan` | 403 | The selected price plan cannot be assigned: it does not exist, is inactive, or belongs to a different reseller. Refresh the list and choose an active plan in this reseller. A price-plan assignment named a plan that could not be read: it does not exist, it is not accepting new assignments, or it belongs to a different biller. |
| `Merchants:ProcessorRoutingAttestationStale` | 409 | The processor accounts these routing rules send transactions to have changed, so the attestation on record no longer covers them. Confirm again that every processor account these rules route to belongs to the same legal entity as this merchant. A write would leave conditional routing enabled while the attestation on record no longer covers the processor accounts the rules target. |
| `Merchants:ProcessorRoutingTargetProfileUnusable` | 409 | A routing rule must send transactions to one of this merchant's own active processor accounts. A conditional routing rule names a processor account that is not an active processor account on this merchant, so the rule routes nowhere. |
| `Merchants:RecurringOverrideRequiresActivePaymentMethod` | 403 | This merchant cannot be moved to the recurring contract billing path without an active stored payment method. Capture a payment method for the merchant first, then change the billing path. A recurring-billing override was requested for a merchant with no active payment method to bill against. |
| `Merchants:RecurringOverrideRequiresCollectionMerchant` | 403 | This merchant cannot be set to the recurring contract billing path: its reseller has no collection merchant configured. Set a collection merchant on the reseller's billing preferences first. A recurring-billing override was requested for a merchant that is not a collection merchant. |
| `Merchants:ResellerRestrictsMerchantToSandbox` | 409 | The reseller this merchant belongs to is marked as a test reseller, so the merchant stays in the sandbox and cannot be promoted. Turn off Is Test on the reseller first, then promote this merchant on its own. The save would put a merchant into Production or Pending Promotion while the reseller it belongs to is flagged as a test reseller. |
| `Merchants:SettlementTimeOffCadence` | 400 | Settlement Time must fall on a settlement cadence boundary and carry no seconds. Auto settlement runs on a fixed cadence, so a time between cadence marks is never acted on. The supplied auto-settlement time does not fall on a settlement cadence boundary, so the platform would never act on it at the minute it names. |
| `Merchants:TenderDisabledByMerchant` | 403 | This tender is not enabled for this merchant. The merchant has not opted in to the requested tender. |
| `Merchants:TenderDisabledByReseller` | 403 | This tender is not enabled by the reseller. The reseller has not opted in to the requested tender. |
| `Merchants:TenderDisabledByTenant` | 403 | This tender is turned off platform-wide. Tenant-level kill switch is off for the requested tender. |
| `Merchants:TenderNotSupportedByProcessorAccount` | 403 | The selected processor account does not support this tender. The selected processor account does not support the requested tender. |
| `Merchants:TrialExtensionMerchantHasNoTrial` | 400 | This merchant is not on a trial, so there is no trial to extend. A trial extension named a merchant that has no trial: it is on no plan and carries no trial deadline. |
| `Merchants:TrialExtensionNotInFuture` | 400 | The new trial end date must be in the future. Choose a later date and try again. A trial extension named a deadline that is not in the future, so it would not have extended anything. |
| `Merchants:TrialExtensionSuspensionNotFromTrial` | 409 | This merchant was suspended for a reason other than its trial ending, so extending the trial does not lift the suspension. Lift the suspension by changing the merchant's environment first. A trial extension named a suspended merchant whose suspension did not come from its trial running out. |

### Multi Merchant

| Code | Status | Message |
| --- | --- | --- |
| `MultiMerchant:MerchantNotActive` | 409 | Merchant is not active! The named merchant resolved, but is not active. |
| `MultiMerchant:MerchantNotFound` | 404 | Merchant not found! The request named a merchant (by id or name) that does not resolve to a configuration. |

### Notifications

| Code | Status | Message |
| --- | --- | --- |
| `Notifications:ChannelIdsRequired` | 400 | At least one channel is required on a subscription. At least one channel ID is required on a subscription. |
| `Notifications:ChannelInUseBySubscription` | 409 | This channel cannot be deleted while these subscriptions use it: {subscriptionNames}. Channel cannot be deleted because it is referenced by one or more subscriptions. |
| `Notifications:ChannelNameAlreadyExists` | 409 | A channel with this name already exists in this scope. Channel name already exists within the same tenant/scope. |
| `Notifications:ChannelNotFound` | 404 | The channel could not be found. Channel entity not found. |
| `Notifications:ChannelScopeViolation` | 403 | The channel's scope is not compatible with the subscription's scope. Channel scope is incompatible with the referencing subscription's scope. |
| `Notifications:DeliveryNotFound` | 404 | The delivery record could not be found. Delivery record not found. |
| `Notifications:DeliveryNotRetryable` | 409 | This delivery cannot be retried from its current status ({Status}). A manual retry was requested for a delivery whose current status cannot be reset (it never failed, or it already succeeded). Replaces a raw InvalidOperationException, which surfaced to the caller as a 500 rather than a handled error. |
| `Notifications:DestinationConfigUnreadable` | 409 | This destination's configuration could not be read. Correct the configuration before trying again. Destination configuration could not be parsed. |
| `Notifications:DestinationInUseByChannel` | 409 | This destination cannot be deleted while these channels use it: {channelNames}. Destination cannot be deleted because it is referenced by one or more channels. |
| `Notifications:DestinationNameAlreadyExists` | 409 | A destination with this name already exists in this scope. Destination name already exists within the same tenant/scope. |
| `Notifications:DestinationNotFound` | 404 | The destination could not be found. Destination entity not found. |
| `Notifications:DestinationScopeViolation` | 403 | The destination's scope is not compatible with the scope of the record referencing it. Destination scope is incompatible with the referencing entity's scope. |
| `Notifications:EventTypeNotAvailable` | 403 | One or more of the selected event types cannot be delivered, so the subscription would never fire. One or more requested event types cannot be delivered: either they are not in the event-type registry at all, or they are registered as a placeholder with no publisher behind them (NotificationEventTypeDefinition.IsAvailable == false). Attaching one to a subscription would produce a subscription that never fires. |
| `Notifications:EventTypesRequired` | 400 | At least one event type is required. |
| `Notifications:FilterExpressionTooDeep` | 400 | The filter expression is nested too deeply. Filter expression exceeds maximum depth. |
| `Notifications:FilterExpressionTooManyConditions` | 400 | The filter expression has too many conditions. Filter expression exceeds maximum number of conditions. |
| `Notifications:InsufficientScopeAccess` | 403 | You do not have access to the selected scope. User does not have access to the specified scope. |
| `Notifications:InvalidDestinationConfig` | 400 | The destination configuration is not valid. Destination configuration is invalid. |
| `Notifications:InvalidFilterExpression` | 400 | The filter expression is not valid. Filter expression is invalid. |
| `Notifications:ListenSessionMerchantRequired` | 403 | A listen session covers one merchant, so select a merchant before opening one. A listen session was requested for a merchant the caller is not authorized over, or by a caller with no merchant context at all. A session is always scoped to exactly one merchant. |
| `Notifications:ListenSessionNotActive` | 404 | That listen session is not available. It may have ended, expired, or never existed. Open a new session to keep listening. The requested listen session does not exist, has ended, or has expired. The three are deliberately indistinguishable so a caller cannot probe for another integrator's session id. |
| `Notifications:ListenSessionSampleEnvironmentUnknown` | 403 | Sample events are for sandbox only, and this request carries nothing that says which environment it is acting in. Use a test API key (sk_test_); a key issued before environments existed has to be rotated before it can trigger a sample. A sample event was requested for a listen session by a caller carrying no evidence of which world they are acting in: a cookie session, or an API key minted before environments existed. Distinguished from ListenSessionSampleProductionRefused because the remedy is different (rotate the key so it is stamped, rather than switch to a test one), and because neither answer is an oracle for anything the caller does not already know about themselves. |
| `Notifications:ListenSessionSampleProductionRefused` | 403 | Sample events are for sandbox only. This request used a live API key, and a sample is a synthetic event that never happened, so it is not delivered alongside real traffic. Use a test key (sk_test_) to trigger a sample. A sample event was requested for a listen session by a caller acting in the live world. Triggering a sample is sandbox-only: a sample is a synthetic event that never happened, and staging one into a production feed would put an invented occurrence in front of a receiver processing a merchant's real traffic. The remedy is a test key. |
| `Notifications:MerchantIdRequiredForMerchantScope` | 400 | A merchant is required when the subscription scope is Merchant. MerchantId is required when scope is Merchant. |
| `Notifications:QuickSetupAlreadyExists` | 409 | A quick-setup subscription already exists for this item. A quick-setup subscription already exists for the given context key. |
| `Notifications:QuickSetupContextFilterMismatch` | 400 | The quick-setup filter does not pin the subscription to the item its context key names. Quick-setup ContextKey identifies a specific entity instance (e.g. hpp:{guid}) but the supplied FilterExpressionJson does not pin the subscription to that instance. Server-side guard against an API caller bypassing the UI-supplied filter. |
| `Notifications:ResellerIdRequiredForResellerScope` | 400 | A reseller is required when the subscription scope is Reseller. ResellerId and ResellerPathPrefix are required when scope is Reseller. |
| `Notifications:SecretRotationAlreadyInProgress` | 409 | A signing-secret rotation is already in progress for this destination. Promote or cancel it before starting another. A signing-secret rotation is already in overlap for this destination. |
| `Notifications:SecretRotationNoPrimarySecret` | 409 | This destination has no signing secret, so there is nothing to rotate. Set a secret on the destination first. The destination has no signing secret, so there is nothing to rotate. |
| `Notifications:SecretRotationNotInProgress` | 409 | No signing-secret rotation is in progress for this destination. No signing-secret rotation is in overlap, so there is nothing to promote or cancel. |
| `Notifications:SecretRotationNotSupported` | 400 | Signing-secret rotation applies to webhook destinations only. This destination is a {destinationType} destination. Signing-secret rotation was requested for a destination type that has no signature. |
| `Notifications:SecretRotationSecretInvalid` | 400 | The replacement signing secret must be at least {minLength} characters. The replacement secret is missing or shorter than the configured minimum length. |
| `Notifications:SecretRotationSecretUnchanged` | 409 | The replacement signing secret is the same as the current one, so rotating to it would change nothing. The replacement secret is the same as the one currently in use. |
| `Notifications:SubscriptionNameAlreadyExists` | 409 | A subscription with this name already exists in this scope. Subscription name already exists within the same tenant/scope. |
| `Notifications:SubscriptionNotFoundForDelivery` | 404 | The subscription behind this delivery could not be found, so it cannot be retried. Subscription not found for delivery retry. |
| `Notifications:TooManyChannels` | 400 | A subscription can have at most {max} channels. Subscription exceeds the maximum number of channels. |
| `Notifications:TooManyEventTypes` | 400 | A subscription can have at most {max} event types. Subscription exceeds the maximum number of event types. |
| `Notifications:UnknownEventType` | 400 | The event type '{eventType}' is not in the event registry. Event type is not recognized in the registry. |
| `Notifications:UnsubscribeFailed` | 400 | The unsubscribe request could not be processed. Please try again. Unsubscribe request could not be processed due to an internal error. |
| `Notifications:UnsupportedDestinationType` | 400 | This destination type is not supported yet. Destination type is not currently supported (no processor available). |
| `Notifications:WebhookEventsMerchantRequired` | 403 | This reads one merchant's own webhook deliveries, so the credential has to resolve a single merchant. Use a merchant-scoped API key. The events pull API was called by a caller with no merchant context: an unscoped administrator, or a tenant-wide API key. The surface answers with one merchant's own deliveries and has no meaning without one, so it refuses rather than widening to every merchant. |
| `Notifications:WebhookReplayBudgetExhausted` | 429 | This endpoint has used its replay allowance for the current window. The delivery is unchanged. Wait for the window to roll over, then replay it. A replay was refused because this endpoint's replay budget for the current window is spent. The delivery is untouched and the same request succeeds once the window rolls over. |
| `Notifications:WebhookReplayEndpointSuppressed` | 409 | This delivery's destination endpoint is suppressed, so nothing would be sent. The delivery is unchanged. Clear the suppression, then replay it. A replay was refused because the delivery's destination endpoint is actively suppressed. The delivery is untouched: clearing a suppression is an administrator's action, so the caller is told that rather than handed an attempt that was never going to leave. |
| `Notifications:WebhookReplayUnavailable` | 409 | Webhook replay is unavailable on this deployment, so nothing was changed. Try again shortly. A replay was refused because this deployment composes no delivery attempt seam. Nothing was reset, deliberately: a delivery reset on a host that cannot then attempt it is left in a state that host never sweeps. |

### Operating Context

| Code | Status | Message |
| --- | --- | --- |
| `WinkPG.OperatingContext:InvalidMerchant` | 404 | The target merchant does not exist, or its ownership chain is not valid. The requested merchant does not exist, or its tenant / reseller ownership chain does not lead back to the context being switched into. |
| `WinkPG.OperatingContext:InvalidReseller` | 404 | The target reseller does not exist, or it does not belong to the specified tenant. The requested reseller does not exist, or does not belong to the specified tenant. |

### Payment

| Code | Status | Message |
| --- | --- | --- |
| `Volo.Payment:010001` | 403 | The payment request has no currency. Set a currency on the request before you start a one-time payment. |

### Payment Encryption Virtu Crypt

| Code | Status | Message |
| --- | --- | --- |
| `03` | 403 | A token value is outside the range the command accepts. |
| `04` | 403 | A required token is missing. The AN token names which. |
| `06` | 403 | The major key the request named is not loaded on the HSM. |
| `07` | 403 | The storage-table slot the request named holds no key. |
| `15` | 403 | The HSM timed out talking to something behind it. |
| `17` | 403 | A message authentication code did not verify. |
| `19` | 403 | The function is not supported or is blocked, which is how an unlicensed command reads. |
| `20` | 403 | The command is not allowed in the HSM's current state. |
| `26` | 403 | The feature is not licensed on this HSM. |
| `34` | 403 | The token is invalid, which is what an expired or malformed bearer token reads as. |
| `45` | 403 | The transaction counter in the key serial number is illegal. |
| `46` | 403 | The key serial number is the same as the current one, so the counter did not advance. |
| `48` | 403 | The wrapped key block could not be unwrapped. |
| `49` | 403 | The login was refused. |
| `50` | 403 | The session is not logged in. |
| `63` | 403 | The key is the wrong type for the command, commonly a wrong major key. |
| `84` | 403 | The derived key is weak and the HSM refuses to use it. |
| `91` | 403 | The identity holds no permission for this command. |
| `93` | 403 | The transaction counter is illegal. The second code the HSM uses for the same fault. |

### Payment Tokenization

| Code | Status | Message |
| --- | --- | --- |
| `PaymentTokenization:DomainAlreadyExists` | 409 | Domain '{domain}' already exists for this wallet provider. Domain already exists for this wallet provider registration. |
| `PaymentTokenization:DomainNotFound` | 404 | The specified domain was not found in this wallet provider registration. Domain was not found in the wallet provider registration. |
| `PaymentTokenization:InternalTokenNotDecryptable` | 400 | Internal stored tokens are resolved through the stored payment method, not the wallet decryption path. An internal stored token was submitted to the wallet decryption seam. Internal tokens are resolved through IPaymentTokenCrudAppService upstream and are never decryptable here. |
| `PaymentTokenization:MetadataNotFound` | 404 | The specified provider metadata key '{key}' was not found. Error code indicating that the requested provider metadata key was not found. |
| `PaymentTokenization:PazeCertificateNoPendingRequest` | 409 | There is no pending {environment} certificate request to merge. Generate a CSR first. There is no pending Paze certificate request to merge for the environment (nothing was generated, or the pending request is no longer in progress). |
| `PaymentTokenization:PazeCertificateNotReady` | 409 | The {environment} Paze certificate hasn't been generated yet. Generate it on the Settings page first. The environment's certificate has not been generated (or is still pending), so the public key needed to build the JWKS is not available yet. |
| `PaymentTokenization:PazeCertificateOperationInProgress` | 409 | A {environment} certificate request is already in progress. Download and merge the pending CSR, or cancel it, then try again. If you just started it, wait a moment and retry. A Paze certificate request is already in progress for the environment (or Key Vault has not finished preparing it), so a new generation cannot start yet. |
| `PaymentTokenization:PazeSignedCertificateInvalid` | 400 | The uploaded file isn't a valid signed certificate. Upload the CA-signed certificate as a PEM, base64, or DER (.cer) file. The uploaded CA-signed certificate could not be read as a valid signed certificate (expected PEM, base64, or DER). |
| `PaymentTokenization:ProviderNotRecognized` | 400 | The submitted token provider does not correspond to a supported wallet provider. The submitted token provider type has no corresponding wallet provider type, so no provider could be selected for decryption. |
| `PaymentTokenization:ProviderNotRegistered` | 409 | The selected wallet provider is not configured or does not support this operation. The configured wallet provider is not registered in the runtime registry. |
| `PaymentTokenization:ProviderRegistrationFailed` | 429 | The wallet provider could not complete the merchant registration. Please verify the details and try again. The wallet provider rejected the merchant-registration request. |
| `PaymentTokenization:ProviderSessionFailed` | 429 | The wallet provider could not start a payment session. Please try again. The wallet provider could not produce a payment session. |
| `PaymentTokenization:ProviderUnregisterFailed` | 429 | The wallet provider could not remove the requested domain(s). Please try again. The wallet provider rejected the unregister-domains request. |
| `PaymentTokenization:WalletDomainAlreadyRegistered` | 409 | Domain '{0}' is already registered on another Paze registration in this environment. Each domain can belong to only one Paze registration per environment. A domain in the create request is already registered on another registration row for a provider that requires globally-unique onboarding identity (Paze). Raised by the create path's pre-flight uniqueness check before the wire call. |
| `PaymentTokenization:WalletMerchantNameAlreadyRegistered` | 409 | Merchant name '{0}' is already used by another registration for this wallet in this environment. Each merchant name must be unique per environment. The merchant name in the create request is already used by another registration row for a provider whose traits require a unique merchant name per environment. Raised by the create path's pre-flight uniqueness check before the wire call. |
| `PaymentTokenization:WalletProviderAlreadyExists` | 409 | Wallet provider is already registered for this merchant. A wallet provider is already registered for the given merchant and provider type. |
| `PaymentTokenization:WalletProviderNotFound` | 404 | Wallet provider registration was not found for the specified merchant and provider. Wallet provider registration was not found for the given identifiers. |

### Payment Tokenization Apple Pay

| Code | Status | Message |
| --- | --- | --- |
| `PaymentTokenization:ApplePay:CertificateRenewalFailed` | 429 | The Apple Pay certificate renewal could not be started. Please try again, or use the manual certificate signing request flow. Certificate renewal could not start (CSR initiate failed). |
| `PaymentTokenization:ApplePay:ConnectionTestFailed` | 429 | The connection test to Apple Pay failed. Please verify the credentials and certificates, then try again. Connectivity / credential probe to Apple's API failed. |
| `PaymentTokenization:ApplePay:DeactivationFailed` | 429 | Apple Pay could not deactivate the merchant registration. Please try again. Apple's API rejected the merchant deactivation. |
| `PaymentTokenization:ApplePay:DomainVerificationUnsupported` | 400 | Apple Pay does not offer a standalone domain verification call. Domains are verified when the merchant is registered. Domain verification could not be performed (Apple has no standalone API for it). |
| `PaymentTokenization:ApplePay:MerchantIdOidMissing` | 403 | The Apple Pay payment processing certificate is missing the Apple merchant identifier extension. Re-run the payment processing certificate flow for this Platform Integrator. The Payment Processing certificate loaded from Key Vault is missing the Apple merchant-id-hash OID (1.2.840.113635.100.6.32). The cert was provisioned incorrectly; re-run the Payment Processing CSR / Complete lifecycle for this Platform Integrator. |
| `PaymentTokenization:ApplePay:PayloadFieldsMissing` | 400 | The Apple Pay payment token decrypted successfully but was missing required card details, so no usable payment credential could be produced. The token decrypted successfully but the plaintext payload was missing a required field (primary account number, or a parseable expiration date), so no usable card credential could be produced. Distinct from TokenDecryptionFailed (the cipher itself failed). |
| `PaymentTokenization:ApplePay:PaymentSessionFailed` | 429 | Apple Pay could not start a payment session. Please try again. Apple Pay merchant session creation failed. |
| `PaymentTokenization:ApplePay:PlatformIntegratorIdentifierNotConfigured` | 403 | The Apple Pay Platform Integrator identifier is not configured. Set it on the Wallets settings page before registering merchants. Apple Pay PlatformIntegratorIdentifier setting is not configured. Drives both the Key Vault cert lookup and the encryptTo wire field on registerMerchant; without it, no Apple call can be made. |
| `PaymentTokenization:ApplePay:PublicKeyHashMismatch` | 400 | The Apple Pay token was encrypted for a different payment processing certificate. This usually means a certificate renewal is in flight or the environment does not match. Apple Pay token's header.publicKeyHash does not match the SHA-256 of the loaded Payment Processing certificate's SubjectPublicKeyInfo. The token was encrypted to a different certificate (likely cert renewal in flight, an environment mismatch, or a stale FE-side cached pub key). |
| `PaymentTokenization:ApplePay:RegistrationFailed` | 429 | Apple Pay could not complete the merchant registration. Please verify the merchant name, URL, and domains, then try again. Apple's API rejected the merchant registration. |
| `PaymentTokenization:ApplePay:SignatureVerificationFailed` | 400 | The Apple Pay payment token signature could not be verified. Apple Pay token signature verification failed. Emitted only when ApplePaySettings.VerifySignature is on. Covers every failure branch of the pre-decryption signature check: malformed/absent CMS signature, cryptographic signature mismatch, missing Apple marker OIDs, a chain that does not build to the pinned Apple Root CA - G3, an unsupported token version, or (when enforced) a signing time outside the allowed window. The specific reason is in the operational log; only this stable code is surfaced to the caller (PCI: never echo token or crypto-library detail into the result message). |
| `PaymentTokenization:ApplePay:TokenDecryptionFailed` | 400 | The Apple Pay payment token could not be decrypted. Apple Pay token decryption failed for a non-classified reason, typically malformed token JSON, an unparseable ephemeral public key, or an AES-GCM authentication-tag failure. The original exception details are emitted to the operational log; this code is the only thing surfaced to the caller (PCI: never echo cryptographic-library messages into ErrorMessage). |

### Payment Tokenization Google Pay

| Code | Status | Message |
| --- | --- | --- |
| `PaymentTokenization:GooglePay:AuthMethodNotSupported` | 403 | The Google Pay payment token used a card authentication method this gateway does not support. The payload declared an authMethod this provider does not implement. |
| `PaymentTokenization:GooglePay:DecryptionFailed` | 403 | The Google Pay payment token could not be decrypted. Check that the key pair in Key Vault is the one the payment page published its public key from. The ECDH / HKDF / AES-CTR decryption of encryptedMessage failed, including a MAC mismatch. |
| `PaymentTokenization:GooglePay:GatewayMerchantIdMismatch` | 403 | The Google Pay payment token was minted for a different merchant. Check that the gateway merchant ID on the wallet registration matches the one the payment page was configured with. The decrypted payload's gatewayMerchantId is not the one the merchant's wallet registration holds, so the token was minted for a different merchant. |
| `PaymentTokenization:GooglePay:PayloadFieldsMissing` | 403 | The Google Pay payment token decrypted but carried no usable card number or expiration date. The inner payload decrypted successfully but was missing a required field (card number, or a parseable expiration month/year), so no usable card credential could be produced. Distinct from DecryptionFailed: the crypto was fine and the contents were not. |
| `PaymentTokenization:GooglePay:PrivateKeyNotConfigured` | 403 | Google Pay is not provisioned for this environment. Check that a key alias is set in the Wallets settings and that the wallet registration carries a Google merchant ID. No Key Vault alias is configured for the target environment, or the secret it names is absent. |
| `PaymentTokenization:GooglePay:RootKeysUnavailable` | 403 | Google's root signing keys could not be retrieved, so the payment token's signature could not be checked. Check the root signing keys URL in the Wallets settings. Google's root signing keys could not be fetched or parsed, so the signature could not be checked. |
| `PaymentTokenization:GooglePay:SignatureVerificationFailed` | 403 | The Google Pay payment token's signature did not verify. Check that the merchant's Google merchant ID matches the one the payment page was configured with. An ECDSA signature over the intermediate signing key or the signed message did not verify. |
| `PaymentTokenization:GooglePay:TokenExpired` | 403 | The Google Pay payment token has expired and cannot be used. The intermediate signing key, or the decrypted message itself, is past its expiration. |
| `PaymentTokenization:GooglePay:TokenMalformed` | 403 | The Google Pay payment token could not be read. Only the ECv2 protocol version is supported. The encrypted token was absent, was not JSON, or declared a protocol version this provider does not implement. |

### Payment Tokenization Paze

| Code | Status | Message |
| --- | --- | --- |
| `PaymentTokenization:Paze:ConnectionTestFailed` | 429 | The connection test to Paze failed. Please verify the credentials and certificates, then try again. |
| `PaymentTokenization:Paze:DeactivationFailed` | 429 | Paze could not deactivate the merchant registration. Please try again. |
| `PaymentTokenization:Paze:JweDecryptionFailed` | 400 | The Paze secured payload could not be decrypted. |
| `PaymentTokenization:Paze:OAuthFailed` | 429 | Paze rejected the authentication request. Please verify the OAuth settings and the environment certificate, then try again. |
| `PaymentTokenization:Paze:PartnerNotConfigured` | 403 | The Paze partner details are not configured. Set the partner identifier and key alias on the Wallets settings page. |
| `PaymentTokenization:Paze:PayloadFieldsMissing` | 400 | The Paze payload decrypted successfully but was missing required card details, so no usable payment credential could be produced. The inner payload decrypted successfully but was missing a required field (network token, or a parseable expiration month/year), so no usable card credential could be produced. Distinct from TokenDecryptionFailed. |
| `PaymentTokenization:Paze:PrivateKeyNotConfigured` | 403 | The Paze certificate for this environment has not been generated yet. Generate it on the Settings page first. |
| `PaymentTokenization:Paze:RegistrationFailed` | 429 | Paze could not complete the merchant onboarding. Please verify the merchant details and try again. |
| `PaymentTokenization:Paze:SecuredPayloadMissing` | 400 | The Paze response did not include a secured payload, so no payment credential could be read. |
| `PaymentTokenization:Paze:SignatureVerificationFailed` | 400 | The Paze payload signature could not be verified. |
| `PaymentTokenization:Paze:TokenDecryptionFailed` | 400 | The Paze payment token could not be decrypted. |

### Phoeni X Gate V2

| Code | Status | Message |
| --- | --- | --- |
| `PhoeniXGate:Data:ConcurrencyConflict` | 409 | This record was changed by someone else while you were editing it. Reload it and try again. |
| `PhoeniXGateV2:InvalidLandingPageUrl` | 400 | The landing page URL must be a valid relative path (e.g. /Dashboard/Merchant). A landing-page URL was supplied that is not a safe relative path: it is absolute, protocol-relative, carries a scheme, or does not parse as a relative URI. Rejected to close the open-redirect vector. |
| `PhoeniXGateV2:TooManyShortcuts` | 403 | You can pin at most {max} shortcuts ({count} were submitted). More shortcuts were saved than PhoeniXGateV2Settings.UserShortcutsMaxCount allows. |
| `PhoeniXGateV2:UnknownFeatureProvider` | 400 | There is no feature provider named {ProviderName}. Features can only be read or changed for a provider this system manages. A feature-management request named a feature value provider this host has no policy for. Only the providers registered in FeatureManagementOptions.ProviderPolicies can be read or written; a name outside that set, the merchant setting provider name M included, is a caller mistake rather than a server fault. |
| `PhoeniXGateV2:UnknownPermissionProvider` | 400 | There is no permission provider named {ProviderName}. Permissions can only be read or changed for a provider this system manages. A permission-management request named a permission value provider this host has no policy for. Only the providers registered in PermissionManagementOptions.ProviderPolicies can be read or written; a name outside that set is a caller mistake rather than a server fault. |
| `PhoeniXGateV2:UserNameChangeNotSupportedOnSelfServiceProfile` | 403 | Username cannot be changed from the profile page. Contact an administrator if you need a different username. A user name change was submitted through the self-service profile. The self-service profile does not change user names; an administrator changes them through user management. |
| `Volo.Account:PhoneNumberConfirmationDisabled` | 403 | Phone number confirmation is disabled! Phone-number confirmation was requested while the SignIn.EnablePhoneNumberConfirmation setting is off. See AccountPhoneNumberEmpty for why this vendored namespace has a first-party constant. |
| `Volo.Account:PhoneNumberEmpty` | 400 | Phone number is empty! A phone-number operation was requested for a user who has no phone number on record. |

### Promotions

| Code | Status | Message |
| --- | --- | --- |
| `Promotions:ApplicabilityTargetsRequired` | 400 | A promotion narrowed to pages, products or plans must name at least one. The applicability scope names a target list, and the list is empty. Such a promotion could never apply to anything, so it is refused rather than stored as permanently inert. |
| `Promotions:CurrencyRequiredForFlatDiscount` | 400 | A flat-amount promotion must name the currency its value is in. A flat-amount promotion must name the currency its value is denominated in. |
| `Promotions:DiscountValueOutOfRange` | 400 | A {DiscountType} discount of {Value} is outside the allowed range, which tops out at {Maximum}. The discount value is outside the range its discount type allows. |
| `Promotions:DurationCyclesRequired` | 400 | Say how many billing cycles the discount runs for. A duration of Cycles must name how many billing cycles it runs for. |
| `Promotions:LimitOutOfRange` | 400 | That limit is outside the allowed range. A redemption ceiling or minimum amount is outside the range the promotion accepts. |
| `Promotions:MerchantIdIsImmutable` | 409 | A promotion cannot be moved to a different merchant after it is created. A promotion's owning merchant cannot be changed after it is created. |
| `Promotions:PromotionCodeAlreadyExists` | 409 | Another promotion of this merchant already uses that code. Another promotion of the same merchant already uses this code. |
| `Promotions:PromotionNotFound` | 404 | That promotion could not be found. The promotion was not found, or is not visible to the caller. |
| `Promotions:RedemptionContention` | 409 | That promotion is being used by too many customers at once. Please try again. A redemption could not be recorded because too many concurrent redemptions of the same promotion contended for the ceiling. |
| `Promotions:RedemptionLimitReached` | 409 | That promotion has been fully used. A redemption was recorded against a promotion whose redemption ceiling is already reached. |
| `Promotions:StartMustBeBeforeEnd` | 400 | The promotion start must fall before the promotion end. The validity start must fall before the validity end. |
| `Promotions:TooManyApplicabilityTargets` | 400 | A promotion can name at most {Maximum} pages, products or plans, and {Requested} were given. The applicability scope names more targets than the embedded list holds. |

### Rate Limiting

| Code | Status | Message |
| --- | --- | --- |
| `RateLimiting:ProfileNameAlreadyExists` | 409 | A rate limit profile named '{name}' already exists. Another profile in the same tenant already uses the requested name. |
| `RateLimiting:ProfileNotFound` | 404 | The selected rate limit profile does not exist. The profile a rule points at does not exist. |
| `RateLimiting:RuleAlreadyExistsForLimiter` | 409 | This profile already has a rule for the limiter '{limiterName}'. The profile already carries a rule for the same limiter. |

### Referential Integrity

| Code | Status | Message |
| --- | --- | --- |
| `WinkPG.ReferentialIntegrity:Blocked` | 409 | This record is still referenced by other records, so it cannot be changed or removed. Code set when GuardAsync``1 trips on a HardBlock reference. The exception's Data bag carries: entityType: full CLR type name of the target. entityId: stable identifier of the target. operation: Kind. references: JSON-serializable list of blocking EntityReference rows. |

### Reports

| Code | Status | Message |
| --- | --- | --- |
| `Reports:ExportFormatNotSupported` | 400 | This report cannot be exported as {Format}. The export asked for a file format this report does not produce. The report catalog lists each report's formats. |
| `Reports:InvalidFilterValue` | 400 | The value for the filter '{Filter}' is not valid. The run supplied a filter value that does not fit the filter's type or its allowed options. |
| `Reports:MerchantFilterNotSupported` | 400 | This report cannot be narrowed to a single merchant. The run named a merchant, but this report cannot be narrowed to one merchant because it does not report merchant-scoped data. The report catalog says which reports accept a merchant. |
| `Reports:MerchantNotInScope` | 404 | The merchant was not found or is not available to you. The run named a merchant the caller cannot read. Answered the same way whether the merchant is outside the caller's scope or does not exist, so the refusal does not reveal which. |
| `Reports:ReportNotFound` | 404 | The report was not found. The report does not exist, or it is not available to this tenant because the feature it belongs to is turned off. Both answer the same way on every report route, so a caller cannot tell a disabled report from one that was never defined. |
| `Reports:RowLimitExceeded` | 400 | The report returned more than {MaxRowCount} rows. Narrow the date range or add filters, then run it again. The run produced more rows than a single request may return. Narrow the date range or add filters and run it again. |
| `Reports:UnknownFilter` | 400 | The filter '{Filter}' is not defined for this report. The run supplied a filter this report does not define. The report's parameters route lists the filters it accepts. |

### Resellers

| Code | Status | Message |
| --- | --- | --- |
| `Resellers:CaptureHppPageRejected` | 403 | The selected card capture Hosted Payment Page is not valid. It must be an active Save Card page that belongs to this reseller. The reseller's candidate card-capture hosted payment page does not exist, is not a card capture page, or belongs to a different reseller. |
| `Resellers:CardAcceptanceDefaultIssuerCountriesInvalid` | 400 | The card acceptance default's issuer countries are not valid for the selected mode. The allow list and deny list modes need at least one country and the other modes need none, and every code must be a distinct two-letter ISO 3166-1 alpha-2 country, for example US. The reseller's card acceptance default carries an issuer country list that disagrees with the issuer country mode it was sent with, or a code that is not a two-letter ISO 3166-1 alpha-2 country. |
| `Resellers:CardAcceptanceDefaultRefusesEverything` | 400 | The card acceptance default refuses both credit and debit cards, or every card type, which refuses very nearly every card for every merchant that inherits it. Accept at least one of credit or debit, and at least one of consumer, commercial or GSA. The reseller's card acceptance default refuses both card rails, or every issued entity, so it would refuse very nearly every card for every merchant that inherits it. |
| `Resellers:CollectionMerchantRejected` | 403 | The selected collection merchant is not valid. It must be an active merchant that belongs to this reseller. The reseller's candidate collection merchant does not exist, is inactive, or belongs to a different reseller. |
| `Resellers:DefaultTaxRateRejected` | 403 | The selected default tax rate is not valid. It must be an active tax rate that belongs to this reseller. The reseller's candidate default tax rate does not exist, is inactive, or belongs to a different biller. |
| `Resellers:InvoiceTemplateRejected` | 403 | The selected invoice template is not valid. It must be an active template that belongs to this reseller. The reseller's candidate default invoice template does not exist, is inactive, or belongs to a different biller. |
| `Resellers:ResellerParentCheckUnavailable` | 403 | The reseller hierarchy could not be checked, so the parent change was not saved. Try again in a moment. The parent chain could not be read, so the cycle check could not reach a verdict. Distinct from a rejection: the request may succeed unchanged on a retry. |
| `Resellers:ResellerParentCreatesCycle` | 403 | A reseller cannot be moved beneath itself or beneath one of its own sub resellers. Choose a parent outside this reseller's hierarchy. The selected parent reseller would place the reseller inside its own ancestry. |
| `Resellers:ResellerParentNotPermitted` | 403 | You can only place a reseller beneath your own reseller. Choose your reseller, or one below it, as the parent. The selected parent reseller is outside what the caller is permitted to parent under. |
| `Resellers:SubResellerCreationDisabled` | 403 | Sub resellers are not enabled for this reseller. Ask an administrator to turn on the Sub Resellers option before creating one. Sub-reseller creation is switched off for the acting reseller or for the selected parent. |
| `Resellers:TopLevelResellerRequiresAdmin` | 403 | Only an administrator can create a top-level reseller. Choose a parent reseller for the new reseller. Creating a reseller with no parent is reserved to administrators. |

### Saas

| Code | Status | Message |
| --- | --- | --- |
| `Saas:Edition:0001` | 403 | Edition doesn't have a plan! |
| `Saas:Edition:0002` | 403 | Unable to delete {EditionName}, It is in use by tenants. |

### Scoped Settings

| Code | Status | Message |
| --- | --- | --- |
| `WinkPG.ScopedSettings:InvalidTarget` | 400 | The target reseller or merchant is not valid. The target reseller/merchant id is empty (Empty). |
| `WinkPG.ScopedSettings:NotOverridable` | 403 | This setting cannot be overridden at the requested scope. The setting is not registered overridable at the requested scope. |
| `WinkPG.ScopedSettings:ProviderNotAllowed` | 403 | This setting cannot be saved at the requested scope: its definition does not allow the scoped provider, so the value would never be read back. The setting is registered overridable at the scope, but its ABP SettingDefinition.Providers restriction excludes the matching scoped provider ("R"/"M"), so a write would persist a value the read pipeline can never resolve (a silent dead write). |
| `WinkPG.ScopedSettings:ScopeUnavailable` | 403 | No reseller or merchant scope is available to save this setting against. No effective reseller/merchant scope is available to resolve a *ForCurrent* write. |

### Security Posture

| Code | Status | Message |
| --- | --- | --- |
| `Sbom:Error:InvalidBuildId` | 400 | Invalid SBOM build identifier. The supplied SBOM build identifier is empty or not in the accepted shape. |
| `Sbom:Error:InvalidComponentName` | 400 | Invalid SBOM component name. The supplied SBOM component name is empty or not in the accepted shape. |
| `Sbom:Error:NotConfigured` | 403 | The SBOM archive is not configured for this deployment. Set WinkPG:SecurityPosture:SbomStorage in configuration to enable it. The SBOM archive has no storage configured for this deployment, so nothing can be served. |

### Shared Models

| Code | Status | Message |
| --- | --- | --- |
| `CardData:CardDataElementRequired` | 400 | No card data was supplied. No card data element of any kind was supplied. |
| `CardData:CardExpired` | 400 | The card has expired. The resolved card expiration is in the past. |
| `CardData:CardNumberFailedLuhn` | 400 | The card number is not valid. The card number failed the Luhn check digit. |
| `CardData:CardNumberLengthInvalid` | 400 | The card number must be 13 to 19 digits. The card number's digit run is outside the accepted 13-19 digit range. |
| `CardData:CardNumberRequiredForManualEntry` | 400 | Manual entry requires a card number. Manual entry was requested without a card number. |
| `CardData:CardVerificationValueConflict` | 400 | CardVerificationValue and Cvv were both sent with different values. Send only CardVerificationValue. Both CardVerificationValue and the deprecated integer Cvv were supplied with different values. |
| `CardData:CvPresenceConflictsWithSuppliedCvv` | 400 | A card security code was supplied, but the card verification indicator says none was collected. A CVV value was supplied while the CV presence indicator says none was collected. |
| `CardData:CvPresenceRequiresCvv` | 400 | The card verification indicator says a card security code was collected, but none was supplied. The CV presence indicator says a CVV was submitted but no value was supplied. |
| `CardData:CvvLengthInvalid` | 400 | The card security code must be 3 or 4 digits. The CVV is not 3 or 4 digits. |
| `CardData:EmvDataRequiredForIccOrProximity` | 400 | A chip or contactless transaction requires EMV data. An ICC or proximity transaction arrived without EMV data. |
| `CardData:EncryptedTrackNotAllowedForManualEntry` | 400 | Encrypted track data is not allowed on a manual-entry request. Encrypted track data accompanied a manual-entry request. |
| `CardData:EncryptedTrackNotAllowedForUnencryptedIcc` | 400 | Encrypted track data is not allowed on an unencrypted chip or contactless transaction. Encrypted track data accompanied a non-encrypted ICC or proximity transaction. |
| `CardData:EncryptedTrackNotAllowedForUnencryptedSwipe` | 400 | Encrypted track data is not allowed on an unencrypted card-reader swipe. Encrypted track data accompanied an unencrypted card-reader swipe. |
| `CardData:EncryptedTrackRequiredForEncryptedSwipe` | 400 | An encrypted card-reader swipe requires encrypted track data. An encrypted card-reader swipe arrived without encrypted track data. |
| `CardData:EntryModeIncompatibleWithTerminalCapability` | 400 | A chip or fallback entry mode is not compatible with a stripe-only terminal capability. A chip or fallback entry mode was combined with a stripe-only terminal capability. |
| `CardData:ExpirationRequired` | 400 | Manual entry requires a card expiration date. Manual entry was requested without a resolvable expiration. |
| `CardData:FullCardNumberNotAllowedOnStoredSnapshot` | 400 | A stored payment method carries only a masked card number. Send the masked value you read, or leave the card number out. A stored payment method's card snapshot carried a full card number where only the masked display copy belongs. |
| `CardData:ManualEntryNotAllowedWhenEncrypted` | 400 | Manual entry cannot be combined with encrypted track data. Manual entry mode was combined with encrypted track data, which is contradictory. |
| `CardData:TrackDataNotAllowedForEncryptedSwipe` | 400 | Unencrypted track data is not allowed on an encrypted card-reader swipe. Unencrypted track data accompanied an encrypted card-reader swipe. |
| `CardData:TrackDataNotAllowedForManualEntry` | 400 | Track data is not allowed on a manual-entry request. Unencrypted track data accompanied a manual-entry request. |
| `CardData:TrackOrEmvRequiredForUnencryptedSwipe` | 400 | An unencrypted card-reader swipe requires track or EMV data. An unencrypted card-reader swipe arrived without track or EMV data. |
| `CustomerAddress:Address1Required` | 400 | Address1 is required. An address was supplied without its street line (address1). |
| `CustomerAddress:AddressEntryMustNotBeNull` | 400 | An address entry must not be null. The addresses array carried a null element ("addresses": \[null, { ... }\]). Rejected because a null entry validates against every per-element rule and then throws in the first reader that looks for the default address. |
| `CustomerAddress:CityRequired` | 400 | City is required. An address was supplied without its city. |
| `CustomerAddress:SingleDefaultAddressRequired` | 400 | When an address is supplied, exactly one must be set as the default. Addresses were supplied but the number flagged isDefault was not exactly one. Supplying no address at all is accepted and does not raise this; every consumer that reads "the customer's address" picks the default first, so an absent or ambiguous default is a defect once entries exist. |
| `CustomerAddress:StateRequired` | 400 | State is required. An address was supplied without its state. |
| `CustomerAddress:ZipRequired` | 400 | Zip code is required. An address was supplied without its postal code (zip). |
| `DeviceData:EncryptionKeyVariantInvalid` | 400 | The encryption key variant is not recognized. Use Data or Pin. The DUKPT key variant is not one of the values the platform recognizes (Data or Pin). |
| `DeviceData:PinVariantNotSupportedForProviderDecryption` | 400 | The Pin key variant is not supported with a DUKPT or ONGUARD encryption type. Send the Data variant or omit the field. The request names the PIN key variant on a DUKPT or ONGUARD payload the merchant's payment encryption provider was about to decrypt. The gateway decrypts data-variant payloads only through a provider; send the data variant, or omit the variant. |
| `Merchants:ScreeningProviderProcessorConflict` | 409 | This screening provider cannot be used alongside one of the merchant's active processors. An active screening provider profile would coexist with an active processor profile the provider declares a conflict with. |
| `PhoeniXGateV2:RoleLadderConflict` | 400 | A user's roles must all belong to a single account type, and it must be the user's own. User type: {UserType}. Conflicting roles: {ConflictingRoles} ({SpannedTypes}). Remove the roles that do not belong to the user's account type, or change the user type to match them. A user's roles were going to span more than one rung of the ladder, or to contradict the user's own type. Answers 400: the submitted content is contradictory whatever the stored state, and the caller's authorization to manage roles is not in question. |

### Shipping

| Code | Status | Message |
| --- | --- | --- |
| `Shipping:DefaultCannotBeCleared` | 409 | This is the current default. Make another one the default first; the flag moves in one step. The row is the merchant's default and the merchant has others, so the default flag cannot be switched off on it directly: another row has to be made the default first, which moves the flag in one step and never leaves the merchant without one. |
| `Shipping:DefaultCannotBeDeleted` | 409 | This is the current default and others exist. Make another one the default before deleting this one. The row is the merchant's default and the merchant has others, so deleting it would leave the merchant with rows but no default. Another row has to be made the default first. |
| `Shipping:MerchantIdIsImmutable` | 409 | A ship-from origin or parcel preset cannot be moved to a different merchant after it is created. An origin's or preset's owning merchant cannot be changed after it is created. |
| `Shipping:OriginNotFound` | 404 | That ship-from origin could not be found. The ship-from origin was not found, or is not visible to the caller. |
| `Shipping:ParcelMeasureOutOfRange` | 400 | Every parcel measure must be greater than zero and no more than {Maximum}. A parcel's weight, length, width or height is zero, negative, or above its ceiling: 100,000 for weight and 10,000 for each dimension, in the parcel's own units. Arrives as the code of each validationErrors entry, whose member names the field (for example parcels\[0\].Weight on a rate quote, or height on a parcel preset). |
| `Shipping:ParcelPresetNotFound` | 404 | That parcel preset could not be found. The parcel preset was not found, or is not visible to the caller. |
| `Shipping:QuoteDestinationChanged` | 409 | The shipping address changed after the rate was quoted. Get shipping rates again for the new address. The submitted destination is not the address the rate quote was priced against. An address change after quoting needs a new quote, never a stale price. |
| `Shipping:QuoteExpired` | 409 | That shipping rate has expired. Get shipping rates again and choose a service. The selected rate quote is past its expiry. The carrier price it recorded is no longer honored, and the shopper has to quote again. |
| `Shipping:QuoteNotFound` | 404 | That shipping rate could not be found. Get shipping rates again and choose a service. The selected rate quote cannot be redeemed: no quote with that identifier exists for this merchant, it belongs to another merchant or was issued for another hosted payment session, or the submit named no quote on a page that prices shipping from one. Deliberately one code for all of these, so a probe cannot tell an unknown identifier from another shopper's quote. |
| `Shipping:QuoteOptionNotFound` | 404 | That shipping service is not one of the rates offered. Get shipping rates again and choose a service. The selected option is not one of the options the rate quote returned. |
| `Shipping:QuoteRateLimitExceeded` | 429 | Too many shipping rate quotes were requested for this merchant. Wait a moment and try again. The merchant has requested more rate quotes inside the current window than the platform's per-merchant ceiling allows. Answered before any carrier call is made. |

### Step Up

| Code | Status | Message |
| --- | --- | --- |
| `WinkPG.StepUp:MfaEnrolmentRequired` | 403 | This action needs an authenticator app. Set one up in your security settings, then try again. The action can only be satisfied by an enrolled second factor and the acting user has none. The remedy is enrolment, never a weaker factor. |
| `WinkPG.StepUp:NoInteractiveSession` | 403 | This action can only be performed by a signed-in person, so it cannot run from an automated caller or an API key. The call reached a guarded action with no interactive session behind it: a Functions host, a background job, or a route authenticated with an API key rather than a person. |
| `WinkPG.StepUp:Required` | 403 | For your security, confirm it is you before continuing. The caller must re-authenticate before this action. Carries the action key and the window in the exception data so a client can tell the person what to satisfy and for how long it lasts. |
| `WinkPG.StepUp:UnknownAction` | 403 | This action is misconfigured and has been refused. Contact support. The guarded method names an action key no contributor declared. A configuration defect, refused rather than passed through so a method that looks guarded is never silently unguarded. |

### Stored Credential Consents

| Code | Status | Message |
| --- | --- | --- |
| `stored_credential_consent_input_invalid` | 400 | A capture request was submitted with fields that contradict each other (e.g. Captured=false but other fields populated; Recurring-scoped consent with no enrollment terms). |
| `stored_credential_consent_required` | 400 | The transaction would store the card and this merchant requires captured consent. Fetch the terms from GET /api/merchant/stored-credential-consent/config and re-submit with a StoredCredentialConsentInput. |
| `stored_credential_consent_text_version_retracted` | 409 | A transaction submitted a ConsentTextVersion for a template that has been retracted. Refetch the consent config and resubmit. |
| `stored_credential_consent_text_version_unknown` | 400 | A transaction submitted a ConsentTextVersion that does not match any known template. The caller should refetch the consent config and resubmit. |

### Surcharging

| Code | Status | Message |
| --- | --- | --- |
| `Surcharging:ConfigurationAlreadyExists` | 409 | A surcharge configuration already exists for this merchant. A surcharge configuration already exists for the merchant (one per merchant). |
| `Surcharging:ConfigurationNotFound` | 404 | This merchant has no surcharge configuration to change. Create one before updating it. The merchant has no surcharge configuration to change. Raised by the merge patch endpoint, which has to read the stored configuration before it can apply a patch to it. |
| `Surcharging:DuplicateStatePolicy` | 400 | The same state code appears more than once in the state policy list. A state policy list contains the same state code more than once. |
| `Surcharging:ForceEnableNotAvailable` | 403 | Skipping the notice waiting period is only possible in a non-production environment with the sandbox override setting turned on. The non-production waiting-period bypass is not available here: the host environment is not on the allowlist, or the sandbox-override setting is off. A policy refusal, not a state conflict. |
| `Surcharging:ForceEnableRequiresConfiguration` | 409 | Save a surcharge rate for this merchant before activating surcharging. Force-enable was requested for a configuration that has not been set up far enough to activate (it carries no rate, so it is still NotConfigured). |
| `Surcharging:InvalidRate` | 400 | The surcharge rate is invalid. A supplied rate is invalid (e.g. negative). |
| `Surcharging:NewNoticeRequiredForProcessorChange` | 409 | This merchant has an active card processor that no filed surcharge notice covers. File a notice for that processor, or one without a processor account to cover every processor, before activating surcharging. An enable, a force enable or the waiting-period sweep refused a configuration because the merchant has an active card processor profile that no counted notice covers. Raised only while the RequireProcessorNoticeCoverage setting is on. A state conflict: filing a notice for that processor profile, or one with no processor account (which covers every processor), clears it. |
| `Surcharging:NoApplicableNotice` | 409 | Surcharging cannot start: no filed notice applies to the currently allowed networks. Activation has no notice applicable to the currently allowed networks. |
| `Surcharging:NoticeNotFound` | 404 | No surcharge notice with this id exists for this merchant. A notice without an id cannot be amended; file a new notice to correct it. No notice on this merchant's configuration carries the supplied notice id. That includes a notice recorded before notice ids were assigned, which carries none and so cannot be named in an amendment; it is corrected by filing a new notice instead. |
| `Surcharging:NoticeRequired` | 400 | A surcharge notice must be filed before surcharging can be activated. An operation requires at least one filed notice. |
| `Surcharging:ProcessorAccountNotFound` | 400 | The processor account is not one of this merchant's card processor profiles. Use a processor profile id from the merchant's processing settings, or leave the processor account out to file a notice that covers every processor. A notice named a processorAccountId that is not one of the merchant's card processor profiles. The value is the processor profile id from the merchant's processing settings; an omitted value files an all-processor notice instead. A request error rather than a state conflict: the same request can never succeed until it names a profile the merchant has. |
| `Surcharging:PromotionBlockedByPolicy` | 409 | Surcharging cannot be activated: this platform only activates merchants whose card-brand notice was filed the required way, and the notices on file for this merchant were not. File a new notice through the API, or ask an administrator about the automatic promotion source policy. An activation was refused by the deployment's SurchargeAutoPromotionSourcePolicy: the notices this configuration's waiting period rests on were not filed the way the policy requires, or the policy promotes nothing at all. A state conflict rather than an authorization failure, for the same reason PromotionHeld is: the caller may hold every permission the verb needs and still be refused until a notice is filed the sanctioned way, or an operator moves the policy. |
| `Surcharging:PromotionHeld` | 409 | Surcharging cannot be activated: a promotion hold is in place for this merchant. Release the hold to allow activation. An activation was refused because the configuration carries an explicit promotion hold. A state conflict, not an authorization failure: the caller may hold every permission the verb needs and still be refused until the hold is released. |
| `Surcharging:QuoteRateLimitExceeded` | 429 | Too many surcharge-quote requests. Please slow down and try again shortly. The surcharge-quote endpoint received more requests than its per-caller ceiling allows. |
| `Surcharging:RateExceedsCap` | 400 | The surcharge rate exceeds the maximum permitted rate. A surcharge rate exceeds the applicable cap (network or state). |
| `Surcharging:RateRequired` | 409 | Set a default surcharge rate, or a rate for every allowed card network, before filing a notice or activating surcharging. A notice, an enable or a force enable was refused because the configuration names no rate for at least one card network it may surcharge: there is no default rate, and either no network policies (so every brand is allowed) or an allowed network with no rate of its own. A state conflict: saving a default rate, or a rate on every allowed network, clears it. |
| `Surcharging:ReEnableRequiresDisabled` | 409 | Surcharging can only be re-enabled from a disabled state. Re-enabling requires the configuration to be in the disabled state. No longer raised: the separate re-enable path was retired and an enable from Disabled goes through the ordinary enable. Kept because the code is published in the customer-facing error code catalog. |
| `Surcharging:WaitingPeriodActive` | 409 | Surcharging cannot start yet: the required notice waiting period has not elapsed. An operation requires the mandatory notice waiting period to have elapsed. |

### Three D Secure

| Code | Status | Message |
| --- | --- | --- |
| `ThreeDSecure:CapabilityNotSupported` | 403 | The configured 3-D Secure provider does not support this step of the authentication. The registered 3-D Secure provider does not implement the capability the caller needs, for example asking a provider with no challenge support to build a challenge request. |
| `ThreeDSecure:FeatureDisabled` | 403 | 3-D Secure is not enabled for this account. The 3-D Secure capability is switched off for this host or tenant, so its configuration and invocation surfaces refuse. Distinct from a merchant that simply has no binding, which is not an error at all: this says the platform is not serving the capability to anyone here. |
| `ThreeDSecure:ProviderNotRegistered` | 403 | 3-D Secure authentication is not available: the configured provider is not installed on this environment. No 3-D Secure provider is registered for the requested provider type. Either the vendor's module is not wired into the host, or configuration selected a provider that does not ship. |

### Three D Secure Fake

| Code | Status | Message |
| --- | --- | --- |
| `ThreeDSecure.Fake:AssertionSignatureMissing` | 400 | The 3-D Secure device confirmation could not be verified because it carried no signature. A Secure Payment Confirmation assertion was submitted with no signature, so there was nothing to verify. |
| `ThreeDSecure.Fake:ChallengeResultMissing` | 400 | The 3-D Secure challenge result could not be read because it carried no data. A challenge result was submitted with no encoded payload, which a real ACS never does. |

### Three D Secure Paay

| Code | Status | Message |
| --- | --- | --- |
| `ThreeDSecure.Paay:AuthenticationRejected` | 400 | The 3-D Secure provider refused this authentication request. Paay refused the request. The content, the credentials, or the account configuration was not acceptable, and repeating the same call unchanged will be refused again. |
| `ThreeDSecure.Paay:ChallengeInstructionIncomplete` | 400 | The 3-D Secure challenge could not be started because the provider returned no challenge address. Paay reported that a challenge is required but did not return the ACS URL and ACS transaction identifier needed to run one, leaving the caller an instruction it cannot act on. |
| `ThreeDSecure.Paay:ChallengeResultMissing` | 400 | The 3-D Secure challenge result could not be read because it carried no data. A challenge result was submitted with no encoded payload, so there was nothing to attribute to the cardholder. |
| `ThreeDSecure.Paay:CredentialsNotConfigured` | 403 | 3-D Secure authentication is not available: the provider credentials have not been configured for this environment. No Paay API key and secret are configured for this environment, so no call was attempted. |
| `ThreeDSecure.Paay:CurrencyNotSupported` | 400 | The 3-D Secure provider does not support the currency of this transaction. The requested currency is not in Paay's published currency table, so no numeric code could be sent. Omitting the field would have Paay authenticate against the account's default currency instead, which is a wrong answer reported as a right one. |
| `ThreeDSecure.Paay:PurchaseContextRequired` | 400 | The 3-D Secure device check could not be prepared because the purchase amount, currency, or browser details were missing. The 3DS Method step was requested without the purchase context Paay needs. Paay has no PAN-only method lookup: the method URL comes back from the authentication call, which requires the amount, the currency, and the browser data. |
| `ThreeDSecure.Paay:ResultNotReady` | 429 | The 3-D Secure provider did not return a result in time. The authentication is still open and can be checked again. The polling budget was spent before Paay produced a result. The authentication is still open on Paay's side; polling the same transaction again can still answer. |
| `ThreeDSecure.Paay:ThreeRiCredentialsNotConfigured` | 403 | 3-D Secure authentication for stored cards is not available: the provider credentials for it have not been configured for this environment. No Paay 3RI OAuth client is configured for this environment. The 3RI endpoints authenticate with a separate client credential pair from the browser Direct API, so an account can hold one and not the other. |
| `ThreeDSecure.Paay:VendorUnavailable` | 429 | The 3-D Secure provider could not be reached. Try again shortly. Paay could not be reached, answered a server error, or rate-limited the call. Nothing about the request was rejected, so the same call can succeed later. |

### Transaction Manager

| Code | Status | Message |
| --- | --- | --- |
| `AUTH_RESULT_TIMEOUT` | 429 | The authorization result did not arrive inside the pending-authorization window. The transaction may still complete out of band, so the caller should reconcile rather than assume the charge did not happen. |
| `CardNotAcceptedByMerchantPolicy` | 400 | The merchant's card acceptance policy refuses the card, judged from the BIN data resolved by enrichment. The orchestrator's backstop for the create path's own gate, and the only enforcement point for a payload whose PAN becomes available only after enrichment. |
| `CashAmountExceedsMerchantPolicy` | 400 | A cash sale is above the merchant's maximum cash sale amount. The orchestrator's backstop for the create path's Transactions:CashAmountExceedsMerchantPolicy, for a cash sale that reached orchestration without passing the create path's gate. |
| `CashRegisterRequiredByMerchantPolicy` | 400 | A cash sale names no register and the merchant's cash policy requires one. The orchestrator's backstop for the create path's Transactions:CashRegisterRequiredByMerchantPolicy. |
| `CashSourceNotAllowedByMerchantPolicy` | 400 | A cash sale came from a source other than the Virtual Terminal and the merchant's cash policy takes cash there only. The orchestrator's backstop for the create path's Transactions:CashSourceNotAllowedByMerchantPolicy. |
| `Decline` | 409 | The transaction was rejected before any processor could act on it, typically because every candidate processor was exhausted. |
| `InvalidMerchantInfo` | 404 | The merchant the request named could not be resolved. |
| `MISSING_CONTEXT` | 400 | The request reached the orchestrator without context the flow needs before it can run, such as merchant or user detail the validation stage could not resolve. The response names the missing pieces. |
| `OPERATION_NOT_ALLOWED` | 409 | The requested operation is not valid for the transaction's current state, for example a capture against a transaction that was never authorized. |
| `PaymentechIncrementalAuthorization.BrandNotSupported` | 403 | The card brand on the transaction does not support incremental authorization at Paymentech. |
| `PaymentechIncrementalAuthorization.InvalidAmount` | 403 | The requested incremental authorization amount is not one the Paymentech host accepts. |
| `PaymentechIncrementalAuthorization.MissingAuthCode` | 403 | An incremental authorization named no authorization code from the original approval, which the Paymentech host requires to attach the increment to it. |
| `PaymentechIncrementalAuthorization.MissingFields` | 403 | An incremental authorization reached the Paymentech handler without the fields the host requires to raise the reserved amount. |
| `PaymentechIncrementalAuthorization.NotAuthorized` | 403 | An incremental authorization was requested against a transaction that is not in an authorized state, so there is no reservation to raise. |
| `PaymentechReversal.PartialReleaseUnavailable` | 403 | A partial release could not be resolved to an amount the Paymentech host would accept, so no reversal was attempted. |
| `PinDebitNotSupportedOnCreditBin` | 400 | The request asked for a PIN debit funding source on a card whose BIN is credit. The two directives contradict each other and no processor can satisfy both. |
| `PolicyRejected` | 403 | A platform policy rule refused the transaction, for example the certification-overrides gate. |
| `ProcessorNotConfigured` | 409 | No processor could be selected for this transaction. A merchant configuration change is needed before a retry can succeed. |
| `REVIEW_DECLINED` | not an HTTP status | A reviewer declined a transaction that fraud screening had held for manual review. This is a policy refusal and it is final: a human looked at this order and rejected it. Read from responseData.declineReasonCode on the transaction rather than from a response status. |
| `REVIEW_EXPIRED` | not an HTTP status | A transaction held for manual review reached its deadline with no disposition, and the merchant's configured expiry action was to decline. Nobody rejected the order; nobody released it either. Read from responseData.declineReasonCode on the transaction rather than from a response status. |
| `Reject` | 409 | The transaction was rejected by the orchestrator's own pre-authorization checks. |
| `SCREENING_STOPPED` | 403 | A screening contributor stopped the transaction. This is a policy refusal and it is final: resubmitting the same request will be stopped again. |
| `SCREENING_UNAVAILABLE` | 429 | Screening could not be completed, for example a screening provider timed out or its circuit is open, on a profile configured to hard stop. Retrying later may succeed with no configuration change. |
| `ThreeDSecureAuthenticationRequired` | 400 | The merchant requires 3-D Secure authentication and the transaction arrived without a usable one. Raised by the pipeline's 3DS stage for a merchant on the Require policy mode when no authentication ran, the issuer did not authenticate, or the result handed to the pipeline failed validation. |
| `TransactionManager:NoAuthorizationContributorRegistered` | 403 | No payment processor integration is registered for processor '{ProcessorKey}'. Please contact support. A processor profile was selected, but no authorization contributor is registered for the processor key. This typically means the processor integration is not installed or configured. |
| `UserAuthenticationFailed` | 404 | The user the request named could not be resolved. |
| `VALIDATION_FAILED` | 400 | The transaction failed validation. |

### Transactions

| Code | Status | Message |
| --- | --- | --- |
| `ACH_REPEAT_AMOUNT_REQUIRED` | 400 | A Repeat against a bank-account (ACH) transaction needs an amount greater than zero, because a repeat of a check tender is always a fresh debit. Re-submit with the amount to debit. |
| `ADJUSTMENT_TYPE_NOT_SUPPORTED` | 409 | The merchant's active processor does not support the adjustment the request asked for (OfflineAdjustment or IncrementalAuthorization). Submit an adjustment the processor supports, or route the merchant to one that offers the shape you need. |
| `Decryption:InvalidPayload` | 400 | The encrypted payload is missing the key serial number or the encrypted track data needed to decrypt it. The request is marked as encrypted with a DUKPT or ONGUARD encryption type but carries no key serial number or no encrypted track data the gateway could decrypt. Send the key serial number and the encrypted track 2 (or track 1) data. |
| `Decryption:InvalidResult` | 409 | The payment encryption provider returned no usable cleartext for the payload. The payment encryption provider answered, but the answer carried no usable cleartext. Retry the swipe; if it recurs, the key configured for this key serial identifier does not match the reader's injected key. |
| `Decryption:KeyDeactivated` | 403 | The key serial number matches a key that has been deactivated for the merchant. The key serial number on the request matches a key serial identifier the merchant has deactivated. Reactivate the key, or re-inject the reader with an active key. |
| `Decryption:ProviderAuthenticationFailed` | 409 | The payment encryption provider refused the gateway's credentials. The payment encryption provider refused the gateway's credentials. This is a configuration fault on the gateway or merchant side, not something the caller can correct. |
| `Decryption:ProviderNotConfigured` | 403 | The merchant's payment encryption provider cannot decrypt this payload: the provider does not support the scheme, or the matched key is not fully configured. The request carried a DUKPT or ONGUARD payload and the merchant's payment encryption provider cannot serve it: the provider does not support the scheme, or the matched key has no key reference on file. Complete the merchant's payment encryption configuration. |
| `Decryption:ProviderRejected` | 409 | The payment encryption provider refused to decrypt the payload. The payment encryption provider refused the decryption. The provider's own result code is recorded for the merchant's support contact; the payload cannot be retried as sent. |
| `Decryption:Timeout` | 429 | The payment encryption provider did not respond in time. Retry the request. The payment encryption provider did not answer within the merchant's decryption timeout. Retry the request; no configuration change is needed. |
| `Decryption:UnknownKeySerialIdentifier` | 403 | The key serial number does not match any key serial identifier configured for the merchant. The key serial number on the request does not match any key serial identifier in the merchant's key table. Add the reader's key serial identifier to the merchant's payment encryption configuration. |
| `OPERATION_IDEMPOTENCY_KEY_CONFLICT` | 409 | The operation IdempotencyKey was already used on this transaction for a different OperationType. A key identifies one logical operation attempt, so use a fresh key for the new operation. |
| `OPERATION_IDEMPOTENCY_STORE_UNAVAILABLE` | 429 | The operation-idempotency dedupe store could not be consulted or updated. The operation is rejected rather than executed without dedupe protection. Retry the operation with the same key. |
| `OPERATION_IN_PROGRESS` | 409 | The same operation is already in flight for this transaction and IdempotencyKey. Poll the transaction status for the final outcome rather than re-submitting. |
| `OPERATION_NOT_ALLOWED_IN_STATE` | 409 | The transaction's current state does not allow the requested operation. The error carries operationType, allowedActions, currentStage and settlementStatus under Data. Submit one of the allowed operations, or wait for a stage where this one applies. |
| `OPERATION_TARGET_NOT_FOUND` | 404 | The transaction the operation targets does not exist, or is not visible to this caller. Check the merchant and transaction identifiers. |
| `OfflineAdjustment.FieldsRequired` | 400 | An OfflineAdjustment arrived with nothing to apply: either no OfflineAdjustmentFields bundle, or one in which every field is empty. Populate at least TipAmount. |
| `OfflineAdjustment.TipOnlyUntilWpg20_2682` | 400 | An offline adjustment can only change the tip today. Submit TipAmount on its own; any other field in the bundle is refused. Deprecated: responses also carry OfflineAdjustment.TipOnly in error.data.successorCode, which replaces this code in error.code in a later release. Match on either value. |
| `REVERSAL_AMOUNT_EXCEEDS_REMAINING` | 409 | The requested reversal is larger than the transaction's remaining reversible balance. |
| `REVERSAL_FULLY_CONSUMED` | 409 | The authorisation has already been reversed in full; there is nothing left to reverse. |
| `Transactions:AchAuthorizationEvidenceRequired` | 400 | This ACH payment needs an authorization on file. Choose how the account holder authorized the debit (phone or signed form) and confirm you obtained it before submitting. A Virtual Terminal Check (ACH) debit was submitted without the required NACHA authorization evidence: the operator did not select a valid TEL / PPD SEC code, or did not attest that the account holder's authorization was obtained. Rejected at the transaction-create boundary (before the transaction document is persisted and before the durable orchestration starts) so a CSR-keyed ACH debit is never taken without a substantiable authorization record. The VT UI gates the submit client-side; this is the authoritative server-side fail-closed guard. |
| `Transactions:AuthCodeMalformed` | 400 | The auth code must be 1 to 20 alphanumeric characters. OriginalTransaction.AuthCode is outside the accepted 1-20 alphanumeric-character bound. Rejected at the boundary so malformed or unbounded input cannot reach processor payloads or persisted state. |
| `Transactions:BatchSequenceMerchantNotFound` | 403 | The merchant could not be found. The merchant named by a batch-sequence request could not be read. |
| `Transactions:BatchSequenceNumberOutOfRange` | 403 | The batch number is outside the range this processor accepts. The requested next batch number falls outside the range the processor's host accepts. |
| `Transactions:BatchSequenceProcessorNotFound` | 403 | This merchant has no active processor profile for the selected processor. The merchant has no active processor profile for the processor a batch-sequence request named. |
| `Transactions:CaptureNotAllowedOnZeroAuth` | 409 | Capture is not allowed on a zero-amount authorization. Zero-dollar authorizations verify the card only and cannot be captured or settled. A capture was requested against a zero-amount authorization. Zero-dollar authorizations are verification-only and can never be captured or settled. |
| `Transactions:CardNotAcceptedByMerchantPolicy` | 400 | This merchant does not accept this type of card. Please use a different card. The merchant's card acceptance policy refuses the card the request presented. No transaction is created and nothing is sent to a processor. Opt-in and per merchant. The card is judged from its BIN on three independent axes: funding source (credit, debit, prepaid), issued entity (consumer, commercial, GSA) and issuer country. A merchant that has configured no policy accepts every card and never produces this code. The response carries a reason datum naming the axis that refused the card (FundingSource, IssuedEntity, IssuerCountry or UnknownBin) and no card data of any kind. Resolution: present a different card, or ask the merchant to widen the policy. Retrying the same card is refused again. |
| `Transactions:CashAmountExceedsMerchantPolicy` | 400 | This cash sale is above the largest cash sale this merchant accepts. Take the payment by another method. A cash sale is above the largest cash sale the merchant's cash policy allows. No transaction is created. Opt-in and per merchant: a merchant with no maximum set never produces this code, and a sale equal to the maximum is accepted. The response carries a reason datum of AmountExceedsMaximum and does not state the maximum. Resolution: take the payment by another tender, or ask the merchant to raise the maximum. |
| `Transactions:CashRegisterRequiredByMerchantPolicy` | 400 | This merchant requires a register on every cash sale. Select the register the cash was taken into and submit again. A cash sale names no till (register) and the merchant's cash policy requires one. No transaction is created. Opt-in and per merchant: a merchant that does not require till tracking never produces this code. The response carries a reason datum of RegisterRequired. Resolution: submit the sale again with a register whose id is set. |
| `Transactions:CashSourceNotAllowedByMerchantPolicy` | 400 | This merchant accepts cash only at the Virtual Terminal. Record the cash sale there, or take the payment by another method. A cash sale was submitted from somewhere other than the Virtual Terminal, and the merchant's cash policy takes cash at the Virtual Terminal only. No transaction is created. Opt-in and per merchant: a merchant that allows cash from every source (the default) never produces this code. The response carries a reason datum of SourceNotAllowed. Resolution: record the cash sale at the Virtual Terminal, or ask the merchant to allow cash from other sources. Retrying from the same source is refused again. |
| `Transactions:CashTenderDeclarationConflictsWithPaymentData` | 400 | A cash transaction cannot carry a payment method. Remove CardData, CheckData, and TokenData, or remove the cash tender declaration. Raised when a request declares tender: Cash while also carrying a payment instrument (card, check, or token). A cash transaction carries no instrument, so the two statements contradict each other and the request is refused rather than resolved in favour of either. |
| `Transactions:CashTenderNotConfiguredForMerchant` | 400 | This merchant is not configured to accept cash payments. Enable a cash processor profile on the merchant account, or submit the transaction with cardData, checkData, or tokenData instead. Raised when a request explicitly declares a cash tender for a merchant that has no active cash-capable processor profile. |
| `Transactions:CashbackNotAllowedForTender` | 400 | Cashback is available on EBT Cash only. Cashback is permitted on EBT Cash only; EBT SNAP and eWIC reject any cashback amount. |
| `Transactions:CashbackNotAllowedOnOperation` | 400 | Cash back is only available on a sale. Cash back is only permitted on a sale. A return, an authorization-only, a repeat sale, a force, or a follow-up operation cannot hand cash to the cardholder. |
| `Transactions:CashbackRequiresCardPresent` | 400 | Cash back requires a card-present entry mode (swipe, chip, contactless, or fallback). Cash back requires a card-present entry mode (swipe, chip, contactless, or fallback). A keyed or otherwise card-not-present entry has no till to hand cash from. |
| `Transactions:CashbackRequiresPin` | 400 | Cash back requires a PIN debit purchase; supply the PIN block. Cash back on a credit / debit card requires a PIN: it is a PIN debit network transaction, and a card with no PIN block authorizes on the credit rail, which has no cash-back construct. |
| `Transactions:CertificationOverridesNotPermitted` | 403 | Processor certification overrides are not permitted for this merchant or processor profile. A request supplied ProcessorCertificationOverrides but the merchant or processor profile is not authorised to accept them. Cert overrides are gated by Merchant.IsTest AND the active processor profile's AcceptCertificationOverrides flag, AND the override object's Processor field must match the active processor. Production merchants will always see this rejection: overrides are a cert-tooling-only escape hatch. |
| `Transactions:ConvenienceFeeDisclosureEvidenceRequired` | 400 | A transaction that charges a convenience fee must record when and where the fee was disclosed and accepted. Send convenienceFeeDisclosureAcknowledgedAt (the UTC time the payer accepted the disclosed fee) and convenienceFeeDisclosureChannel (one of: VirtualTerminal, HostedPaymentPage, Api). A transaction charging a non-zero convenience fee on a card tender was submitted without complete disclosure evidence: the UTC time the payer accepted the disclosed fee, and the channel the disclosure was shown on. The fee is only permitted where the payer was told about it and chose to pay it, so the acknowledgment is the compliance artifact behind every fee the platform charges; a fee nobody claims to have disclosed cannot be substantiated later. The Virtual Terminal and Hosted Payment Page produce the evidence themselves, so this code is what a caller posting straight to the transaction API sees. Also returned when the supplied evidence is present but not usable: a channel outside the recognized set, or an acknowledgment timestamp materially in the future. An ACH / e-check debit is out of scope (the card-only tender gate governs whether a fee belongs there at all), and an EBT / eWIC benefit tender is already rejected by ConvenienceFeeNotAllowedForTender. |
| `Transactions:ConvenienceFeeNotAllowedForTender` | 400 | A convenience fee cannot be charged on an EBT or eWIC tender. Convenience fee amounts are not permitted on EBT / eWIC tenders. USDA SNAP regulations prohibit both surcharges and convenience fees on benefit transactions. |
| `Transactions:ConvenienceFeeNotEnabled` | 403 | Convenience fees are not currently enabled. Remove the convenience amount from the request, or ask an administrator to enable convenience fees before submitting a transaction that includes one. A transaction carried a convenience amount greater than zero while the tenant-level convenience-fee launch feature is off. The fee is resolved by the client surfaces rather than computed by the pipeline, so a caller posting straight to the API would otherwise bypass the gated Virtual Terminal / hosted-page surfaces and charge a cardholder a fee the platform is not offering. Operations on an already-processed transaction (Void, Capture, CaptureAll, Adjustment, a linked Return) are exempt, so turning the feature off cannot strand an existing authorization that carried a fee. |
| `Transactions:CreateIdempotencyInProgress` | 409 | A transaction with this idempotency key is still being processed. Retry shortly with the same key to receive the recorded outcome. A create arrived carrying an idempotencyKey whose original create is still running. No second charge is attempted, and the request does not wait for the original to finish: the caller receives this deterministic rejection instead. Resolution: retry shortly with the same key to receive the recorded outcome, or resolve the transaction directly through the lookup-by-idempotency-key endpoint. The key itself is never echoed back. |
| `Transactions:CreateIdempotencyStoreUnavailable` | 429 | The idempotency check could not be completed, so the transaction was not submitted. Retry the request with the same key. The create-idempotency dedupe store could not be consulted or updated. The create is rejected (fail closed) rather than executed without dedupe protection, because an unprotected retry of a create is exactly the double charge the key exists to prevent. Resolution: retry the request with the same key. |
| `Transactions:CvvAliasInvalid` | 409 | The CVV alias cannot be used with this card. It may have expired or belong to a different card or merchant, or the card may not belong to this merchant directly. Nothing was charged. If the alias expired, ask the cardholder to enter the CVV again and submit with the new alias. The CVV alias could not be used with this card: it is unknown, it has expired, it was issued for a different card or merchant, or the card belongs to this merchant only through reseller token sharing. Nothing was charged. Resolution: when the alias expired, have the cardholder enter the CVV again, which returns a new alias, and submit with that; an alias cannot be used with a shared card at all. |
| `Transactions:CvvAliasNotSupported` | 403 | CVV aliases are not available for this merchant. Nothing was charged. Send the request without cvvAlias. CVV aliases are not available for this merchant: the vault proxy is not enabled for it, or not deployed in this environment. Nothing was charged. Resolution: send the request without cvvAlias, or ask for vault routes to be enabled. |
| `Transactions:CvvAliasRequiresPublicTokenReference` | 400 | A CVV alias can only be used on a sale or authorization that charges a stored card by its pt_ reference. Nothing was charged. Send the pt_ reference returned with the alias as tokenData.token, or send the request without cvvAlias. A CVV alias was sent with a token that is not the card's pt_ public reference, or on a request that does not charge a stored card by its token (a force, a repeat sale, an unlinked return). An alias is bound to that reference, so nothing else can carry one. Nothing was charged. Resolution: send the pt_ reference returned with the alias as tokenData.token, or send the request without cvvAlias. |
| `Transactions:CvvAliasStoreUnavailable` | 429 | The CVV alias could not be checked right now. Nothing was charged. Retry shortly with the same alias. The store that holds CVV aliases could not be reached, so the alias could not be checked. Nothing was charged. Resolution: retry shortly with the same alias, which stays valid until it expires. |
| `Transactions:DatawireProfileNotConfigured` | 409 | This processor profile has no processor configuration, so the Datawire ID could not be saved to it. Open the profile, check its configuration, and save the merchant before registering again. A Datawire registration reached the persistence step for a processor profile whose MerchantProcessorConfiguration is absent on the merchant aggregate. Unreachable through the normal path (the same profile was already read and validated through the merchant DTO before anything was sent to Datawire), so this guards a torn write rather than an operator mistake: it exists so the issued id is never silently dropped on the floor. |
| `Transactions:DirectAchVaultUnavailable` | 429 | The bank account could not be saved for this session. Confirm the account holder details, then start the capture again. The operator-keyed direct ACH vault (IHppDirectAchVaultSubmitter) could not bind a customer to vault the bank account against: the resolved HPP tokenization session does not request credential storage, no usable account-holder email was supplied, or the session is otherwise not in a state where a stored-credential customer can be resolved. The operator should confirm the account-holder details and restart the capture. |
| `Transactions:DirectCaptureSessionUnavailable` | 429 | The card capture session is no longer available. Start the capture again. The operator-keyed direct-capture submit (IHppDirectCaptureSubmitter) could not resolve the HPP tokenization session addressed by the supplied short token: the session is missing, expired, or otherwise not in a usable state. The operator should restart the capture so a fresh session is issued. |
| `Transactions:EnhancedDataRequirementsNotMet` | 400 | This merchant requires complete enhanced data on a commercial card. Correct the values named below and submit again. A commercial Visa or Mastercard transaction was submitted with enhanced data that is unlikely to meet the card brand requirements, for a merchant whose Processing.EnhancedDataEnforcement setting is Strict. No transaction is created. Opt-in and per merchant. The gateway accepts this data everywhere else and settles it at whatever rate it earns; a merchant chooses Strict because a commercial-card participation fee makes an unqualifiable sale worse than a refused one. A merchant left at the default never sees this code. The response carries one validation error per finding, each naming the field to correct and carrying the same plain-language message the Virtual Terminal shows. The findings are WinkPG's assessment against the published Visa CEDP and Mastercard Data Rate requirements, not an acquirer determination. Resolution: complete the named fields and re-submit, or ask the merchant to move the setting to Warn. |
| `Transactions:FollowUpCardExpirationMismatch` | 400 | The card expiration does not match the original authorization. The card expiration supplied on a Force does not match the original authorization's. Compared on the effective (year, month) resolved from whichever expiration fields each side supplied, so a request that sends month and year still matches a stored expiration date. |
| `Transactions:FollowUpCardNumberMismatch` | 400 | The card number does not match the original authorization. The card number supplied on a Force does not match the card on the original authorization. Compared on the full PAN when both are available and on the last four digits otherwise. |
| `Transactions:FollowUpMerchantMismatch` | 400 | The original transaction belongs to a different merchant. The referenced original transaction belongs to a different merchant than the follow-up request. Intentionally reported as a validation failure rather than a not-found result so the two cases stay distinguishable to the caller that legitimately owns the transaction. |
| `Transactions:FollowUpOriginalNotAuthorization` | 409 | A force can only be applied to an authorization. A Force (capture) references an original transaction that is not an Authorization. Only an Authorization holds an amount that a capture can draw against. |
| `Transactions:FollowUpPostOperationReadFailed` | 429 | The operation completed, but the transaction could not be read back to build the response. Re-read the transaction rather than submitting it again. A Force or Void submitted through the create path completed on the orchestration, but the transaction could not be re-read to build the response. The create path maps its TransactionDto from committed state rather than from the orchestrator's reply, so a failed re-read leaves nothing authoritative to return. Reported as a codified error instead of mapping the submitted model, which would present pre-operation values as the operation's outcome. The operation itself has already been applied: resolution is to re-read the transaction, not to re-submit. |
| `Transactions:ForceRequestShapeInvalid` | 400 | A force needs an original transaction id, a merchant transaction id, or an auth code, and the two gateway identifiers cannot both be supplied. A Force request does not match either supported shape. It must carry at least one of OriginalTransaction.TransactionId, MerchantTransactionId, or OriginalTransaction.AuthCode, and the two gateway identifiers are mutually exclusive because they name the same prior transaction. |
| `Transactions:HppSessionInvalidTransactionType` | 400 | This transaction type cannot be submitted from a hosted payment page. The HPP-session-bearing anonymous create request supplied a TransactionType that is not part of the allowed cardholder-submit set (Sale / Authorization). Refunds, voids, captures, force-captures and any follow-up shapes are rejected before the AuthZ bypass is committed, since they have no business arriving via the cardholder iframe and would touch downstream code paths that assume an authenticated principal. |
| `Transactions:HppSessionLinkVerificationFailed` | 400 | This payment session could not be verified. Start the payment again. The HPP-session-bearing anonymous create request (CreateFromHppSessionAsync) could not verify that the supplied session was atomically consumed by the supplied transaction id. Returned uniformly for every server-side failure shape (session not found, not in Consumed status, ConsumedByTransactionId mismatch, expired) so an attacker probing the endpoint cannot enumerate session identifiers or differentiate failure modes. |
| `Transactions:InvalidStoredCardExpiration` | 400 | The stored card expiration on this contract is missing or unreadable. Update the contract's payment method. A recurring billing run produced a transaction whose stored card expiration could not be parsed into a valid month/year. Indicates corrupt or unset contract data; the transaction is rejected before it reaches the processor so the failure surfaces as a deterministic app-layer error rather than an opaque processor decline. |
| `Transactions:InvalidStoredCardPaymentToken` | 400 | The stored card payment method on this contract could not be resolved. Update the contract's payment method. A recurring billing run references a PaymentTokenId that cannot be resolved to a valid Card payment token with a usable account number. Surfaces failures from four distinct conditions: the token does not exist, it exists but is not a Card token, ownership verification failed, or the resolved payment details do not contain a non-empty card number. Rejected before the processor call so the failure is a deterministic app-layer error: callers can triage the contract's token reference instead of chasing an opaque "Invalid CardNumber format" decline. |
| `Transactions:InvalidStoredCheckPaymentToken` | 400 | The stored bank account on this contract could not be resolved. Update the contract's payment method. A recurring billing run references a PaymentTokenId on a Check (ACH) contract that cannot be resolved to a valid Check payment token with a usable account number. Surfaces failures from the same conditions as InvalidStoredCardPaymentToken (token missing, wrong token type, ownership failure, or empty/masked account data) but for the ACH token resolver. Rejected before the processor call so the failure is a deterministic app-layer error rather than an opaque VeriCheck "Invalid Account Number" decline. |
| `Transactions:KeyedCardEntryNotSupportedByProcessor` | 403 | This merchant's processor requires card data captured by a reader, so a manually keyed card cannot be charged. A manually keyed card charge was submitted for a merchant whose every active card-capable processor profile runs on a processor that can only accept card data a physical reader captured (encrypted tracks, a device-encrypted EMV payload, or a stored reader token). A keyed PAN carries no ciphertext for such a processor to decrypt, so the charge can never be authorized as submitted; it is rejected at the boundary rather than run through the whole create / validate / enrich / screen pipeline only to fail at authorization with a message that reads as a bug ("no card is present" when the caller supplied one). Fail-open by construction: a merchant with any keyed-capable card profile, a processor that does not advertise the requirement, and a capability lookup fault all permit the charge. Reader-captured submissions, reader-token and vault-token charges, wallet charges, stored-payment snapshots, and operations on an already-processed transaction (Void, Capture, CaptureAll, Adjustment, a linked Return) are never gated. |
| `Transactions:LinkedRefundInstrumentMismatch` | 400 | The payment instrument does not match the original transaction's. A refund that names an original transaction carried a card, a stored payment token, or a bank account that is not the one the original transaction was paid with. A linked refund is paid to the original's payment instrument, so send the refund with no payment data, or with the original's. A digital wallet or card-reader token is refused here too, because the gateway cannot read the card it stands for and so cannot confirm it is the same one. |
| `Transactions:MerchantCannotProcessCheck` | 403 | This merchant cannot accept eCheck (ACH) payments. No active ACH processor is configured for the merchant. A Check (ACH / eCheck) transaction was submitted for a merchant that has no active ACH/eCheck-capable processor profile. Rejected at the transaction-create boundary (before the transaction document is persisted and before the durable orchestration starts) so the caller gets a deterministic, client-safe validation error instead of an orphaned Declined record and a late ProcessorNotConfigured decline from the Transaction Manager. The InitializeContext processor-configuration throw remains as a defense-in-depth backstop for any path that bypasses this gate. |
| `Transactions:MerchantIdRequired` | 400 | A merchant is required for this request. MerchantId is required but was not provided in the request. Applies to admin, reseller, and multi-merchant users who must specify it explicitly. |
| `Transactions:MerchantInitiatedCustomerIdRequired` | 400 | A merchant-initiated charge against a stored payment method requires a customer. Re-submit with the customer that owns the stored payment method. A merchant-initiated transaction supplied a resolvable stored payment method token but no invoiceData.customerId. The stored credential (and therefore the consent that authorizes the charge) is resolved from the token within a customer scope, so the customer binding is a required precondition. Distinct from WinkPG.StoredCredentialConsents.StoredCredentialConsentsErrorCodes.StoredCredentialConsentRequired on purpose: no consent is consulted on this branch, so the consent code would point the caller at a remediation (capture consent and re-submit) that cannot fix a missing customer id. Resolution: re-submit with invoiceData.customerId set to the customer that owns the token being charged. |
| `Transactions:MerchantNotFoundOrAccessDenied` | 404 | The merchant could not be found, or you do not have access to it. The specified merchant was not found or the caller does not have access to it. Intentionally ambiguous to prevent merchant ID enumeration. |
| `Transactions:NoMerchantsAvailable` | 403 | Your account has no merchants associated with it. The current user has no merchants associated with their account. |
| `Transactions:OperationNotAllowed` | 409 | The operation '{requestedOperation}' is not allowed on a {originalTransactionType} transaction with result '{result}' and settlement state '{settlementState}'. The requested follow-on operation is not allowed against the original transaction's type, result and settlement state. |
| `Transactions:OriginalTransactionIdMalformed` | 400 | The original transaction id is not a valid identifier. OriginalTransaction.TransactionId was supplied but is not a well-formed GUID. Distinguishes a malformed identifier from one that is well-formed but resolves to no transaction (which is reported separately as a not-found result). |
| `Transactions:OriginalTransactionReferenceRequired` | 400 | A void or return needs either the original transaction id or the merchant transaction id. A Void or Return request carries neither OriginalTransaction.TransactionId nor MerchantTransactionId. Both operations act on a prior gateway transaction, so at least one identifier is required to resolve it. |
| `Transactions:PartialApprovalAcknowledgmentUnavailable` | 409 | This decision cannot be applied yet on this gateway build. The partial approval is unchanged and still awaiting a decision; voiding it is available in the meantime. The acknowledgment is the right operation for the transaction's state, and the gateway admits it, but the handler that applies this particular decision is not wired in this build. Surfaced verbatim from the orchestration's own verdict rather than translated into a generic failure: nothing is broken and nothing was applied, so the reduced authorization is still held and still awaiting a decision. Voiding a pending partial approval is unaffected and remains available. |
| `Transactions:PartialApprovalDeferralNotAllowed` | 400 | This merchant does not accept partial approvals, so there is no partial approval to decide. Nothing was charged. Remove deferPartialApprovalAcknowledgment and submit again. A create request asked to decide a partial approval itself (deferPartialApprovalAcknowledgment) for a merchant that does not accept partial approvals. Such a merchant never keeps one, so there would be nothing to decide. Nothing was charged. Resolution: remove deferPartialApprovalAcknowledgment, or turn partial approvals on for the merchant. |
| `Transactions:PartialApprovalNotFound` | 404 | This transaction was not partially approved, so there is no decision to record. An acknowledgment named a transaction that carries no partial approval at all. Distinct from PartialApprovalNotPending, which is a partial approval that existed and has already been decided: only the latter means somebody (or the deadline) got there first. |
| `Transactions:PartialApprovalNotObserved` | 429 | The decision was submitted, but the updated transaction could not be read in time. Refresh the transaction to see the outcome; do not submit the decision again. The acknowledgment was accepted and raised on the parked orchestration, but the transaction did not show a resolved disposition within the bounded wait, so there is no post-acknowledgment state to return. The decision is not lost. It was durably raised before this code is reported, and the orchestration applies it independently of whether anyone is still waiting. Reported rather than answering with the pre-acknowledgment transaction, which would show an operator their decision as still pending. Resolution is to re-read the transaction, never to re-submit: a second acknowledgment answers PartialApprovalNotPending. |
| `Transactions:PartialApprovalNotPending` | 409 | This partial approval has already been decided. It was accepted, voided or moved to a split tender, or its acknowledgment deadline passed and the reduced authorization was voided. Refresh the transaction to see the outcome. An acknowledgment was submitted against a partial approval that is no longer awaiting a decision. It was already accepted, voided or moved to a split tender by another caller, or its acknowledgment deadline elapsed and the gateway voided the reduced authorization. Raised both by the app service's own state gate (read from committed state before anything is sent) and by the orchestration, which is the authoritative answer when the disposition moves in the window between that read and the raise. The pre-check is advisory for exactly that reason; a late acknowledgment is refused rather than quietly applied on top of an outcome the platform already committed. Resolution: re-read the transaction and see what actually happened. |
| `Transactions:PartialApprovalTransactionNotFound` | 404 | Transaction not found. An acknowledgment named a transaction that does not exist, or that the caller cannot see. The lookup is a partition-scoped point read against the resolved merchant, so a well-formed id paired with the wrong merchant answers this rather than confirming the transaction exists under another merchant. |
| `Transactions:PartialApprovalVoidReasonNotPermitted` | 400 | The void reason looks like it contains card or credential data, or a reason was supplied on a decision that does not record one. Describe why the reduced amount was rejected without it. A void acknowledgment carried a reason that looks like it contains card or credential data, or an accept / split-tender acknowledgment carried a void reason at all. The field is operator-entered free text that lands on the transaction document and crosses the durable event boundary, so the "never put card data here" invariant is enforced rather than documented, and a reason supplied on a decision that never records one is named rather than silently dropped. The refusal never echoes the offending text. |
| `Transactions:PartialApprovalVoidReasonTooLong` | 400 | The void reason is too long. Shorten it and submit the decision again. A void acknowledgment carried a reason longer than the accepted bound. The field is a stable gateway-authored code or a short operator note; it is persisted onto the transaction document and carried over the durable event boundary, so it is bounded at the point of entry rather than trusted to be short. Rejected rather than truncated, because silently shortening the justification for releasing a live authorization damages the audit record the field exists to be. |
| `Transactions:PaymentMethodRequiredForNonCashMerchant` | 400 | A payment method is required. Provide cardData, checkData, or tokenData. This merchant is not configured to accept cash payments. An initial Sale, Authorization, or unlinked Return was submitted with no card, check, or token for a merchant that has no active cash-capable processor profile. A tender-less request is not malformed in itself: the platform reads it as a cash transaction, which is a first-class tender the Virtual Terminal offers. What it cannot do is tell "I meant cash" from "I forgot the tender", so the request is accepted only where the merchant is actually configured to take cash. Everywhere else it would previously have been persisted and then rejected late at processor-profile filtering, returning a ProcessorNotConfigured error inside a 200 that reads like a gateway fault. A merchant that is cash-capable is unaffected. Resolution: send cardData, checkData, or tokenData. Also returned for a tender-less submission on the anonymous hosted-payment-page path, which rejects unconditionally: a hosted page collects no cash tender, so a tender-less submit there is always a defect regardless of how the merchant is configured elsewhere. |
| `Transactions:PaymentTokenNotOwnedByCustomer` | 403 | The requested payment token does not belong to the customer. Ownership verification failed. The payment token being charged is bound to a different customer than the one the charge names in invoiceData.customerId. No transaction is created. A token that is bound to no customer, and a charge that names no customer, never produce this code. Resolution: name the customer the token is bound to, or omit the customer. Retrying the same pair is refused again. |
| `Transactions:PaymentTokenNotOwnedByMerchant` | 403 | The requested payment token does not belong to the merchant. Ownership verification failed. The payment token being charged belongs to a different merchant than the one the charge is for, and no token-sharing arrangement lets this merchant use it. No transaction is created. Resolution: charge a token this merchant owns. Retrying the same token is refused again. |
| `Transactions:PayoutRequiresCheckData` | 400 | Payout requires CheckData (bank account routing and account number). Payout is an ACH credit operation; card payouts are not supported. A Payout transaction was submitted without CheckData. Payout is an ACH credit operation that sends funds to the payee's bank account, so it requires bank-account routing and account details carried on CheckData. A card Payout has no processor dispatch path (TSYS falls through to Ccretail.Authorize, which charges the cardholder instead of paying out). Rejected at the boundary so the caller learns which tender to supply. |
| `Transactions:ReceiptMerchantMismatch` | 404 | This receipt belongs to a different merchant. A receipt operation resolved a transaction belonging to a different merchant than the caller's. Rejected rather than served, so a receipt cannot expose cross-merchant data. |
| `Transactions:ReceiptNoEmailAddress` | 400 | No email address is available for this receipt. Supply one with the request. Emailing a receipt was requested with no address to send it to: none was supplied on the request and the receipt snapshot carries no customer email. |
| `Transactions:ReceiptSplitTenderSummaryNotGeneratedPerTransaction` | 400 | A split tender summary receipt covers every payment in the group and is generated automatically when the last payment completes. It cannot be generated for a single transaction. A split tender summary receipt was requested through the single-transaction receipt generation path. The summary covers every tender in a group, so it cannot be built from one transaction; it is generated by the split tender completion path once the group is complete. |
| `Transactions:RecurringBillingNoResolvablePaymentInstrument` | 409 | This recurring contract has no usable payment method. Add a card or bank account to the contract. A recurring billing run produced a transaction that carries no resolvable payment instrument: no card number, no check account, and no payment token. Without this guard the transaction reaches the Transaction Manager, classifies as a Cash tender (the classifier's last-resort default), and fails with the opaque "No active processor profile configured for payment method Cash" message (or, worse, silently records a $0 cash sale if the merchant has a cash processor). Rejected before the processor call so the failure is a deterministic app-layer error: a recurring contract must always resolve to a card or check instrument. |
| `Transactions:RefundAmountExceedsRemaining` | 409 | The refund amount is greater than the amount still refundable on this transaction. The requested refund exceeds the amount still refundable on the original transaction. Follow-ups accumulate on the same transaction document, so the remaining amount accounts for every refund already applied. |
| `Transactions:ReversalAmountExceedsRemaining` | 409 | The reversal amount is greater than the amount still reversible on this transaction. The requested reversal exceeds the amount still reversible on the original transaction. The remaining amount accounts for every reversal already applied to the same transaction document. |
| `Transactions:ReviewDispositionNotObserved` | 429 | The decision was submitted, but the updated transaction could not be read in time. Refresh the transaction to see the outcome; do not submit the decision again. The disposition was accepted and raised on the parked orchestration, but the transaction did not show a resolved hold within the bounded wait, so there is no post-disposition state to return. The decision is not lost. It was durably raised before this code is reported, and the orchestration applies it independently of whether anyone is still waiting. Reported rather than answering with the pre-disposition transaction, which would show the reviewer their approval as still pending. Resolution is to re-read the transaction, never to re-submit: a second disposition on the same hold answers ReviewNotPending. |
| `Transactions:ReviewDispositionReasonNotPermitted` | 400 | The review reason looks like it contains card or credential data. Describe the decision without it. A review disposition carried a reason that looks like it contains card or credential data. The field is operator-entered free text that lands on the transaction document and crosses the durable event boundary, so the "never put card data here" invariant is enforced rather than documented. Rejected rather than redacted, for the same reason an over-long reason is rejected rather than truncated: this is an audit record, and silently rewriting what an operator typed is as damaging as silently shortening it. The refusal never echoes the offending text. |
| `Transactions:ReviewDispositionReasonRequired` | 400 | A reason is required to decline a transaction held for review. A decline was submitted with no reason. Approving a held transaction needs no justification (the payment simply proceeds as the cardholder intended), but rejecting one is an adverse decision the merchant has to be able to substantiate later, so the reason is required on that direction only. |
| `Transactions:ReviewDispositionReasonTooLong` | 400 | The review reason is too long. Shorten it and submit the decision again. A review disposition carried a reason longer than the accepted bound. The field is a stable gateway-authored code or a short operator note; it is persisted onto the transaction document and carried over the durable event boundary, so it is bounded at the point of entry rather than trusted to be short. Rejected rather than truncated, because silently shortening the justification for an adverse decision damages the audit record the field exists to be. |
| `Transactions:ReviewHoldNotFound` | 404 | This transaction was never held for manual review, so there is no decision to record. A review disposition named a transaction that carries no review hold at all: it was never parked for manual review, so there is no decision to record. Distinct from ReviewNotPending, which is a hold that existed and has already been resolved: the remedies differ, and only the latter means "somebody else got there first". |
| `Transactions:ReviewNotPending` | 409 | This transaction is not awaiting a review decision. It was already approved or declined, or its review deadline passed. Refresh the queue to see the outcome. A review disposition was submitted against a hold that is no longer awaiting a decision. The hold was already approved or declined by another reviewer, or its deadline elapsed and the merchant's configured expiry behaviour resolved it. Raised both by the app service's own state gate (read from committed state before anything is sent) and by the orchestration seam's typed conflict, which is the authoritative answer when the hold resolves in the window between the two. The disposition seam has no late arm on purpose: a stale decision is refused rather than quietly applied on top of an outcome the platform already committed. Resolution: refresh the queue and read what actually happened. |
| `Transactions:ReviewTransactionNotFound` | 404 | Transaction not found. A review disposition named a transaction that does not exist, or that the caller cannot see. The lookup is a partition-scoped point read against the supplied merchant, so a well-formed id paired with the wrong merchant answers this rather than exposing that the transaction exists under another merchant. |
| `Transactions:SandboxAchStatusAlreadyInTargetStatus` | 409 | This transaction is already {status}. Advancing it again would publish nothing, because repeat deliveries of the same event are dropped. Use a new transaction to test the same step again. The named transaction already holds the requested status. Refused rather than re-published, because the notification event ids are deterministic and a second delivery would be dropped downstream anyway: answering success would report a delivery that never happened. |
| `Transactions:SandboxAchStatusEnvironmentUnknown` | 403 | Advancing an ACH settlement status is for the sandbox only, and this request did not state which environment it was for. Authenticate with a test API key (sk_test_); a key issued before environments existed has to be rotated first. An ACH status advance was requested by a caller carrying no environment stamp: a cookie session, or an API key minted before environments existed. Distinguished from SandboxAchStatusProductionRefused because the remedy is different: use a sk_test_ key, or rotate a key that predates environment stamping. |
| `Transactions:SandboxAchStatusMerchantUnknown` | 403 | This API key is not tied to a merchant, so there is no transaction to advance. Use a key issued for the sandbox merchant that owns the transaction. An ACH status advance was requested by a caller whose credential names no merchant. The transaction is loaded by a partition-scoped point read against the caller's own merchant, so without the claim there is no partition to read. Raised by an admin or reseller key. |
| `Transactions:SandboxAchStatusNotAchSale` | 409 | This endpoint advances ACH sales. The transaction you named is a {transactionType} and carries no check data. The named transaction is not an ACH sale. The settlement statuses this endpoint writes are the ACH lifecycle's own, and a card transaction reaches its settled state through the batch pipeline instead. |
| `Transactions:SandboxAchStatusNotApproved` | 409 | This ACH sale was not approved, so it has no settlement lifecycle to advance. Send a sale at an approving amount first. The named ACH sale was not approved. A refused sale never had a settlement status written at all, and giving one a lifecycle here would let an integrator certify against a state the platform never produces. |
| `Transactions:SandboxAchStatusNotLoopbackTransaction` | 403 | This transaction was processed by {processor}, not by the sandbox simulator. Only a simulator transaction can have its ACH settlement status advanced by hand. The named transaction is not owned by the loopback simulator. This endpoint fabricates a settlement outcome, so it may only ever move a transaction no real processor has an opinion about. A transaction carrying no processor key at all is refused here too. |
| `Transactions:SandboxAchStatusNotSandboxTransaction` | 403 | This transaction was not taken in the sandbox, so its settlement status cannot be simulated. The named transaction carries no sandbox stamp, or is stamped for the live world. The second belt behind the environment claim: the claim states which world the caller is acting in, and this states which world the transaction was taken in. |
| `Transactions:SandboxAchStatusProductionRefused` | 403 | Advancing an ACH settlement status is for the sandbox only, and this request used a live API key. Real ACH status comes from the processor. Use a test key (sk_test_) against a sandbox merchant. An ACH status advance was requested with a credential the platform stamped for the live world. The simulator drives the real lifecycle publish path, so letting a live key reach it would let an integrator fabricate settlement events for a production merchant's transactions. |
| `Transactions:SandboxAchStatusTargetStatusRequired` | 400 | Name the settlement status to advance to: SettlementSucceeded, SettlementRolledBack, NotEligible, SettlementFailed, Accepted, Verifying, Originated or PartiallySettled. An ACH status advance named no target status. Refused rather than defaulted: the enum's zero member is SettlementSucceeded, so a request that forgot the field would otherwise settle the transaction and report that the caller had asked for it. The boundary validator refuses this first; the app service re-checks because an in-process caller never passes through it. |
| `Transactions:SandboxAchStatusTransactionNotFound` | 404 | No transaction with this id belongs to the merchant this API key was issued for. The named transaction does not exist in the calling credential's merchant partition, or is not visible to this caller. A well-formed id belonging to another merchant answers this rather than disclosing that it exists elsewhere. |
| `Transactions:SandboxAchStatusTransitionNotAllowed` | 409 | An ACH transaction cannot move from {currentStatus} to {targetStatus}. The intermediate statuses only move forward (Accepted, then Verifying, then Originated, then PartiallySettled), a returned transaction is final, and from any other final status only a late return can still land. The ACH lifecycle does not allow the requested move from the transaction's current status. The same rule the production status sync obeys: a return is terminal and is never overwritten, and from any other terminal status only a late return may still land. |
| `Transactions:SandboxCloseCohortTooLarge` | 409 | This merchant has {count} transactions waiting to settle, and the on-demand close handles at most {maximum} in one request. They will settle on the next scheduled close. The merchant's outstanding cohort is larger than the on-demand close will settle in one synchronous request. The close answers only once the batch has cleared, so it is bounded rather than allowed to hold a request open for an unbounded set; the scheduled close has no such bound. |
| `Transactions:SandboxCloseEnvironmentUnknown` | 403 | Closing a batch on demand is for the sandbox only, and this request did not state which environment it was for. Authenticate with a test API key (sk_test_); a key issued before environments existed has to be rotated first. An on-demand batch close was requested by a caller carrying no environment stamp: a cookie session, or an API key minted before environments existed. Distinguished from SandboxCloseProductionRefused because the remedy is different: use a sk_test_ key, or rotate a key that predates environment stamping. |
| `Transactions:SandboxCloseInProgress` | 409 | A batch close is already running for this merchant. It finishes in the request that started it, so wait for that response rather than retrying this one. An on-demand batch close was requested for a merchant that already has one running. The close is read-then-settle, so two concurrent runs would load the same cohort and each settle it, producing overlapping batch rows and duplicate settled events for the same transactions. The second caller is refused rather than queued: the first is synchronous and about to answer, and by the time it does there is nothing left for the second to close. |
| `Transactions:SandboxCloseMerchantUnknown` | 403 | This API key is not tied to a merchant, so there is no batch to close. Use a key issued for the sandbox merchant whose batch you want to close. An on-demand batch close was requested by a caller whose credential names no merchant. The close acts on the caller's own merchant and takes no merchant argument, so without the claim there is nothing to close. Raised by an admin or reseller key, which is not tied to a merchant. |
| `Transactions:SandboxCloseProductionRefused` | 403 | Closing a batch on demand is for the sandbox only. This request used a live API key, and when a production batch closes is scheduled by the platform. Use a test key (sk_test_) against a sandbox merchant. An on-demand batch close was requested with a credential the platform stamped for the live world. When a production batch closes is a scheduling decision this platform owns; the on-demand close exists so a sandbox integrator can reach post-settlement behaviour at all. |
| `Transactions:SettlementAlreadyInProgress` | 403 | A settlement run is already in progress for this merchant. Wait for it to finish, then try again. A settlement was triggered for a processor that already has a batch in progress. The batch in flight has to reach a terminal state before another can be started for the same processor. |
| `Transactions:SettlementBatchNotBlocking` | 409 | This settlement batch is no longer blocking a new run: it is already in {Status} status. An override was requested against a settlement batch that is no longer Pending, so it is not blocking anything and there is nothing to override. |
| `Transactions:SettlementBatchNotFound` | 404 | The settlement batch could not be found for this merchant. The requested settlement batch does not exist, or belongs to another merchant. |
| `Transactions:SettlementOverride:InvalidTarget` | 403 | The requested settlement status is not a valid target for an override. The requested status is not a settlement status an override can target at all. |
| `Transactions:SettlementOverride:NoOp` | 403 | The transaction is already in the requested settlement status, so there is nothing to change. The transaction already carries the requested status, so the override would change nothing. Reported rather than silently accepted so a bulk request tells the caller which of its transactions it actually moved. |
| `Transactions:SettlementOverride:NotEligible` | 403 | This transaction cannot have its settlement status overridden while it is in its current state. The transaction's current state does not admit a settlement override, for example one that has not reached settlement at all. |
| `Transactions:SettlementOverride:ReasonRequired` | 403 | A settlement status override needs a reason. Supply one and submit the change again. The override was submitted without the reason the audit trail records against it. |
| `Transactions:SettlementOverride:SystemOnlyTarget` | 403 | Only the platform can move a transaction into the requested settlement status. Choose a status an operator is allowed to set. The requested status is one only the platform sets as a batch progresses, so an operator cannot move a transaction into it by hand. |
| `Transactions:SettlementProcessorKeyNotActive` | 403 | The processor '{ProcessorKey}' is not active for this merchant. A settlement trigger selected a processor that is not active for the merchant. Caught before the run starts, because the fan-out would otherwise filter to zero groups and report success while nothing settled. |
| `Transactions:SettlementTriggerFailed` | 429 | The settlement run could not be started. Please try again. Starting the settlement workflow failed. The underlying fault is logged with its exception; the caller gets a single deterministic code plus correlation ids. |
| `Transactions:SplitTenderAmountExceedsRemaining` | 409 | The amount is more than the split tender still has to collect. Refresh the transaction and submit no more than the remaining balance. A continuation asked for more than the group still has to collect. The remaining balance is the order total less what the group's tenders have already collected or are collecting. Resolution: re-read the group and submit a continuation for no more than what remains. |
| `Transactions:SplitTenderContinuationAmountInvalid` | 400 | An additional payment on a split tender must be for more than zero. A continuation's amount was zero or negative. A continuation exists to collect part of the remaining balance, so it must ask for a positive amount. |
| `Transactions:SplitTenderContinuationLinkedToOriginal` | 400 | An additional payment on a split tender is a new charge. Remove the original transaction reference and submit it again. A continuation referenced an existing transaction as its original. A continuation is a new charge in the group, never a follow-up operation on another transaction, and it names the group's primary through the route rather than through the request. |
| `Transactions:SplitTenderContinuationNotSale` | 400 | An additional payment on a split tender must be a sale. A continuation was not a Sale. Every continuation tender authorizes and captures in one step, so an authorization-only, return or follow-up shape is refused whatever the group's state. |
| `Transactions:SplitTenderGroupBusy` | 409 | Another payment is already being added to this split tender. Nothing was charged. Wait for it to finish, refresh the transaction, and submit again if a balance remains. Another payment is already being added to the same split tender. Continuations on one group are taken one at a time, because each one is checked against what the group has already collected. Nothing was charged. Resolution: wait for the other payment to finish, re-read the group, and submit again if a balance remains. |
| `Transactions:SplitTenderGroupNotFound` | 404 | Transaction not found. A continuation named a primary transaction that does not exist, or that the caller cannot see. The lookup is a partition-scoped point read against the continuation's own merchant, so a primary that belongs to another merchant answers this rather than confirming it exists. Also the answer when the split tender of a transaction that is not part of one is read. |
| `Transactions:SplitTenderGroupNotOpen` | 409 | This transaction has no split tender open for another payment. The split tender was never started, it is already complete or closed, or the transaction was voided. Refresh the transaction to see what happened. A continuation named a primary transaction that exists but has no split-tender group open for further tenders. The group was never started, it has already been completed or closed, or the primary was voided. Resolution: re-read the primary and see what happened to the group. |
| `Transactions:SplitTenderMaxTendersReached` | 409 | This split tender already has the most payments the merchant allows. The group already holds as many tenders as the merchant's split-tender maximum allows, so no further continuation can be added. A declined or voided tender does not hold a place. |
| `Transactions:SplitTenderNotEnabled` | 403 | Split tender is turned off for this merchant, so nothing was changed or charged. A partial approval awaiting a decision can still be accepted or voided. A split tender was started for a merchant whose split-tender setting is turned off: either on a partial approval, or by declaring a split-tender order total on a create request. Nothing was raised or charged: a partial approval is unchanged and still awaiting a decision, so it can still be accepted or voided. The setting is read from the merchant that owns the transaction. A split tender that was already open when the setting was turned off is not affected and can still collect its remaining balance. |
| `Transactions:SplitTenderPlanMaxTendersTooLow` | 403 | This merchant allows only one payment per order, so a split tender cannot be planned. Nothing was charged. Submit the payment without a split tender order total. A create request declared a split-tender order total for a merchant whose split-tender maximum is a single payment, so no second payment could ever be added. Nothing was charged. Resolution: raise the merchant's split-tender maximum to at least two, or submit the payment without a declared order total. |
| `Transactions:SplitTenderPlanNotFirstPayment` | 400 | Only the first payment of an order can declare a split tender order total. Nothing was charged. Remove the split tender order total and submit again. A create request declared a split-tender order total on a payment that is itself an additional payment on a split tender, or that references another transaction. One order has one split tender, and only its first payment declares the order total. Nothing was charged. Resolution: remove the declared order total. |
| `Transactions:SplitTenderTenderNotSupported` | 400 | This payment method is not accepted for this split tender payment. Use a card, a bank account or cash. A continuation used a payment method that is not accepted as a continuation tender, or a create request declared a split-tender order total on a payment that is not a card (the first payment of a planned split tender is always a card). Card, ACH (check) and cash continuations are accepted. |
| `Transactions:StandaloneTokenCreateIdempotencyInProgress` | 409 | A token with this idempotency key is already being created. Retry shortly; the retry returns the original token. A standalone token create arrived carrying an idempotencyKey whose original create is still running. No second token is minted, and the request does not wait for the original to finish: the caller receives this deterministic rejection instead. Resolution: retry shortly with the same key to receive the original token. |
| `Transactions:StandaloneTokenCreateIdempotencyKeyReused` | 409 | This idempotency key was already used for a different payment method. No token was created. Use a new key, or resend the original payment details unchanged. A standalone token create arrived carrying an idempotencyKey that an earlier create already used for a different payment method. Replaying would hand back a token for the wrong card, so the request is refused and nothing is minted. Resolution: send a fresh key for the new card, or resend the original payment details unchanged. The key itself is never echoed back. |
| `Transactions:StandaloneTokenCreateIdempotencyStoreUnavailable` | 429 | The idempotency check could not be completed, so no token was created. Retry the request with the same key. The create-idempotency dedupe store could not be consulted or updated on a standalone token create. The create is rejected (fail closed) rather than executed without dedupe protection, because an unprotected retry is exactly the duplicate vault record the key exists to prevent. Resolution: retry the request with the same key. |
| `Transactions:StandaloneTokenCreateIdempotencyTokenGone` | 409 | This idempotency key can no longer be resolved to a token, so the original response cannot be returned. Use a new idempotency key to create a new token. A standalone token create arrived carrying an idempotencyKey whose original create produced a token that no longer exists. The original response cannot be reconstructed, and minting under a key whose original cannot be accounted for would be the second vault record the key exists to prevent, so the request is refused. Resolution: use a new idempotency key. |
| `Transactions:StoredCredentialConsentLookupUnavailable` | not an HTTP status | The stored payment method's consent status could not be verified, so the automatic charge was not submitted. It will be retried on the next cycle; a failure that repeats needs investigation. A platform-originated merchant-initiated charge (a recurring-invoice auto-charge or a payment-plan installment) could not verify the stored credential's consent status because the lineage or consent lookup faulted. Not a consent decision: nothing is known about whether the credential is consented, so the charge fails closed, but it is recorded as a retryable decline so the next sweep tries again, and logged at Error so a fault that repeats every cycle is visible to operators. Distinct from WinkPG.StoredCredentialConsents.StoredCredentialConsentsErrorCodes.StoredCredentialConsentRequired, which is the non-retryable answer when the consent state was read and does not permit the charge. Read from payments\[\].declineReasonCode \| installments\[\].lastDeclineReasonCode on the transaction rather than from a response status. |
| `Transactions:SurchargeDisclosureRequired` | 400 | A surcharge was submitted without a disclosure acknowledgment. Confirm the surcharge was disclosed to and accepted by the cardholder (set the surcharge disclosure acknowledgment) before submitting. An API-channel transaction carried a non-zero surcharge but no surcharge disclosure attestation. The API enforcement path fails closed: unlike the Virtual Terminal / Hosted Payment Page (whose UI captures the acknowledgment), a direct integrator must attest that it disclosed the surcharge to the cardholder, so a surcharge submitted without SurchargeDisclosureAcknowledgedAt is rejected before persist / orchestration. |
| `Transactions:SurchargeExceedsPermitted` | 400 | The submitted surcharge exceeds the maximum permitted for this card. Request a surcharge quote and submit the returned amount, or reduce the surcharge, before resubmitting. An API-channel transaction carried a surcharge that exceeds the maximum the gateway's server-side re-evaluation permits for the card / transaction (the lesser of the network, state, and cost-of-acceptance caps). The over-cap surcharge is rejected rather than reduced, so the integrator corrects the value instead of unknowingly over-charging the cardholder. |
| `Transactions:SurchargeIsGatewayComputed` | 400 | The gateway computes the surcharge for this merchant; remove the surcharge amount from the request. A transaction on a non-API channel carried a caller-supplied, non-zero surcharge while the surcharging feature is enabled and the merchant has an active surcharge configuration. For an actively-surcharging merchant the gateway computes the authoritative surcharge post-BIN, so a caller-supplied value is rejected (not silently overwritten) to surface the integrator's disclosure mismatch at the boundary rather than hiding it. The API channel is exempt: a direct integrator may submit an attested, server-re-validated surcharge via the eligibility quote path. Operations on an already-processed transaction (Void, Capture, CaptureAll, Adjustment, a linked Return) are exempt, so an existing authorization that carried a surcharge is never stranded. |
| `Transactions:SurchargeNotAllowedForTender` | 400 | A surcharge cannot be charged on an EBT or eWIC tender. Surcharge amounts are not permitted on EBT / eWIC tenders. |
| `Transactions:SurchargeNotEligibleForTransaction` | 400 | The submitted surcharge cannot be applied to this card. The gateway re-evaluated eligibility and found this transaction is not surchargeable. Remove the surcharge and resubmit. An API-channel transaction carried a non-zero surcharge, but the gateway's server-side re-evaluation found the card / transaction is not eligible to be surcharged (for example a debit or prepaid card, a non-US-issued card, or a state that prohibits surcharging). The integrator's client-computed surcharge is rejected with this distinct code rather than silently zeroed, so the mismatch surfaces at submission. |
| `Transactions:SurchargeNotEnabled` | 403 | Surcharging is not enabled for this merchant. Remove the surcharge amount from the request, or enable surcharging before submitting a transaction that includes one. A transaction-create request supplied a non-zero surcharge amount while the surcharging feature is disabled for the transaction's merchant scope. Rejected at the transaction-create boundary (before the transaction document is persisted and before the durable orchestration starts) so the integrator learns immediately that their request and the merchant's feature state disagree, rather than a disclosed fee silently flowing through to the processor. Fails closed on writes; persisted surcharge values on responses are left untouched. Linked follow-up operations that inherit an original transaction's surcharge are exempt. |
| `Transactions:TenderNotEligibleForRecurringBilling` | 400 | EBT and eWIC tenders cannot be used for recurring billing. EBT / eWIC tenders are ineligible for recurring billing contracts. |
| `Transactions:TenderNotEligibleForStoredPayment` | 400 | EBT and eWIC tenders cannot be used with a stored payment method. EBT / eWIC tenders are ineligible for stored-payment replay (token transactions). |
| `Transactions:TenderNotEligibleForWalletPayment` | 400 | EBT and eWIC tenders cannot be used with a digital wallet. EBT / eWIC tenders are ineligible for wallet / network-token flows (Apple Pay, Google Pay, etc.). |
| `Transactions:TenderOperationNotAllowed` | 400 | This operation is not permitted for the selected tender. The requested TransactionType is not permitted for the tender (per the allowed-operations matrix). |
| `Transactions:TenderRequiresCardPresent` | 400 | This tender requires a card-present entry mode. EBT SNAP / EBT Cash / eWIC require a card-present entry mode (swipe / chip / contactless / fallback). Raised for manual-keyed transactions on these tenders unless the processor configuration explicitly permits manual entry. |
| `Transactions:TenderRequiresPin` | 400 | This tender requires the cardholder PIN. EBT SNAP / EBT Cash / eWIC require the cardholder PIN per program rules. Raised when the request omits PIN data (HasPin not set or false). |
| `Transactions:TenderRequiresValidEntryMode` | 400 | The entry mode could not be determined, and this tender requires a valid card-present entry mode. The submitted entry mode could not be classified (Unknown / NotApplicable) while the tender requires a valid card-present entry mode. |
| `Transactions:TenderTypeImmutableOnChainedTransaction` | 409 | The tender type cannot change on a follow-up transaction. Omit it, or send the same value as the original. A chained transaction (Capture / Refund / Void / Reversal) supplied a CardTenderType that differs from the original transaction's tender. Tender is immutable across the chain: the child must either omit the field (defaults to inheritance) or supply the same value as the original. |
| `Transactions:TipNotAllowedForTender` | 400 | A tip cannot be added to an EBT or eWIC tender. Tip / gratuity adjustments are not permitted on EBT / eWIC tenders. |
| `Transactions:TokenMintTenderMissing` | 400 | This transaction has no stored card or bank account to tokenize. Tokens can only be minted from transactions that captured a reusable card or check. Minting a token from a transaction found no reusable stored tender on it: a wallet-only or track-only row carries no persisted account number to vault. |
| `Transactions:TokenRedacted` | 409 | This payment token has been redacted. Its stored card or bank account details were permanently erased, so it cannot be reactivated, regenerated, updated or charged. The requested operation was refused because the token has been redacted. Redaction wipes the vaulted account data and is terminal, so a redacted token can no longer be reactivated, regenerated, updated or charged. Raised by reactivate, regenerate and update; the charge path keeps its existing token-not-accessible outcome. |
| `Transactions:TokenRegenerationFailed` | 429 | The payment token could not be regenerated. Please try again shortly. Token regeneration failed for a reason that is not a business-rule violation. The underlying fault is logged; the caller gets a single deterministic code. |
| `Transactions:TransactionManagerUnavailable` | 429 | Transaction Manager is temporarily unavailable. Please try again shortly. The Transaction Manager Function App is unreachable, unhealthy, or did not respond within the configured operation timeout. Surfaces as a typed business error instead of a generic 500 so HTTP clients (and the Blazor UI) can display a meaningful message and decide whether to retry. |
| `Transactions:UserContextUnavailable` | 403 | Your user context could not be determined, so the merchant could not be validated. Sign in again and retry. Unable to determine the current user context for merchant validation. |
| `Transactions:VoiceAuthForceCheckDataNotPermitted` | 403 | A voice-authorization force cannot carry check data. Voice approval codes apply to card transactions only. A voice-authorization Force was submitted with CheckData. Verbal approval codes are a card-network construct with no ACH equivalent, so the check path is rejected explicitly at the boundary instead of failing later at the processor with a less clear error. |
| `Transactions:VoiceAuthForceRequiresCardData` | 400 | A voice-authorization force needs the card data supplied with the request. A voice-authorization Force (an AuthCode with no prior-transaction reference) was submitted without CardData. Verbal approval codes are issued against a card, so the card data must accompany the request rather than being inherited. |
| `Transactions:VoucherClearRequiresEbtTender` | 400 | A voucher clear applies to EBT SNAP and eWIC tenders only. A VoucherClear was submitted against something other than an EBT SNAP or eWIC card tender. The offline paper-voucher program exists for food stamps and eWIC only: EBT Cash has no paper voucher, and credit/debit has no benefit voucher at all. Reported separately from TenderOperationNotAllowed so the caller learns the operation needs a benefit tender, rather than reading a generic matrix rejection as though the tender they sent were unsupported outright. |
| `Transactions:VoucherClearRequiresVoucherApprovalCode` | 400 | A voucher clear needs the voucher approval code obtained by phone. A VoucherClear was submitted without ebt.voucherApprovalCode. A voucher is only clearable because an approval code was obtained by phone while the terminal was offline; that code is the authorization the clear presents in place of an online approval, and TSYS carries it as VoucherCode. A clear without it has no substantiated approval behind it, so it is rejected at the boundary rather than sent to settle. |
| `Transactions:VoucherClearRequiresVoucherNumber` | 400 | A voucher clear needs the paper voucher number. A VoucherClear was submitted without ebt.voucherNumber. The voucher number is the paper voucher's identity and is what both processors put on the wire (Fiserv EbtGrp.VoucherNum, TSYS VoucherNumber); without it there is nothing to clear, and the handlers would fall back to a placeholder that clears no real voucher. |
| `Transactions:WalletNotEnabled` | 403 | This digital wallet is not currently enabled on the platform. A charge was submitted through a digital wallet whose environment-wide feature toggle (Wallets.ApplePay / Wallets.Paze) is off. The toggle is the platform-level kill switch that sits above the per-merchant wallet binding: while it is off the wallet is withheld from every hosted payment page, so this code is what a caller posting straight to the transaction API sees. Distinct from WalletNotSupportedForCardPresentIndustry, which is a merchant-configuration rejection and not affected by the toggle. Operations on an already-processed wallet transaction (Void, Capture, CaptureAll, Adjustment, a linked Return) are exempt, so turning a toggle off cannot strand an existing authorization. |
| `Transactions:WalletNotSupportedForCardPresentIndustry` | 403 | Digital wallet payments are not supported on this merchant's processor profile, which is configured for card-present processing. A digital wallet charge (Apple Pay, Google Pay, or Paze) was submitted for a merchant whose routed processor profile carries a card-present industry (Retail, Food/Restaurant). Wallet transactions always emit card-not-present authentication fields (the wallet cryptogram travels as CAVV, alongside ECI and XID), which are supported only on the card-not-present processor component, so the charge can never be authorized as submitted. Rejected at the boundary rather than sent to the processor, where it would decline with a message that never names the industry as the cause. The capability layer already hides wallet buttons from the hosted payment pages for these merchants; this code is what a direct API caller sees. |
| `VOID_NOT_APPLICABLE_ZERO_DOLLAR_VERIFICATION` | 409 | A Void was requested against a zero-dollar verification authorization, for example a TSYS PREATH. No funds were held, so there is nothing to release. Carries the same Data context as OPERATION_NOT_ALLOWED_IN_STATE. |
| `initiation_type_required` | 400 | A Repeat request did not declare an InitiationType. Re-submit with InitiationType=CardholderInitiated when the cardholder is present, or InitiationType=MerchantInitiated together with an MITReason. |
| `mit_reason_required` | 400 | A merchant-initiated Repeat request did not carry an MITReason. Re-submit with the reason that matches the charge, for example Recurring, UnscheduledCOF, NoShow, DelayedCharge or Installment. |
| `original_transaction_not_resubmittable` | 409 | The original transaction's decline does not qualify for network resubmission. Only NSF-class declines qualify: InsufficientFunds, ActivityLimitExceeded and ExceedsApprovalAmount. Re-submit as a cardholder-initiated charge, or as a general MIT under a captured consent. |
| `resubmission_amount_mismatch` | 400 | A resubmission has to be for the same amount as the original declined charge. Re-submit at the original amount, or send the request as an ordinary cardholder-initiated or merchant-initiated charge. |
| `resubmission_limit_exceeded` | 409 | The original declined charge has already used its network resubmission allowance, for example 15 attempts within 30 days on Visa. Re-authorize with the cardholder (CIT), or under a stored credential consent (general MIT). |

### Twilio

| Code | Status | Message |
| --- | --- | --- |
| `Twilio:ApiKeyRequiredForWebhookRegistration` | 400 | Set the SendGrid API key before registering the webhook; the gateway needs it to call the SendGrid configuration API. Auto-registration was attempted but no SendGrid API key is configured. |
| `Twilio:SendGridApiKeyNotConfigured` | 403 | The message was not sent because no SendGrid API key is configured. Set the SendGrid API key, then retry. An outbound email send was attempted with no SendGrid API key configured. Terminal: the send cannot succeed and no retry changes that. Raised rather than swallowed so a caller holding a delivery record (the notification email destination) records a failed attempt instead of a silent success for a message that was never sent. |
| `Twilio:SendGridFromAddressMissing` | 403 | The message was not sent because it had no sender address and no default from-address is configured. Set a default from-address, then retry. An outbound email send was attempted with no From address, and none could be resolved from the host's default mailing configuration either. Terminal for the same reason as SendGridApiKeyNotConfigured. |
| `Twilio:SendGridRecipientMissing` | 400 | The message was not sent because it had no To address. Add at least one To recipient, then retry. An outbound email send was attempted with no To address. CC and BCC recipients alone do not make a message sendable. Terminal for the same reason as SendGridApiKeyNotConfigured. |
| `Twilio:SendGridReturnedEmptyPublicKey` | 429 | SendGrid accepted the webhook configuration but returned an empty signed-events public key. Try again, or configure signed events manually in the SendGrid dashboard. SendGrid returned an empty public key after enabling signed events: config did not land. |
| `Twilio:SendGridWebhookRegistrationTimedOut` | 429 | SendGrid did not respond within {budgetSeconds} seconds, so the webhook registration did not finish. Registration is safe to repeat; try again in a few minutes. SendGrid did not answer the webhook registration calls within the registration budget (one SendGrid send timeout for all three calls). Every registration step is idempotent, so retrying later can succeed with no configuration change. The exception carries budgetSeconds. |
| `Twilio:SmsComplianceScopeUnresolved` | 400 | The message was not sent because the merchant could not be determined, so the recipient's opt-out status could not be checked. Try again, or specify the merchant explicitly. The merchant scope required to evaluate merchant-scoped opt-outs could not be resolved (the ambient merchant lookup threw). The send is blocked fail-closed rather than falling back to an unscoped check that could bypass a merchant opt-out. Non-interactive callers should set SmsMessageProperties.MerchantId to supply the scope explicitly. |
| `Twilio:SmsDisallowedLinkDomain` | 400 | The message was not sent because its body links to a web address the messaging policy does not permit. Use a platform-branded link; public link shorteners are blocked by mobile carriers. The message body carries a link on a host the platform link policy refuses: a public URL shortener, or (where an allowlist is configured) a host outside it. Carriers filter A2P traffic containing public shortener links, so the send is refused at authoring time rather than dropped invisibly by the carrier. The exception carries the offending host. |
| `Twilio:SmsNotConfigured` | 403 | SMS sending is not configured. Configure the Twilio SMS settings (Account SID, Auth Token, and either a Messaging Service SID or a From Phone Number) in the settings panel. Twilio SMS is not configured: the Account SID or Auth Token is missing, or neither sender (Messaging Service SID, From Phone Number) is set. Either sender satisfies the check. |
| `Twilio:SmsQuietHoursBlocked` | 429 | The message was not sent because it falls outside the permitted sending window for marketing and reminder messages. Schedule it inside the window, or send it as part of a live customer interaction. The send was refused because it fell outside the quiet-hours sending window on the recipient's clock. Applies to the recurring categories only (Marketing and Reminder); a transactional message is never blocked by this. The exception carries nextAllowedUtc when the next permitted instant is known, so a caller holding a schedule can defer instead of failing. |
| `Twilio:SmsRecipientOptedOut` | 403 | This recipient has opted out of SMS messages from this merchant. The message was not sent. The recipient must opt back in before SMS can resume. The recipient has opted out of SMS for the sending merchant. The send-time compliance gate blocks the message before any Twilio API call. Stable so callers can record a channel-level failure (e.g. suppress the SMS channel on a multi-channel notification) without failing sibling channels. |
| `Twilio:SmsSendFailed` | 429 | Failed to send SMS via Twilio. Please check your Twilio configuration and try again. Twilio API returned an error when attempting to send an SMS. |
| `Twilio:SmsSettingsAreHostOnly` | 403 | Twilio SMS settings are platform-wide and can only be changed from the host context. Sign in to the host to configure them. A Twilio SMS settings save was attempted from a tenant context. The values are host-level: the anonymous inbound webhook and Functions-host sends read them with no ambient tenant, so a tenant-scoped write would never be seen by any reader. |
| `Twilio:WebhookBaseUrlMustBeHttps` | 400 | The public webhook base URL must be HTTPS. SendGrid will not accept HTTP for signed events. Auto-registration was called with a non-HTTPS public base URL. |
| `Twilio:WebhookRegistrationIsHostOnly` | 403 | Registering the SendGrid signed event webhook is a host-only operation. Sign in to the host context to configure the gateway-wide webhook key. Webhook registration was attempted from a tenant context; it is a host-only operation. |

### Usage

| Code | Status | Message |
| --- | --- | --- |
| `USAGE_BUDGET_EXCEEDED` | 429 | The caller's plan has no budget left for this operation. |
| `Usage:ConcurrencyConflict` | 409 | A concurrency conflict occurred while updating usage aggregates. Please retry. An optimistic concurrency conflict occurred during aggregate update. |
| `Usage:ExceedsResellerEntitlement` | 403 | Requested quantity ({RequestedQuantity}) exceeds the reseller's entitlement for SKU '{SkuCode}' (limit: {ResellerLimit}). A merchant entitlement requested more quantity than the reseller above it is entitled to. |
| `Usage:InvalidScope` | 400 | The supplied entitlement scope value is not valid. The supplied EntitlementScope value is outside the defined enum range. |
| `Usage:MerchantAndResellerIdRequired` | 400 | ResellerId and MerchantId are both required for the {Scope} scope. A merchant-scoped entitlement was supplied without both the merchant and the reseller that merchant sits under. Merchant scope is nested inside reseller scope, so both are required. |
| `Usage:PrerequisiteNotMet` | 409 | SKU '{SkuCode}' requires prerequisite SKU '{PrerequisiteSkuCode}', which is not entitled at the caller's scope. The consumed SKU declares a PrerequisiteSkuCode that has not been met by the caller. Met = the prerequisite SKU has a non-disabled effective entitlement at the caller's scope. |
| `Usage:ResellerIdRequired` | 400 | ResellerId is required for the {Scope} scope. A reseller-scoped entitlement was supplied without the reseller it is scoped to. |
| `Usage:ScopeNotPermitted` | 403 | The requested reseller/merchant scope is not accessible from your operating context. The caller's operating context does not match the requested reseller/merchant scope. |
| `Usage:SkuDisabled` | 403 | SKU '{SkuCode}' is disabled for this scope. The SKU is disabled for the current entitlement scope. |
| `Usage:SkuNotFound` | 404 | SKU '{SkuCode}' is not registered. The requested SKU code is not registered in the system. |
| `Usage:UsageLimitExceeded` | 403 | Usage limit exceeded for SKU '{SkuCode}'. Current: {CurrentUsage}, Limit: {Limit}. Consumption would exceed the hard limit for this SKU. |

### Vault Proxy

| Code | Status | Message |
| --- | --- | --- |
| `VaultProxy:AmbiguousProperty` | 400 | A property in the request body appears more than once. A property on a filter path appears twice in one object, by the same name or by names that differ only by case, so the route cannot tell which one the upstream reads. Answered 400. |
| `VaultProxy:BodyTooLarge` | 413 | The request body is larger than the vault route allows. The body is larger than the route's cap. Answered 413 before the body is parsed. |
| `VaultProxy:CaptureRouteNotFound` | 404 | The vault route was not found. No enabled inbound route answers to the public id, or the vault proxy feature is off for the route's tenant. One code for every case, so the answer cannot be used to tell them apart. Answered 404. |
| `VaultProxy:CvvAliasStoreUnavailable` | 503 | The card could not be captured. Try again. The short-lived CVV alias store could not be written, so the capture was refused rather than forwarding a card without a usable CVV alias. Nothing is forwarded and no token is minted. Answered 503. |
| `VaultProxy:CvvWithoutCardNumber` | 400 | The request carries a CVV but no card number. The body carries a CVV but no card number, so there is no card to bind the CVV alias to. Answered 400. |
| `VaultProxy:InvalidCardNumber` | 400 | The card number is not valid. The value at the route's card number path is not a 13 to 19 digit number that passes the Luhn check. Answered 400. |
| `VaultProxy:InvalidCvv` | 400 | The CVV is not valid. The value at the route's CVV path is not a 3 or 4 digit number. Answered 400. |
| `VaultProxy:InvalidUpstreamPath` | 400 | The request path is not valid. The path after the route id contains a dot segment or a double-dot segment, or an encoded slash or backslash. Answered 400. |
| `VaultProxy:MalformedBody` | 400 | The request body is not valid JSON. The body is not well-formed JSON. Answered 400. |
| `VaultProxy:MerchantIdIsImmutable` | 409 | A vault route's merchant cannot be changed. A route's owning merchant cannot be changed after it is created. |
| `VaultProxy:OriginNotAllowed` | 403 | This origin is not allowed to post to the vault route. The request named an Origin the route does not allow. Answered 403. |
| `VaultProxy:OriginRequired` | 403 | The request must carry an Origin header. The request carried no Origin header and the route does not accept native clients. Answered 403. |
| `VaultProxy:RouteNotFound` | 404 | The vault route was not found. The route was not found, is not visible to the caller, or the vault proxy feature is off for the tenant. One code for all three, so the answer cannot be used to tell them apart. |
| `VaultProxy:UnsupportedMediaType` | 415 | The vault route accepts JSON request bodies only. The body is not JSON. Only JSON bodies are captured. Answered 415. |
| `VaultProxy:UnvaultedCardNumber` | 400 | The request body carries a card number in a field the vault route does not protect. The body carries a card number at a path the route does not vault. Nothing is forwarded and no token is minted. Answered 400. |
| `VaultProxy:UpstreamTimeout` | 504 | The merchant's server did not answer in time. The upstream did not answer within the route's timeout. The request was sent once and is never retried. Answered 504. |
| `VaultProxy:UpstreamUnavailable` | 502 | The merchant's server could not be reached. The upstream could not be reached: a connection, TLS or DNS failure, or an address the proxy refuses to connect to. Answered 502. |
| `VaultProxy:VaultUnavailable` | 503 | The card could not be captured. Try again. The card vault could not be read or written. Nothing is forwarded. Answered 503. |

## See also

- [All documentation](https://devportal.demo.winkpg.io/llms.txt): the machine-readable index of every public page on this site.
