> For the complete documentation index, see [llms.txt](https://docs.omni.integratedcommerce.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.omni.integratedcommerce.io/api-reference/tokens.md).

# Tokens

Endpoints for creating and removing tokenized payment methods

## Create Token (Card Present)

> Tokenize a card using a physical terminal. A $0 authorization will occur to validate the card. If valid, the card and expiration date will be tokenized and stored and a paymentMethodId will be returned for future Card On File transactions.\
> \
> \#### Webhook Events\
> \
> The following webhook events will be triggered during the tokenization process:\
> \- \`auth.completed\` - when the $0 authorization is processed and completed.\
> \- \`token.created\` - when a card is tokenized and stored as a payment method.<br>

````json
{"openapi":"3.1.0","info":{"title":"Integrated Commerce Omni-Channel Payment API","version":"2026.08.03"},"tags":[{"name":"Tokens","description":"Endpoints for creating and removing tokenized payment methods"}],"servers":[{"url":"https://api.omni.integratedcommerce.io/v1"}],"security":[{"ApiKeyHeader":[]}],"components":{"securitySchemes":{"ApiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key","description":"All API requests must be authenticated using an API key. The API key should be included in the `x-api-key` header of each request.\n\n### API Key Format\n\n```\nx-api-key: <api-key>\n```\n\n### Resource Scope\n\nAn API key grants access only to the resources owned by its merchant. Every resource identifier you supply - transaction IDs, account holder IDs, payment method tokens, subscription IDs, settlement IDs - is resolved within the scope of the merchant that owns the API key.\n\nSupplying an identifier that belongs to a different merchant produces the same not-found response as an identifier that does not exist at all. Responses do not distinguish between the two cases, so a resource ID cannot be used to determine whether it exists under another merchant.\n\n### Environment-Specific Keys\n\nSeparate API keys identify the sandbox and production environments. The base URL for both environments is the same (`https://api.omni.integratedcommerce.io/v1`), but the keys are different.\n\n#### Sandbox Environment\n- Format: `key_test_<ULID>.<secret>`\n- Use this key for development and testing\n\n#### Production Environment\n- Format: `key_live_<ULID>.<secret>`\n- Use this key for live production traffic\n\nSend the complete key, including the secret after the dot. Legacy keys without a dot remain supported until revoked.\n\n### Error Responses\n\nIf authentication fails because of an invalid or missing API key, you will receive a `401 Unauthorized` response. Check the response message for more details.\n\n## Rate Limiting\n\nIf you receive `429 Too Many Requests`, pause before retrying. Honor `Retry-After` when supplied; otherwise use exponential backoff. The API does not guarantee `X-RateLimit-*` headers.\n"}},"schemas":{"CardOnFileAddRequest":{"type":"object","description":"Represents the request to tokenize a card using a physical terminal.","properties":{"paymentMethodId":{"type":"string","description":"The ID of the payment method (physical terminal) to use for adding a card.\n\n- Example format: `pmt_trm_01JRZPTMTBN41PC3VPQNZ5T3HF`\n- Omit only when the merchant has a configured default for this operation or a single resolvable payment method.\n- Otherwise, provide an explicit payment method ID.\n\nYou can retrieve all available physical payment terminal IDs for your account using the [`GET /payment-methods`](https://fiska.gitbook.io/omni-payment-api/payments/payment-methods) endpoint.\n","maxLength":35},"referenceId":{"$ref":"#/components/schemas/ReferenceIdForTokens"}}},"ReferenceIdForTokens":{"type":"string","maxLength":100,"description":"An optional custom reference ID to tag and track this token operation. Use a unique value per merchant. This field is not an idempotency key and reusing it does not replay the prior response. If not provided, a unique reference ID will be generated automatically by the system.\n"},"CardOnFileAddResponse":{"type":"object","description":"Represents the response to a physical card tokenization request.","properties":{"id":{"type":"string","description":"Unique ID for this card tokenization transaction."},"timestamp":{"type":"string","format":"date-time","description":"Transaction timestamp in UTC"},"type":{"type":"string","enum":["Sale","Auth","Capture","Refund","Void"],"description":"Type of transaction"},"status":{"$ref":"#/components/schemas/TransactionStatus"},"accountHolder":{"allOf":[{"$ref":"#/components/schemas/AccountHolder"},{"type":"object","description":"Account holder information, when available. Contact fields are omitted when they were not collected."}]},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethodDetails"},{"type":"object","description":"The physical terminal used to create the token. Read `transactionResponses[].paymentMethod` for the resulting token.","properties":{"id":{"type":"string"}}}]},"referenceId":{"$ref":"#/components/schemas/ReferenceIdForResponse"},"resultCode":{"$ref":"#/components/schemas/ResultCode"},"resultText":{"$ref":"#/components/schemas/ResultText"},"requestedAmount":{"type":"integer","description":"The requested amount in minor units. Virtual tokenization uses 0; physical tokenization uses a 1-unit authorization."},"approvedAmount":{"type":"integer","description":"The approved amount in minor units. This is 0 for a decline or an outcome that is still in flight. A terminal may adjust the amount, for example by adding a tip."},"transactionResponses":{"type":"array","description":"Individual gateway responses, with receipts under each entry. The array can be empty while processing is incomplete.","items":{"$ref":"#/components/schemas/TransactionResponse"}},"sourceId":{"type":"string","description":"Identifier of the session or other source that initiated the transaction."},"threeDs":{"$ref":"#/components/schemas/ThreeDsAuthentication"},"threeDsChallenge":{"$ref":"#/components/schemas/ThreeDsChallenge"},"balanceAmount":{"type":"integer","description":"The balance that remains to be paid on the transaction. This amount is always an integer in the smallest denomination of the currency (e.g. cents for USD or CAD)."},"invoiceNumber":{"$ref":"#/components/schemas/InvoiceNumber"},"orderNumber":{"$ref":"#/components/schemas/OrderNumber"}}},"TransactionStatus":{"type":"string","enum":["Unknown","Created","Pending","Submitted","Acknowledged","Processing","AwaitingSettlement","Completed","Failed","Abandoned"],"description":"The processing lifecycle, separate from approval or decline. `Pending` waits for customer input; `Submitted`, `Acknowledged`, and `Processing` are in flight; `AwaitingSettlement` waits for a bank transfer outcome. `Completed` can be approved or declined: inspect `resultCode` and `transactionResponses[].responseCode`. `Failed` means processing stopped because of a technical issue. `Abandoned` means the customer did not complete the flow before submission. Both states close the request.\n"},"AccountHolder":{"type":"object","description":"Account holder information associated with the transaction","properties":{"id":{"type":"string","description":"Account holder ID assigned by the API."},"externalId":{"type":"string","description":"External account holder ID assigned by the integrator."},"contact":{"type":"object","description":"Contact information for the account holder. Only returned for Card Not Present transactions.","properties":{"name":{"type":"string"},"countryCode":{"type":"string"},"zipCode":{"type":"string"},"address":{"type":"string"},"state":{"type":"string"},"city":{"type":"string"},"address2":{"type":"string","description":"Additional address information, when supplied."}}}},"required":["id","externalId"]},"PaymentMethodDetails":{"type":"object","description":"The payment method used for this transaction.","properties":{"id":{"type":"string","description":"The unique identifier of the payment method. This ID has a prefix that makes it human-readable (pmt_trm_* for physical terminals, pmt_vrt_* for virtual card terminals, pmt_ach_* for virtual ACH terminals, pmt_tkn_* for tokens, which store a card or an ACH bank account), but applications should always use the `type` field to determine the payment method type rather than parsing this ID."},"dateDeleted":{"type":"string","format":"date-time","description":"The date and time (UTC) when this payment method token was removed from the system using the Remove Token endpoint. This parameter will only be returned if the token was removed/deleted from the system.\n\nThis field will be present in three scenarios:\n- As part of a successful Remove Token endpoint response\n- In token.removed webhook notifications\n- In List Transactions responses for transactions where the associated token was later removed\n\nWhen this field is present, sensitive card details (maskedCardNumber, cardExpDate) will be removed for security purposes, but all other transaction data remains intact.\n"},"type":{"type":"string","enum":["Unknown","Physical","Virtual","VirtualAch","VirtualEft","Token"],"description":"The type of payment method:\n- `Physical` - A physical payment terminal for Card Present transactions\n- `Virtual` - A hosted payment form/iframe for Card Not Present transactions\n- `VirtualAch` - A hosted payment form/iframe for ACH (US) bank transfer transactions\n- `Token` - A stored payment method (a card or an ACH bank account) for card-on-file and recurring transactions\n- `VirtualEft` - A virtual Canadian bank-transfer payment method, when configured.\n- `Unknown` - The payment method type is not known.\n"},"currency":{"type":"string","enum":["USD","CAD"],"description":"The currency of the payment method."},"description":{"type":"string","description":"A human-readable name for the payment method."},"billingContact":{"allOf":[{"$ref":"#/components/schemas/BillingContact"},{"type":"object","description":"Billing contact information. Only returned for tokenized payment methods from Card Not Present transactions."}]},"cardType":{"type":"string","enum":["UNKNOWN","DEBIT","VISA","MASTERCARD","AMEX","DINERS","DISCOVER","JCB","UNIONPAY","MAESTRO","GIFT","CASH","EBT","INTERAC","OTHER"],"description":"The type of card used for this transaction:\n * `UNKNOWN` - The card type is unknown\n * `DEBIT` - Debit Card\n * `VISA` - Visa Credit Card\n * `MASTERCARD` - MasterCard Credit Card\n * `AMEX` - American Express Credit Card\n * `DINERS` - Diners Club Credit Card\n * `DISCOVER` - Discover Credit Card\n * `JCB` - JCB Credit Card\n * `UNIONPAY` - UnionPay Credit Card\n * `MAESTRO` - Maestro Debit Card\n * `GIFT` - Gift Card\n * `CASH` - All-cash Transaction\n * `EBT` - Electronic Benefits Transfer Card\n * `INTERAC` - Interac (Canadian PIN-debit network), Card Present only\n * `OTHER` - Other tender types\n"},"maskedCardNumber":{"type":"string","description":"The masked card number. The format may vary (e.g. ************0011, 4****0011, etc.)"},"cardExpDate":{"type":"string","description":"The expiration date of the card in MMYY format."},"accountType":{"type":"string","enum":["Credit","Debit","Checking","Savings"],"description":"Funding-source classification of this token-type payment method.\n- For card tokens: `Credit` or `Debit`.\n- For ACH tokens: `Checking` or `Savings`.\nMay be omitted for older tokens that pre-date this field, for non-token payment methods,\nor when the source did not provide a classification.\n"},"accountNumber":{"type":"string","description":"Masked bank account number, when available for a bank-account token."}},"required":["id","type","currency"]},"BillingContact":{"type":"object","description":"Billing contact information associated with the payment method. Not returned on card present transactions or on refunds, captures, etc. of card present transactions.","properties":{"name":{"type":"string"},"countryCode":{"type":"string"},"zipCode":{"type":"string"},"address":{"type":"string"},"state":{"type":"string"},"city":{"type":"string"},"address2":{"type":"string","description":"Additional address information, when supplied."}}},"ReferenceIdForResponse":{"type":"string","maxLength":100,"description":"The reference ID for this transaction. This will either be the reference ID provided in the transaction request or, if no value was provided, a value generated automatically by the system. This reference ID can be used for processing captures and referenced refunds by including it in the originalReferenceId field of subsequent transactions.\n"},"ResultCode":{"type":"integer","enum":[0,55,1003,1005,1010,3000,3002,3005,3006,3009,3010,3011,3012,3013,3014,3020,4001,4100,6800,6801,6802,6900,6901,6902,9998,9999],"description":"Result codes:\n* `0` - Successful transaction request. (Check each transactionResponse's responseCode to determine if a transaction was approved or declined, card was tokenized, etc.)\n* `1003` - The payment terminal is busy or unavailable. (If this error persists, the user may need to close and re-open the Payment Application on the terminal or restart the terminal)\n* `1005` - The specified terminal could not be found.\n* `1010` - The transaction request is pending to become transaction.\n* `3000` - An unexpected error occurred. Please try again.\n* `3002` - The transaction could not be completed because the payment terminal could not find the related transaction.\n* `3005` - To resolve this error, power off your payment terminal, wait one minute and retry the transaction. If the error persists, please contact support.\n* `3006` - The transaction could not be completed because the payment terminal is low on battery.\n* `3009` - The transaction could not be completed because of network connectivity issues with the payment terminal. Please restore connectivity and try again.\n* `3010` - The transaction took too long and was cancelled by the application.\n* `3011` - Invalid transaction request. Please consult the documentation for valid transaction requests.\n* `3012` - Invalid transaction request. paymentMethodId is invalid.\n* `3013` - Invalid transaction request. The specified amount appears to be incorrect. Please retry with a smaller amount.\n* `3014` - Invalid amount. Please try your transaction again with an amount greater than $0.\n* `3020` - An unsupported transaction result was received.\n* `4001` - The transaction is being processed. This is returned for asynchronous bank transfer payment methods (e.g. ACH) while awaiting a final outcome.\n* `6800` - 3D Secure authentication was rejected by the issuer's ACS (card auth failed, security failure, stolen card, suspected fraud, etc.). Surfaced on declined CNP transactions when `formConfig.enable3DSecure: true`. See [SDK Error Codes](https://fiska.gitbook.io/omni-payment-api/payment-sdk/error-codes) for guidance.\n* `6801` - 3D Secure authentication could not be completed because of an upstream technical issue (ACS timeout, directory server unreachable). Retry later or, if merchant policy allows, retry with 3DS disabled.\n* `6802` - The card is not enrolled in 3D Secure. Retry without 3DS only if merchant policy allows; approval is not guaranteed.\n* `6900` - The issuer declined the card on the hosted payment form or Payment SDK path. The specific reason is on `transactionResponses[0].responseCode` (10-18) and in `resultText`. See [SDK Error Codes](https://fiska.gitbook.io/omni-payment-api/payment-sdk/error-codes) for guidance.\n* `6901` - The gateway rejected or failed the hosted payment form payment because of validation or a gateway failure rather than an issuer decision. See [SDK Error Codes](https://fiska.gitbook.io/omni-payment-api/payment-sdk/error-codes) for guidance.\n* `6902` - The gateway reported no failure but returned no payment token. See [SDK Error Codes](https://fiska.gitbook.io/omni-payment-api/payment-sdk/error-codes) for guidance.\n* `9998` - The system is not ready to process a transaction.\n* `9999` - An unknown error has occurred. If available, an auxiliary error code will be provided in the message.\n* `1010` - The transaction request is pending customer interaction.\n* `4100` - Cardholder verification is required. Follow `threeDsChallenge.redirectUrl` and reconcile the final transaction outcome before retrying.\n* `55` - The customer cancelled the hosted payment attempt.\n"},"ResultText":{"type":"string","description":"A message describing the result code in more detail."},"TransactionResponse":{"type":"object","description":"A single Sale Transaction may result in multiple Responses.","properties":{"responseCode":{"$ref":"#/components/schemas/ResponseCode"},"authCode":{"type":"string","description":"The authorization code provided by the card issuer, confirming transaction approval."},"amountApproved":{"type":"integer","description":"The amount approved for this transaction response."},"approvedAmountBreakdown":{"$ref":"#/components/schemas/ApprovedAmountBreakdown"},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethodDetails"},{"type":"object","description":"The payment method used for this transaction response."}]},"cardType":{"$ref":"#/components/schemas/CardType"},"avsResult":{"$ref":"#/components/schemas/AVSResult"},"cvvResult":{"$ref":"#/components/schemas/CVVResult"},"accountType":{"type":"string","description":"The funding-source classification associated with the payment method used in this\ntransaction.\n- For credit/debit cards: `Credit` or `Debit`. May be null when the classification is unavailable, including on older transactions.\n- For ACH/EFT bank accounts: `Checking` or `Savings`.\n\nThe immediate hosted ACH response uses lowercase `checking` or `savings`. Retrieved transaction responses normally use `Checking` or `Savings`.\n","enum":["Credit","Debit","Checking","Savings","checking","savings"]},"hostResponseText":{"type":"string","description":"A meaningful text explaining the response code from the host."},"receipt":{"$ref":"#/components/schemas/Receipt"}}},"ResponseCode":{"type":"integer","enum":[0,1,2,10,11,12,13,14,15,16,17,18,19,20,21,50,51,52,53,54,55,56,57],"description":"Response codes:\n * `0` - UNKNOWN\n * `1` - APPROVED - Transaction was approved\n * `2` - PARTIAL_APPROVED - Transaction was partially approved, but not for the full requested amount.\n * `10` - DECLINED - Transaction was declined by the issuer or bank.  For Card-Present, the transaction can also be declined offline by the terminal.\n * `11` - INSUFFICIENT_FUNDS - Rejected due to insufficient funds in the account.\n * `12` - CARD_EXPIRED - The card used for the transaction has expired.\n * `13` - FRAUD_SUSPECTED - The transaction was flagged for potential fraud.\n * `14` - INVALID_PAYMENT_INFO - The provided information is invalid (e.g.: card number).\n * `15` - LIMIT_EXCEEDED - Rejected due to reaching a limit (e.g.: credit limit).\n * `16` - VERIFICATION_REQUIRED - Additional customer verification is required for the transaction to proceed (e.g.: 3D Secure).\n * `17` - AUTHENTICATION_FAILED - Rejected due to incorrect PIN or authentication failure.\n * `18` - POLICY_VIOLATION - Rejected due to a violation of the payment gateway's terms of service or policies.\n * `19` - BATCH_EMPTY - Indicates that the batch for processing is empty.\n * `20` - RECORD_NOT_FOUND - Indicates that the requested record for a transaction was not found (e.g.: to VOID a reference).\n * `21` - ALREADY_VOIDED - Indicates that the transaction has already been voided and cannot be processed again.\n * `50` - NETWORK_ERROR - Rejected due to network or communication issues with the payment gateway.\n * `51` - INVALID_ECR_PARAMETER - Payment Terminal or system-specific errors.\n * `52` - NOT_SUPPORTED - Rejected because the requested operation is not supported.\n * `53` - BUSY_OR_UNAVAILABLE - The Payment Terminal, Payment App or system is busy or unavailable.\n * `54` - CANCELLED - Cancelled by the user or merchant.\n * `55` - USER_CANCELLED - Specific to scenarios where the user cancels the transaction.\n * `56` - TIMED_OUT_ON_USER_INPUT - Failed to complete the transaction in time.\n * `57` - PENDING - The transaction is awaiting an asynchronous outcome.\n"},"ApprovedAmountBreakdown":{"type":"object","description":"A breakdown of approvedAmount, where each amounts approved on the payment terminal are returned separately. Only returned on Sale, Auth and Capture transactions.","properties":{"amountGoodsAndServices":{"type":"integer","description":"The approved amount for goods and services."},"tax":{"type":"integer","description":"The approved tax amount."},"cashback":{"type":"integer","description":"The approved cashback amount."},"tip":{"type":"integer","description":"The approved tip amount."}}},"CardType":{"type":"string","enum":["UNKNOWN","DEBIT","VISA","MASTERCARD","AMEX","DINERS","DISCOVER","JCB","UNIONPAY","MAESTRO","GIFT","CASH","EBT","INTERAC","OTHER"],"description":"The type of card used for this transaction:\n * `UNKNOWN` - The card type is unknown\n * `DEBIT` - Debit Card\n * `VISA` - Visa Credit Card\n * `MASTERCARD` - MasterCard Credit Card\n * `AMEX` - American Express Credit Card\n * `DINERS` - Diners Club Credit Card\n * `DISCOVER` - Discover Credit Card\n * `JCB` - JCB Credit Card\n * `UNIONPAY` - UnionPay Credit Card\n * `MAESTRO` - Maestro Debit Card\n * `GIFT` - Gift Card\n * `CASH` - All-cash Transaction\n * `EBT` - Electronic Benefits Transfer Card\n * `INTERAC` - Interac (Canadian PIN-debit network), Card Present only\n * `OTHER` - Other tender types\n"},"AVSResult":{"type":"string","enum":["A","E","N","R","S","U","G","W","X","Y","Z"],"description":"The AVS (Address Verification Service) result. Typically returned on Card Not Present transactions.\n * `A` - Address matches, ZIP does not. The first five numerical characters contained in the address match those stored at the VIC or issuer's center. However, the ZIP code does not match.\n * `E` - Ineligible transaction.\n * `N` - Neither address nor ZIP matches. Neither the first five numerical characters contained in the address match with those stored at the VIC nor issuer's center nor the ZIP code match.\n * `R` - Retry (system unavailable or timed out).\n * `S` - Card type not supported. The card type for this transaction is not supported by AVS. AVS can verify addresses for Visa cards, MasterCard, proprietary cards, and private label transactions.\n * `U` - Address information unavailable.\n * `G` - Address information unavailable, International - Visa only, The address information was not available at the VIC or issuer's center.\n * `W` - Nine-digit ZIP match, address does not. The nine-digit Postal ZIP code matches that stored at the VIC or card issuer's centre. However, the first five numerical characters contained in the address do not match.\n * `X` - Exact match (nine digit ZIP and address). Both the nine-digit Postal ZIP code as well as the first five numerical characters contained in the address match.\n * `Y` - Address and five-digit ZIP match. Both the five-digit Postal ZIP code as well as the first five numerical characters contained in the address match.\n * `Z` - Five-digit ZIP matches, address does not. The five-digit Postal ZIP code matches that stored at the VIC or card issuer's centre.\n\nNOTE: Transactions can still be authorized if the AVS responses are no match or failure. AVS responses are for merchant information only, and usually do not influence the overall Authorization result. This can vary based on the Issuing Bank.\n"},"CVVResult":{"type":"string","enum":["M","N","P","S","U"],"description":"The CVV verification result. Typically returned on Card Not Present transactions.\n * `M` - CVV Match\n * `N` - CVV No Match\n * `P` - Not Processed\n * `S` - CVV should be on the card but the merchant indicates it is not.\n * `U` - User is unregistered\n\nNOTE: Transactions can still be authorized if the CVV responses are no match or failure. CVV responses are for merchant information only, and usually do not influence the overall Authorization result. This can vary based on the Issuing Bank.\n"},"Receipt":{"type":"object","description":"Receipt lines for this transaction response. Card-present responses can contain printable receipt lines; other responses can return an empty lines array.","properties":{"lines":{"type":"array","items":{"type":"string"},"description":"The receipt lines."}},"required":["lines"]},"ThreeDsAuthentication":{"type":"object","description":"3D Secure (EMV 3DS2) authentication outcome. Populated on transactions initiated with `formConfig.enable3DSecure: true`. All fields (other than `status`) are optional - what the issuer returns depends on whether the transaction was frictionless, challenged, or declined.\n","properties":{"status":{"allOf":[{"$ref":"#/components/schemas/ThreeDsStatus"}],"description":"The authentication outcome to use in your integration. The other `threeDs` fields provide supporting authentication data.\n"},"cavv":{"type":"string","description":"Cardholder Authentication Verification Value. Cryptographic proof that 3DS authentication was performed. Present on frictionless and challenge-completed transactions."},"eci":{"type":"string","description":"E-commerce Indicator. Indicates 3DS liability-shift status per card scheme:\n * `02` / `05` - Fully authenticated (liability shift to issuer).\n * `01` / `06` - Attempted authentication (partial liability shift).\n * `00` / `07` - Not authenticated (no liability shift).\n"},"xid":{"type":"string","description":"3DS1 transaction identifier. Some issuers still return this under 3DS2 for backward compatibility."},"dsTransId":{"type":"string","description":"Directory Server transaction ID (3DS2). Identifies the authentication associated with this card transaction."},"threeDReasonId":{"type":"string","description":"Issuer-provided reason code when 3DS authentication failed. Populated only on declines. Use `status` to handle the outcome and `threeDReason` for the explanation.\n"},"threeDReason":{"type":"string","description":"Human-readable description matching `threeDReasonId`."}}},"ThreeDsStatus":{"type":"string","enum":["Authenticated","Attempted","NotAuthenticated","Failed","Unavailable","NotEnrolled"],"description":"Outcome classification of the 3D Secure check performed on a card transaction. Only present on transactions initiated with `formConfig.enable3DSecure: true`. Use this status to handle the authentication outcome; the other `threeDs` fields provide supporting authentication data.\n\n* `Authenticated` - Issuer fully authenticated the cardholder (ECI `02` / `05`). Full liability shift to the issuer for fraud chargebacks. Covers both frictionless and challenge-completed flows.\n* `Attempted` - Issuer attempted authentication but did not fully authenticate the cardholder (ECI `01` / `06`). Partial liability shift, scheme-dependent.\n* `NotAuthenticated` - 3DS completed but the issuer did not authenticate the cardholder (ECI `00` / `07`). No liability shift.\n* `Failed` - Issuer's ACS rejected the cardholder authentication. Transaction was declined. See `threeDReasonId` for the issuer's reason.\n* `Unavailable` - A technical issue prevented 3DS authentication from completing.\n* `NotEnrolled` - The card is not enrolled in 3DS.\n"},"ThreeDsChallenge":{"type":"object","description":"Present when a token payment requires a cardholder challenge (`resultCode: 4100`).","properties":{"redirectUrl":{"type":"string","format":"uri","description":"Hosted URL where the cardholder completes the challenge."},"followUpToken":{"type":"string","description":"Opaque token identifying the pending challenge."}},"required":["redirectUrl","followUpToken"]},"InvoiceNumber":{"type":"string","maxLength":100,"description":"An optional alphanumeric invoice number for this transaction. If provided in the request, the same value will be populated in the response.\n"},"OrderNumber":{"type":"string","maxLength":100,"description":"An optional alphanumeric order number for this transaction. If provided, the same value will be populated in the response.\n"},"Error":{"type":"object","description":"Represents an error that occurred during the request.","properties":{"code":{"type":"integer","enum":[0,1000,1001,2000,2010,2011,2012,2013,2020,2021,2100,2101,2102,2103,2105,2201,2202,2900,2901,2902,2903,3000,3201,3202,3203,3204,3205,3299,3400,3401,3501,3502,3503,4000,4001,4002,4003,4004,6000,6001,6100,6101,6102,6103,6200,6201,6202,6203,6300,6301,6400,6500,6501,6600,6700,6701,6800,6801,6802,6900,6901,6902,9000,9999],"description":"Numeric error code. These are grouped into ranges for easier identification and troubleshooting.\n\n * `0` - Unknown\n * `1000` - AuthenticationGenericError\n * `1001` - MerchantNotAuthorizedForResource\n * `2000` - RequestValidationGenericError\n * `2010` - InvalidPaymentMethodType - The payment method type is not supported for this operation. This includes: ACH tokens cannot be used for auth, void, or subscription transactions.\n * `2011` - InvalidTransactionType\n * `2012` - InvalidAmount\n * `2013` - PaymentMethodUsageRestricted\n * `2020` - InvalidPaymentMethodOwner\n * `2021` - InvalidAccountHolderOwner\n * `2100` - InvalidAccountHolderInformation\n * `2101` - InvalidCountryCode\n * `2102` - InvalidZipCode\n * `2103` - MissingCountryCodeOrZipCode\n * `2105` - InvalidEmailAddress\n * `2201` - MissingUniqueToken\n * `2202` - TransactionDeclinedError\n * `2900` - RequestValidationUnsupportedPayload\n * `2901` - RequestValidationWebhookMissingTypeAndDmnType\n * `2902` - RequestValidationUnsupportedWebhookTokenization\n * `2903` - RequestValidationUnsupportedWebhookType\n * `3000` - UnknownServerGenericError\n * `3201` - InvalidOrExpiredSession\n * `3202` - FailedToCreateSessionInAdapter\n * `3203` - ErrorWhileTokenizingCard\n * `3204` - FailedToLoadThirdPartySdk\n * `3205` - SessionExpired\n * `3299` - JavaScriptError\n * `3400` - ResourceNotReadyError\n * `3401` - TransactionNotCompleteYet\n * `3501` - CouldNotCreateBillingSubscription\n * `3502` - CouldNotCancelBillingSubscription\n * `3503` - BillingSubscriptionInvalidStatus\n * `4000` - ResourceNotFound\n * `4001` - TokenizedCardNotFound\n * `4002` - PaymentMethodNotFound\n * `4003` - AccountHolderNotFound\n * `4004` - BillingSubscriptionNotFound\n * `6000` - SdkGenericError\n * `6001` - SdkJavaScriptError\n * `6100` - SdkSessionLoadFailed\n * `6101` - SdkSessionExpired\n * `6102` - SdkSessionNotInitialized\n * `6103` - SdkEnvironmentMismatch\n * `6200` - SdkCardFieldsInvalid\n * `6201` - SdkCardTokenizationFailed\n * `6202` - SdkCardInitFailed\n * `6203` - SdkCardProcessingFailed\n * `6300` - SdkAchFieldsInvalid\n * `6301` - SdkAchProcessingFailed\n * `6400` - SdkEftProcessingFailed\n * `6500` - SdkWalletError\n * `6501` - SdkWalletProcessingFailed\n * `6600` - SdkThirdPartySdkLoadFailed\n * `6700` - SdkFormValidationFailed\n * `6701` - SdkBillingValidationFailed\n * `6800` - Sdk3dsAuthenticationFailed - Issuer ACS rejected the 3DS authentication. Only emitted when `formConfig.enable3DSecure: true`.\n * `6801` - Sdk3dsAuthenticationUnavailable - 3DS authentication could not be completed due to an upstream technical issue.\n * `6802` - Sdk3dsNotEnrolled - The card is not enrolled in 3DS.\n * `6900` - SdkCardDeclined - The issuer declined the card on the hosted payment form or Payment SDK path. The specific reason is on `transactionResponses[0].responseCode` (10-18) and in `resultText`.\n * `6901` - SdkCardGatewayError - The gateway rejected or failed the hosted payment form payment because of validation or a gateway failure rather than an issuer decision.\n * `6902` - SdkCardTokenMissing - The gateway reported no failure but returned no payment token.\n * `9000` - UnhandledGenericError\n * `9999` - MaxErrorCode\n"},"status":{"type":"string","enum":["Completed","Rejected","Interrupted","Unknown"],"description":"Execution status of the request sent to the payment gateway.\n\n * Completed - The request completed successfully.\n * Rejected - The request was rejected by the payment gateway. No action or side effects occurred. The transaction can safely be retried.\n * Interrupted - The request was interrupted, and the final status is unknown. Possible side effects may have occurred (e.g., a Sale (Token) that returns Interrupted might still charge the customer, even if an error is returned). Additional checks are required before retrying the transaction.\n * Unknown\n"},"message":{"type":"string","description":"Developer-facing error message."},"traceId":{"type":"string","description":"Unique trace identifier for tracking and debugging this request."},"timestamp":{"type":"string","format":"date-time","description":"The timestamp when the error occurred (UTC)."},"errorDetails":{"type":"array","items":{"$ref":"#/components/schemas/ErrorDetail"}}}},"ErrorDetail":{"type":"object","description":"Represents a validation error that occurred during the request.","properties":{"code":{"type":"integer","description":"Error code"},"field":{"type":"string","description":"The field with the error"},"message":{"type":"string","description":"Error message"},"details":{"type":"string","description":"Additional details about the error"}}}},"responses":{"UnauthorizedError":{"description":"Unauthorized response due to an invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/payment-methods/physical":{"post":{"operationId":"createCardPresentToken","summary":"Create Token (Card Present)","description":"Tokenize a card using a physical terminal. A $0 authorization will occur to validate the card. If valid, the card and expiration date will be tokenized and stored and a paymentMethodId will be returned for future Card On File transactions.\n\n#### Webhook Events\n\nThe following webhook events will be triggered during the tokenization process:\n- `auth.completed` - when the $0 authorization is processed and completed.\n- `token.created` - when a card is tokenized and stored as a payment method.\n","tags":["Tokens"],"requestBody":{"description":"The request to tokenize a card using a physical terminal.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardOnFileAddRequest"}}}},"responses":{"200":{"description":"A successful card tokenization response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardOnFileAddResponse"}}}},"400":{"description":"Request Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}}}}
````

## Create Token (Card Not Present)

> Tokenize a card or ACH bank account using a hosted payment form/iframe. This POST request initiates a tokenization session and returns a short-lived iframe URL to embed in your page, rather than using a static iframe URL.\
> \
> When the customer submits their payment information, they will be redirected to the returnUrl (if provided) or a postMessage event will be sent to the parent window. The complete tokenization result will be delivered via the Token Created webhook.\
> \
> \#### Payment Method Types\
> \
> This endpoint supports different payment method types based on the \`paymentMethodId\` provided:\
> \- \*\*Virtual (Card)\*\*: A $0 authorization will occur to validate the card. If valid, a tokenized card ID will be stored and a paymentMethodId will be returned for future Card On File transactions.\
> \- \*\*VirtualAch (ACH Bank Account)\*\*: The bank account information will be tokenized for future ACH transactions.\
> \
> \#### ACH Tokenization\
> \
> When using an ACH payment method (VirtualAch):\
> \- \*\*No verification is performed\*\* on the bank account during tokenization. The account details are stored as provided.\
> \- Verification of account validity occurs only when a subsequent sale transaction is processed.\
> \- The tokenized ACH payment method can be used for future token sales via the \`/transactions/token-sale\` endpoint.\
> \- ACH tokens have restrictions and cannot be used for authorization, void, or subscription transactions.\
> \
> \#### Account Holder Integration\
> \
> You can optionally provide an \`accountHolder\` object with an \`externalId\` to associate the tokenized payment method with a customer in your system. This is useful for:\
> \- Managing payment methods per customer using your own identifiers\
> \- Querying a customer's saved payment methods later via \`GET /payment-methods/account-holder/{externalId}\`\
> \
> If an account holder with the provided \`externalId\` already exists, it will be used. Otherwise, a new account holder will be created.\
> \
> \#### Webhook Events\
> \
> The following webhook events will be triggered during the tokenization process:\
> \- \`auth.completed\` - when a $0 authorization is processed and completed (card only).\
> \- \`token.created\` - when a card or ACH account is tokenized and stored as a payment method.<br>

````json
{"openapi":"3.1.0","info":{"title":"Integrated Commerce Omni-Channel Payment API","version":"2026.08.03"},"tags":[{"name":"Tokens","description":"Endpoints for creating and removing tokenized payment methods"}],"servers":[{"url":"https://api.omni.integratedcommerce.io/v1"}],"security":[{"ApiKeyHeader":[]}],"components":{"securitySchemes":{"ApiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key","description":"All API requests must be authenticated using an API key. The API key should be included in the `x-api-key` header of each request.\n\n### API Key Format\n\n```\nx-api-key: <api-key>\n```\n\n### Resource Scope\n\nAn API key grants access only to the resources owned by its merchant. Every resource identifier you supply - transaction IDs, account holder IDs, payment method tokens, subscription IDs, settlement IDs - is resolved within the scope of the merchant that owns the API key.\n\nSupplying an identifier that belongs to a different merchant produces the same not-found response as an identifier that does not exist at all. Responses do not distinguish between the two cases, so a resource ID cannot be used to determine whether it exists under another merchant.\n\n### Environment-Specific Keys\n\nSeparate API keys identify the sandbox and production environments. The base URL for both environments is the same (`https://api.omni.integratedcommerce.io/v1`), but the keys are different.\n\n#### Sandbox Environment\n- Format: `key_test_<ULID>.<secret>`\n- Use this key for development and testing\n\n#### Production Environment\n- Format: `key_live_<ULID>.<secret>`\n- Use this key for live production traffic\n\nSend the complete key, including the secret after the dot. Legacy keys without a dot remain supported until revoked.\n\n### Error Responses\n\nIf authentication fails because of an invalid or missing API key, you will receive a `401 Unauthorized` response. Check the response message for more details.\n\n## Rate Limiting\n\nIf you receive `429 Too Many Requests`, pause before retrying. Honor `Retry-After` when supplied; otherwise use exponential backoff. The API does not guarantee `X-RateLimit-*` headers.\n"}},"schemas":{"CardOnFileVirtualAddRequest":{"type":"object","description":"Represents the request to tokenize a card using a hosted payment form / iframe.","properties":{"paymentMethodId":{"type":"string","description":"The ID of the payment method (hosted payment form/iframe) to use for adding a card.\n\n- Example format: `pmt_vrt_01JRZPTWS99Z7RB57Q1CVWSWDS`\n- Omit only when the merchant has a configured default for this operation or a single resolvable payment method.\n- Otherwise, provide an explicit payment method ID.\n\nYou can retrieve all available payment method IDs for your account using the `GET /payment-methods` endpoint.\n","maxLength":35},"referenceId":{"$ref":"#/components/schemas/ReferenceIdForTokens"},"returnUrl":{"type":"string","description":"Will be used to redirect user back to merchant's site after iframe completed or canceled if provided. The URL will include various parameters that are detailed in \"Transaction Result Parameters\" section of the Hosted Payment Form section. If you do not provide a returnUrl, the customer will see either a \"Payment Processed Successfully\" or \"Payment Processing Issue\" screen.","maxLength":1000},"useJavaScriptCallback":{"type":"boolean","description":"At the end of the transaction flow, the user is always redirected to either the returnUrl (if provided), or a default success or issue page. In addition, if useJavaScriptCallback is set to true, the iframe will use JavaScript to post a message to the parent window to notify the merchant's site when the iframe completed. This allows for more control over the user experience on the merchant's site."},"formConfig":{"allOf":[{"$ref":"#/components/schemas/FormConfig"}],"description":"Optional form configuration. Omit to use payment method defaults. If supplied, billingInfoCaptureLevel is required."},"accountHolder":{"allOf":[{"$ref":"#/components/schemas/AccountHolderRequest"}],"description":"Optional external customer identifier. Supported by virtual sale and virtual tokenization; ignored by virtual authorization."}}},"ReferenceIdForTokens":{"type":"string","maxLength":100,"description":"An optional custom reference ID to tag and track this token operation. Use a unique value per merchant. This field is not an idempotency key and reusing it does not replay the prior response. If not provided, a unique reference ID will be generated automatically by the system.\n"},"FormConfig":{"type":"object","description":"Optional form configuration. If omitted, the payment method defaults apply. With no configured default, billing capture is `Full` and 3D Secure is disabled. When you send this object, include `billingInfoCaptureLevel`; omitted optional fields inherit the payment method defaults.\n","properties":{"billingInfoCaptureLevel":{"allOf":[{"$ref":"#/components/schemas/BillingInfoCaptureLevel"}],"description":"Required when formConfig is supplied. Choose Full or Minimal."},"enable3DSecure":{"type":"boolean","description":"Optional. When `true`, the hosted payment form performs 3D Secure (3DS2) cardholder authentication during card tokenization. The issuer decides whether the transaction is authenticated frictionlessly or requires a cardholder challenge; when a challenge is required, it is rendered in an overlay on the hosted payment form and the SDK resumes automatically on completion.\n\nWhen `false`, 3DS is skipped and liability for fraudulent chargebacks remains with the merchant.\n\nThe resulting transaction exposes the authentication outcome in `threeDs.status` and additional authentication data in `threeDs` when available.\n\n**Card payment methods only.** 3DS is a card-network protocol and is not supported for ACH or EFT payment methods - setting `enable3DSecure: true` on a non-card payment method is rejected with a validation error.\n\nWhen omitted, this field inherits the payment method default.\n\n**Recommended:** Combine with `billingInfoCaptureLevel: Full` (the default) to maximize the likelihood of a frictionless flow. Full billing is not required for 3DS to function, but the risk engine uses address, email, and phone to score the transaction.\n"},"piiFields":{"type":"array","description":"Optional additional contact fields to collect. Omitted values inherit the payment method defaults; an empty array requests neither field.","items":{"type":"string","enum":["Email","Phone"]}}},"required":["billingInfoCaptureLevel"]},"BillingInfoCaptureLevel":{"type":"string","enum":["Minimal","Full"],"description":"Controls how much billing information is collected in the hosted payment form.\n * `Minimal` - Collects country and ZIP/postal code in a compact layout below the card fields. **Not supported for ACH payment methods.**\n * `Full` - (Fallback when no payment method default is configured) Collects complete billing address (street, city, state, country, ZIP). Required for ACH payment methods.\n"},"AccountHolderRequest":{"type":"object","description":"Account holder information for associating a transaction or tokenized payment method with a customer.\n\nWhen you provide an `externalId`, the system will either find an existing account holder with that ID or create a new one. This allows you to link payment methods and transactions to your own customer identifiers (e.g., CRM contact IDs, user IDs from your system).\n","properties":{"externalId":{"type":"string","maxLength":255,"description":"Your external identifier for this customer. This can be any unique identifier from your system.\n\nIf an account holder with this external ID already exists for your merchant account, it will be used. Otherwise, a new account holder will be created with this external ID.\n"}}},"CardOnFileVirtualAddResponse":{"type":"object","description":"Represents the response to a hosted payment form / iframe card tokenization request.","properties":{"id":{"type":"string","description":"Identifier of the hosted session request (`ifr_` prefix). This is not a transaction ID."},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethodDetails"},{"type":"object","description":"The payment method used for this transaction.","properties":{"id":{"type":"string"}}}]},"referenceId":{"$ref":"#/components/schemas/ReferenceIdForResponse"},"requestedAmount":{"type":"integer","description":"Always 0 when creating a virtual tokenization session."},"sessionId":{"type":"string","description":"Opaque session token. Pass this exact unprefixed value to the SDK."},"iframeUrl":{"type":"string","description":"URL for the iframe to collect payment information, or null if iframe could not be created due to invalid credentials, etc."},"expirationTimestamp":{"type":"string","format":"date-time","description":"Absolute session expiry in UTC, 10 minutes after creation."},"status":{"type":"string","enum":["Pending","Completed","Expired","Error"],"description":"Status of the iframe session. When created, this will always be pending. The status values will be updated as the customer goes through the iframe process."}}},"PaymentMethodDetails":{"type":"object","description":"The payment method used for this transaction.","properties":{"id":{"type":"string","description":"The unique identifier of the payment method. This ID has a prefix that makes it human-readable (pmt_trm_* for physical terminals, pmt_vrt_* for virtual card terminals, pmt_ach_* for virtual ACH terminals, pmt_tkn_* for tokens, which store a card or an ACH bank account), but applications should always use the `type` field to determine the payment method type rather than parsing this ID."},"dateDeleted":{"type":"string","format":"date-time","description":"The date and time (UTC) when this payment method token was removed from the system using the Remove Token endpoint. This parameter will only be returned if the token was removed/deleted from the system.\n\nThis field will be present in three scenarios:\n- As part of a successful Remove Token endpoint response\n- In token.removed webhook notifications\n- In List Transactions responses for transactions where the associated token was later removed\n\nWhen this field is present, sensitive card details (maskedCardNumber, cardExpDate) will be removed for security purposes, but all other transaction data remains intact.\n"},"type":{"type":"string","enum":["Unknown","Physical","Virtual","VirtualAch","VirtualEft","Token"],"description":"The type of payment method:\n- `Physical` - A physical payment terminal for Card Present transactions\n- `Virtual` - A hosted payment form/iframe for Card Not Present transactions\n- `VirtualAch` - A hosted payment form/iframe for ACH (US) bank transfer transactions\n- `Token` - A stored payment method (a card or an ACH bank account) for card-on-file and recurring transactions\n- `VirtualEft` - A virtual Canadian bank-transfer payment method, when configured.\n- `Unknown` - The payment method type is not known.\n"},"currency":{"type":"string","enum":["USD","CAD"],"description":"The currency of the payment method."},"description":{"type":"string","description":"A human-readable name for the payment method."},"billingContact":{"allOf":[{"$ref":"#/components/schemas/BillingContact"},{"type":"object","description":"Billing contact information. Only returned for tokenized payment methods from Card Not Present transactions."}]},"cardType":{"type":"string","enum":["UNKNOWN","DEBIT","VISA","MASTERCARD","AMEX","DINERS","DISCOVER","JCB","UNIONPAY","MAESTRO","GIFT","CASH","EBT","INTERAC","OTHER"],"description":"The type of card used for this transaction:\n * `UNKNOWN` - The card type is unknown\n * `DEBIT` - Debit Card\n * `VISA` - Visa Credit Card\n * `MASTERCARD` - MasterCard Credit Card\n * `AMEX` - American Express Credit Card\n * `DINERS` - Diners Club Credit Card\n * `DISCOVER` - Discover Credit Card\n * `JCB` - JCB Credit Card\n * `UNIONPAY` - UnionPay Credit Card\n * `MAESTRO` - Maestro Debit Card\n * `GIFT` - Gift Card\n * `CASH` - All-cash Transaction\n * `EBT` - Electronic Benefits Transfer Card\n * `INTERAC` - Interac (Canadian PIN-debit network), Card Present only\n * `OTHER` - Other tender types\n"},"maskedCardNumber":{"type":"string","description":"The masked card number. The format may vary (e.g. ************0011, 4****0011, etc.)"},"cardExpDate":{"type":"string","description":"The expiration date of the card in MMYY format."},"accountType":{"type":"string","enum":["Credit","Debit","Checking","Savings"],"description":"Funding-source classification of this token-type payment method.\n- For card tokens: `Credit` or `Debit`.\n- For ACH tokens: `Checking` or `Savings`.\nMay be omitted for older tokens that pre-date this field, for non-token payment methods,\nor when the source did not provide a classification.\n"},"accountNumber":{"type":"string","description":"Masked bank account number, when available for a bank-account token."}},"required":["id","type","currency"]},"BillingContact":{"type":"object","description":"Billing contact information associated with the payment method. Not returned on card present transactions or on refunds, captures, etc. of card present transactions.","properties":{"name":{"type":"string"},"countryCode":{"type":"string"},"zipCode":{"type":"string"},"address":{"type":"string"},"state":{"type":"string"},"city":{"type":"string"},"address2":{"type":"string","description":"Additional address information, when supplied."}}},"ReferenceIdForResponse":{"type":"string","maxLength":100,"description":"The reference ID for this transaction. This will either be the reference ID provided in the transaction request or, if no value was provided, a value generated automatically by the system. This reference ID can be used for processing captures and referenced refunds by including it in the originalReferenceId field of subsequent transactions.\n"},"Error":{"type":"object","description":"Represents an error that occurred during the request.","properties":{"code":{"type":"integer","enum":[0,1000,1001,2000,2010,2011,2012,2013,2020,2021,2100,2101,2102,2103,2105,2201,2202,2900,2901,2902,2903,3000,3201,3202,3203,3204,3205,3299,3400,3401,3501,3502,3503,4000,4001,4002,4003,4004,6000,6001,6100,6101,6102,6103,6200,6201,6202,6203,6300,6301,6400,6500,6501,6600,6700,6701,6800,6801,6802,6900,6901,6902,9000,9999],"description":"Numeric error code. These are grouped into ranges for easier identification and troubleshooting.\n\n * `0` - Unknown\n * `1000` - AuthenticationGenericError\n * `1001` - MerchantNotAuthorizedForResource\n * `2000` - RequestValidationGenericError\n * `2010` - InvalidPaymentMethodType - The payment method type is not supported for this operation. This includes: ACH tokens cannot be used for auth, void, or subscription transactions.\n * `2011` - InvalidTransactionType\n * `2012` - InvalidAmount\n * `2013` - PaymentMethodUsageRestricted\n * `2020` - InvalidPaymentMethodOwner\n * `2021` - InvalidAccountHolderOwner\n * `2100` - InvalidAccountHolderInformation\n * `2101` - InvalidCountryCode\n * `2102` - InvalidZipCode\n * `2103` - MissingCountryCodeOrZipCode\n * `2105` - InvalidEmailAddress\n * `2201` - MissingUniqueToken\n * `2202` - TransactionDeclinedError\n * `2900` - RequestValidationUnsupportedPayload\n * `2901` - RequestValidationWebhookMissingTypeAndDmnType\n * `2902` - RequestValidationUnsupportedWebhookTokenization\n * `2903` - RequestValidationUnsupportedWebhookType\n * `3000` - UnknownServerGenericError\n * `3201` - InvalidOrExpiredSession\n * `3202` - FailedToCreateSessionInAdapter\n * `3203` - ErrorWhileTokenizingCard\n * `3204` - FailedToLoadThirdPartySdk\n * `3205` - SessionExpired\n * `3299` - JavaScriptError\n * `3400` - ResourceNotReadyError\n * `3401` - TransactionNotCompleteYet\n * `3501` - CouldNotCreateBillingSubscription\n * `3502` - CouldNotCancelBillingSubscription\n * `3503` - BillingSubscriptionInvalidStatus\n * `4000` - ResourceNotFound\n * `4001` - TokenizedCardNotFound\n * `4002` - PaymentMethodNotFound\n * `4003` - AccountHolderNotFound\n * `4004` - BillingSubscriptionNotFound\n * `6000` - SdkGenericError\n * `6001` - SdkJavaScriptError\n * `6100` - SdkSessionLoadFailed\n * `6101` - SdkSessionExpired\n * `6102` - SdkSessionNotInitialized\n * `6103` - SdkEnvironmentMismatch\n * `6200` - SdkCardFieldsInvalid\n * `6201` - SdkCardTokenizationFailed\n * `6202` - SdkCardInitFailed\n * `6203` - SdkCardProcessingFailed\n * `6300` - SdkAchFieldsInvalid\n * `6301` - SdkAchProcessingFailed\n * `6400` - SdkEftProcessingFailed\n * `6500` - SdkWalletError\n * `6501` - SdkWalletProcessingFailed\n * `6600` - SdkThirdPartySdkLoadFailed\n * `6700` - SdkFormValidationFailed\n * `6701` - SdkBillingValidationFailed\n * `6800` - Sdk3dsAuthenticationFailed - Issuer ACS rejected the 3DS authentication. Only emitted when `formConfig.enable3DSecure: true`.\n * `6801` - Sdk3dsAuthenticationUnavailable - 3DS authentication could not be completed due to an upstream technical issue.\n * `6802` - Sdk3dsNotEnrolled - The card is not enrolled in 3DS.\n * `6900` - SdkCardDeclined - The issuer declined the card on the hosted payment form or Payment SDK path. The specific reason is on `transactionResponses[0].responseCode` (10-18) and in `resultText`.\n * `6901` - SdkCardGatewayError - The gateway rejected or failed the hosted payment form payment because of validation or a gateway failure rather than an issuer decision.\n * `6902` - SdkCardTokenMissing - The gateway reported no failure but returned no payment token.\n * `9000` - UnhandledGenericError\n * `9999` - MaxErrorCode\n"},"status":{"type":"string","enum":["Completed","Rejected","Interrupted","Unknown"],"description":"Execution status of the request sent to the payment gateway.\n\n * Completed - The request completed successfully.\n * Rejected - The request was rejected by the payment gateway. No action or side effects occurred. The transaction can safely be retried.\n * Interrupted - The request was interrupted, and the final status is unknown. Possible side effects may have occurred (e.g., a Sale (Token) that returns Interrupted might still charge the customer, even if an error is returned). Additional checks are required before retrying the transaction.\n * Unknown\n"},"message":{"type":"string","description":"Developer-facing error message."},"traceId":{"type":"string","description":"Unique trace identifier for tracking and debugging this request."},"timestamp":{"type":"string","format":"date-time","description":"The timestamp when the error occurred (UTC)."},"errorDetails":{"type":"array","items":{"$ref":"#/components/schemas/ErrorDetail"}}}},"ErrorDetail":{"type":"object","description":"Represents a validation error that occurred during the request.","properties":{"code":{"type":"integer","description":"Error code"},"field":{"type":"string","description":"The field with the error"},"message":{"type":"string","description":"Error message"},"details":{"type":"string","description":"Additional details about the error"}}}},"responses":{"UnauthorizedError":{"description":"Unauthorized response due to an invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/payment-methods/virtual":{"post":{"operationId":"createCardNotPresentToken","summary":"Create Token (Card Not Present)","description":"Tokenize a card or ACH bank account using a hosted payment form/iframe. This POST request initiates a tokenization session and returns a short-lived iframe URL to embed in your page, rather than using a static iframe URL.\n\nWhen the customer submits their payment information, they will be redirected to the returnUrl (if provided) or a postMessage event will be sent to the parent window. The complete tokenization result will be delivered via the Token Created webhook.\n\n#### Payment Method Types\n\nThis endpoint supports different payment method types based on the `paymentMethodId` provided:\n- **Virtual (Card)**: A $0 authorization will occur to validate the card. If valid, a tokenized card ID will be stored and a paymentMethodId will be returned for future Card On File transactions.\n- **VirtualAch (ACH Bank Account)**: The bank account information will be tokenized for future ACH transactions.\n\n#### ACH Tokenization\n\nWhen using an ACH payment method (VirtualAch):\n- **No verification is performed** on the bank account during tokenization. The account details are stored as provided.\n- Verification of account validity occurs only when a subsequent sale transaction is processed.\n- The tokenized ACH payment method can be used for future token sales via the `/transactions/token-sale` endpoint.\n- ACH tokens have restrictions and cannot be used for authorization, void, or subscription transactions.\n\n#### Account Holder Integration\n\nYou can optionally provide an `accountHolder` object with an `externalId` to associate the tokenized payment method with a customer in your system. This is useful for:\n- Managing payment methods per customer using your own identifiers\n- Querying a customer's saved payment methods later via `GET /payment-methods/account-holder/{externalId}`\n\nIf an account holder with the provided `externalId` already exists, it will be used. Otherwise, a new account holder will be created.\n\n#### Webhook Events\n\nThe following webhook events will be triggered during the tokenization process:\n- `auth.completed` - when a $0 authorization is processed and completed (card only).\n- `token.created` - when a card or ACH account is tokenized and stored as a payment method.\n","tags":["Tokens"],"requestBody":{"description":"The request to tokenize a card using a hosted payment form / iframe.","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardOnFileVirtualAddRequest"}}}},"responses":{"200":{"description":"A successful Card Not Present tokenization response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardOnFileVirtualAddResponse"}}}},"400":{"description":"Request Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}}}}
````

## Remove Token

> Remove a previously tokenized card from the system.\
> \
> \*\*IMPORTANT:\*\* Once a payment method token is removed, it cannot be used for any future transactions, including:\
> \- New sales or authorizations\
> \- Referenced captures of existing authorizations\
> \- Referenced refunds of completed transactions\
> \- Any other transaction operations\
> \
> \*\*Before removing a token, ensure that:\*\*\
> \- All pending authorizations have been captured or voided\
> \- No future captures or refunds will be needed for transactions using this token\
> \- All business processes dependent on this token are complete\
> \
> \*\*Post-removal effects:\*\*\
> \- The \`dateDeleted\` timestamp will be immediately reflected in all future List Transactions responses for transactions that used this token\
> \- Sensitive card details (\`maskedCardNumber\`, \`cardExpDate\`) will be removed for security purposes\
> \- Historical transaction data (amounts, authorization codes, reference IDs) remains intact for reconciliation\
> \
> \#### Webhook Events\
> \
> The following webhook events will be triggered during the tokenized card removal process:\
> \- \`token.removed\` - when a previously tokenized card is removed from the system.<br>

````json
{"openapi":"3.1.0","info":{"title":"Integrated Commerce Omni-Channel Payment API","version":"2026.08.03"},"tags":[{"name":"Tokens","description":"Endpoints for creating and removing tokenized payment methods"}],"servers":[{"url":"https://api.omni.integratedcommerce.io/v1"}],"security":[{"ApiKeyHeader":[]}],"components":{"securitySchemes":{"ApiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key","description":"All API requests must be authenticated using an API key. The API key should be included in the `x-api-key` header of each request.\n\n### API Key Format\n\n```\nx-api-key: <api-key>\n```\n\n### Resource Scope\n\nAn API key grants access only to the resources owned by its merchant. Every resource identifier you supply - transaction IDs, account holder IDs, payment method tokens, subscription IDs, settlement IDs - is resolved within the scope of the merchant that owns the API key.\n\nSupplying an identifier that belongs to a different merchant produces the same not-found response as an identifier that does not exist at all. Responses do not distinguish between the two cases, so a resource ID cannot be used to determine whether it exists under another merchant.\n\n### Environment-Specific Keys\n\nSeparate API keys identify the sandbox and production environments. The base URL for both environments is the same (`https://api.omni.integratedcommerce.io/v1`), but the keys are different.\n\n#### Sandbox Environment\n- Format: `key_test_<ULID>.<secret>`\n- Use this key for development and testing\n\n#### Production Environment\n- Format: `key_live_<ULID>.<secret>`\n- Use this key for live production traffic\n\nSend the complete key, including the secret after the dot. Legacy keys without a dot remain supported until revoked.\n\n### Error Responses\n\nIf authentication fails because of an invalid or missing API key, you will receive a `401 Unauthorized` response. Check the response message for more details.\n\n## Rate Limiting\n\nIf you receive `429 Too Many Requests`, pause before retrying. Honor `Retry-After` when supplied; otherwise use exponential backoff. The API does not guarantee `X-RateLimit-*` headers.\n"}},"schemas":{"CardOnFileRemoveResponse":{"type":"object","description":"Represents the response to a tokenized card removal request.","properties":{"id":{"type":"string","description":"Unique ID for this payment-method operation (`ops_` prefix)."},"paymentMethod":{"allOf":[{"$ref":"#/components/schemas/PaymentMethodDetails"},{"type":"object","description":"The tokenized card that was removed.","properties":{"id":{"type":"string"}}}]},"referenceId":{"$ref":"#/components/schemas/ReferenceIdForResponse"},"resultCode":{"$ref":"#/components/schemas/ResultCode"},"resultText":{"$ref":"#/components/schemas/ResultText"}}},"PaymentMethodDetails":{"type":"object","description":"The payment method used for this transaction.","properties":{"id":{"type":"string","description":"The unique identifier of the payment method. This ID has a prefix that makes it human-readable (pmt_trm_* for physical terminals, pmt_vrt_* for virtual card terminals, pmt_ach_* for virtual ACH terminals, pmt_tkn_* for tokens, which store a card or an ACH bank account), but applications should always use the `type` field to determine the payment method type rather than parsing this ID."},"dateDeleted":{"type":"string","format":"date-time","description":"The date and time (UTC) when this payment method token was removed from the system using the Remove Token endpoint. This parameter will only be returned if the token was removed/deleted from the system.\n\nThis field will be present in three scenarios:\n- As part of a successful Remove Token endpoint response\n- In token.removed webhook notifications\n- In List Transactions responses for transactions where the associated token was later removed\n\nWhen this field is present, sensitive card details (maskedCardNumber, cardExpDate) will be removed for security purposes, but all other transaction data remains intact.\n"},"type":{"type":"string","enum":["Unknown","Physical","Virtual","VirtualAch","VirtualEft","Token"],"description":"The type of payment method:\n- `Physical` - A physical payment terminal for Card Present transactions\n- `Virtual` - A hosted payment form/iframe for Card Not Present transactions\n- `VirtualAch` - A hosted payment form/iframe for ACH (US) bank transfer transactions\n- `Token` - A stored payment method (a card or an ACH bank account) for card-on-file and recurring transactions\n- `VirtualEft` - A virtual Canadian bank-transfer payment method, when configured.\n- `Unknown` - The payment method type is not known.\n"},"currency":{"type":"string","enum":["USD","CAD"],"description":"The currency of the payment method."},"description":{"type":"string","description":"A human-readable name for the payment method."},"billingContact":{"allOf":[{"$ref":"#/components/schemas/BillingContact"},{"type":"object","description":"Billing contact information. Only returned for tokenized payment methods from Card Not Present transactions."}]},"cardType":{"type":"string","enum":["UNKNOWN","DEBIT","VISA","MASTERCARD","AMEX","DINERS","DISCOVER","JCB","UNIONPAY","MAESTRO","GIFT","CASH","EBT","INTERAC","OTHER"],"description":"The type of card used for this transaction:\n * `UNKNOWN` - The card type is unknown\n * `DEBIT` - Debit Card\n * `VISA` - Visa Credit Card\n * `MASTERCARD` - MasterCard Credit Card\n * `AMEX` - American Express Credit Card\n * `DINERS` - Diners Club Credit Card\n * `DISCOVER` - Discover Credit Card\n * `JCB` - JCB Credit Card\n * `UNIONPAY` - UnionPay Credit Card\n * `MAESTRO` - Maestro Debit Card\n * `GIFT` - Gift Card\n * `CASH` - All-cash Transaction\n * `EBT` - Electronic Benefits Transfer Card\n * `INTERAC` - Interac (Canadian PIN-debit network), Card Present only\n * `OTHER` - Other tender types\n"},"maskedCardNumber":{"type":"string","description":"The masked card number. The format may vary (e.g. ************0011, 4****0011, etc.)"},"cardExpDate":{"type":"string","description":"The expiration date of the card in MMYY format."},"accountType":{"type":"string","enum":["Credit","Debit","Checking","Savings"],"description":"Funding-source classification of this token-type payment method.\n- For card tokens: `Credit` or `Debit`.\n- For ACH tokens: `Checking` or `Savings`.\nMay be omitted for older tokens that pre-date this field, for non-token payment methods,\nor when the source did not provide a classification.\n"},"accountNumber":{"type":"string","description":"Masked bank account number, when available for a bank-account token."}},"required":["id","type","currency"]},"BillingContact":{"type":"object","description":"Billing contact information associated with the payment method. Not returned on card present transactions or on refunds, captures, etc. of card present transactions.","properties":{"name":{"type":"string"},"countryCode":{"type":"string"},"zipCode":{"type":"string"},"address":{"type":"string"},"state":{"type":"string"},"city":{"type":"string"},"address2":{"type":"string","description":"Additional address information, when supplied."}}},"ReferenceIdForResponse":{"type":"string","maxLength":100,"description":"The reference ID for this transaction. This will either be the reference ID provided in the transaction request or, if no value was provided, a value generated automatically by the system. This reference ID can be used for processing captures and referenced refunds by including it in the originalReferenceId field of subsequent transactions.\n"},"ResultCode":{"type":"integer","enum":[0,55,1003,1005,1010,3000,3002,3005,3006,3009,3010,3011,3012,3013,3014,3020,4001,4100,6800,6801,6802,6900,6901,6902,9998,9999],"description":"Result codes:\n* `0` - Successful transaction request. (Check each transactionResponse's responseCode to determine if a transaction was approved or declined, card was tokenized, etc.)\n* `1003` - The payment terminal is busy or unavailable. (If this error persists, the user may need to close and re-open the Payment Application on the terminal or restart the terminal)\n* `1005` - The specified terminal could not be found.\n* `1010` - The transaction request is pending to become transaction.\n* `3000` - An unexpected error occurred. Please try again.\n* `3002` - The transaction could not be completed because the payment terminal could not find the related transaction.\n* `3005` - To resolve this error, power off your payment terminal, wait one minute and retry the transaction. If the error persists, please contact support.\n* `3006` - The transaction could not be completed because the payment terminal is low on battery.\n* `3009` - The transaction could not be completed because of network connectivity issues with the payment terminal. Please restore connectivity and try again.\n* `3010` - The transaction took too long and was cancelled by the application.\n* `3011` - Invalid transaction request. Please consult the documentation for valid transaction requests.\n* `3012` - Invalid transaction request. paymentMethodId is invalid.\n* `3013` - Invalid transaction request. The specified amount appears to be incorrect. Please retry with a smaller amount.\n* `3014` - Invalid amount. Please try your transaction again with an amount greater than $0.\n* `3020` - An unsupported transaction result was received.\n* `4001` - The transaction is being processed. This is returned for asynchronous bank transfer payment methods (e.g. ACH) while awaiting a final outcome.\n* `6800` - 3D Secure authentication was rejected by the issuer's ACS (card auth failed, security failure, stolen card, suspected fraud, etc.). Surfaced on declined CNP transactions when `formConfig.enable3DSecure: true`. See [SDK Error Codes](https://fiska.gitbook.io/omni-payment-api/payment-sdk/error-codes) for guidance.\n* `6801` - 3D Secure authentication could not be completed because of an upstream technical issue (ACS timeout, directory server unreachable). Retry later or, if merchant policy allows, retry with 3DS disabled.\n* `6802` - The card is not enrolled in 3D Secure. Retry without 3DS only if merchant policy allows; approval is not guaranteed.\n* `6900` - The issuer declined the card on the hosted payment form or Payment SDK path. The specific reason is on `transactionResponses[0].responseCode` (10-18) and in `resultText`. See [SDK Error Codes](https://fiska.gitbook.io/omni-payment-api/payment-sdk/error-codes) for guidance.\n* `6901` - The gateway rejected or failed the hosted payment form payment because of validation or a gateway failure rather than an issuer decision. See [SDK Error Codes](https://fiska.gitbook.io/omni-payment-api/payment-sdk/error-codes) for guidance.\n* `6902` - The gateway reported no failure but returned no payment token. See [SDK Error Codes](https://fiska.gitbook.io/omni-payment-api/payment-sdk/error-codes) for guidance.\n* `9998` - The system is not ready to process a transaction.\n* `9999` - An unknown error has occurred. If available, an auxiliary error code will be provided in the message.\n* `1010` - The transaction request is pending customer interaction.\n* `4100` - Cardholder verification is required. Follow `threeDsChallenge.redirectUrl` and reconcile the final transaction outcome before retrying.\n* `55` - The customer cancelled the hosted payment attempt.\n"},"ResultText":{"type":"string","description":"A message describing the result code in more detail."},"Error":{"type":"object","description":"Represents an error that occurred during the request.","properties":{"code":{"type":"integer","enum":[0,1000,1001,2000,2010,2011,2012,2013,2020,2021,2100,2101,2102,2103,2105,2201,2202,2900,2901,2902,2903,3000,3201,3202,3203,3204,3205,3299,3400,3401,3501,3502,3503,4000,4001,4002,4003,4004,6000,6001,6100,6101,6102,6103,6200,6201,6202,6203,6300,6301,6400,6500,6501,6600,6700,6701,6800,6801,6802,6900,6901,6902,9000,9999],"description":"Numeric error code. These are grouped into ranges for easier identification and troubleshooting.\n\n * `0` - Unknown\n * `1000` - AuthenticationGenericError\n * `1001` - MerchantNotAuthorizedForResource\n * `2000` - RequestValidationGenericError\n * `2010` - InvalidPaymentMethodType - The payment method type is not supported for this operation. This includes: ACH tokens cannot be used for auth, void, or subscription transactions.\n * `2011` - InvalidTransactionType\n * `2012` - InvalidAmount\n * `2013` - PaymentMethodUsageRestricted\n * `2020` - InvalidPaymentMethodOwner\n * `2021` - InvalidAccountHolderOwner\n * `2100` - InvalidAccountHolderInformation\n * `2101` - InvalidCountryCode\n * `2102` - InvalidZipCode\n * `2103` - MissingCountryCodeOrZipCode\n * `2105` - InvalidEmailAddress\n * `2201` - MissingUniqueToken\n * `2202` - TransactionDeclinedError\n * `2900` - RequestValidationUnsupportedPayload\n * `2901` - RequestValidationWebhookMissingTypeAndDmnType\n * `2902` - RequestValidationUnsupportedWebhookTokenization\n * `2903` - RequestValidationUnsupportedWebhookType\n * `3000` - UnknownServerGenericError\n * `3201` - InvalidOrExpiredSession\n * `3202` - FailedToCreateSessionInAdapter\n * `3203` - ErrorWhileTokenizingCard\n * `3204` - FailedToLoadThirdPartySdk\n * `3205` - SessionExpired\n * `3299` - JavaScriptError\n * `3400` - ResourceNotReadyError\n * `3401` - TransactionNotCompleteYet\n * `3501` - CouldNotCreateBillingSubscription\n * `3502` - CouldNotCancelBillingSubscription\n * `3503` - BillingSubscriptionInvalidStatus\n * `4000` - ResourceNotFound\n * `4001` - TokenizedCardNotFound\n * `4002` - PaymentMethodNotFound\n * `4003` - AccountHolderNotFound\n * `4004` - BillingSubscriptionNotFound\n * `6000` - SdkGenericError\n * `6001` - SdkJavaScriptError\n * `6100` - SdkSessionLoadFailed\n * `6101` - SdkSessionExpired\n * `6102` - SdkSessionNotInitialized\n * `6103` - SdkEnvironmentMismatch\n * `6200` - SdkCardFieldsInvalid\n * `6201` - SdkCardTokenizationFailed\n * `6202` - SdkCardInitFailed\n * `6203` - SdkCardProcessingFailed\n * `6300` - SdkAchFieldsInvalid\n * `6301` - SdkAchProcessingFailed\n * `6400` - SdkEftProcessingFailed\n * `6500` - SdkWalletError\n * `6501` - SdkWalletProcessingFailed\n * `6600` - SdkThirdPartySdkLoadFailed\n * `6700` - SdkFormValidationFailed\n * `6701` - SdkBillingValidationFailed\n * `6800` - Sdk3dsAuthenticationFailed - Issuer ACS rejected the 3DS authentication. Only emitted when `formConfig.enable3DSecure: true`.\n * `6801` - Sdk3dsAuthenticationUnavailable - 3DS authentication could not be completed due to an upstream technical issue.\n * `6802` - Sdk3dsNotEnrolled - The card is not enrolled in 3DS.\n * `6900` - SdkCardDeclined - The issuer declined the card on the hosted payment form or Payment SDK path. The specific reason is on `transactionResponses[0].responseCode` (10-18) and in `resultText`.\n * `6901` - SdkCardGatewayError - The gateway rejected or failed the hosted payment form payment because of validation or a gateway failure rather than an issuer decision.\n * `6902` - SdkCardTokenMissing - The gateway reported no failure but returned no payment token.\n * `9000` - UnhandledGenericError\n * `9999` - MaxErrorCode\n"},"status":{"type":"string","enum":["Completed","Rejected","Interrupted","Unknown"],"description":"Execution status of the request sent to the payment gateway.\n\n * Completed - The request completed successfully.\n * Rejected - The request was rejected by the payment gateway. No action or side effects occurred. The transaction can safely be retried.\n * Interrupted - The request was interrupted, and the final status is unknown. Possible side effects may have occurred (e.g., a Sale (Token) that returns Interrupted might still charge the customer, even if an error is returned). Additional checks are required before retrying the transaction.\n * Unknown\n"},"message":{"type":"string","description":"Developer-facing error message."},"traceId":{"type":"string","description":"Unique trace identifier for tracking and debugging this request."},"timestamp":{"type":"string","format":"date-time","description":"The timestamp when the error occurred (UTC)."},"errorDetails":{"type":"array","items":{"$ref":"#/components/schemas/ErrorDetail"}}}},"ErrorDetail":{"type":"object","description":"Represents a validation error that occurred during the request.","properties":{"code":{"type":"integer","description":"Error code"},"field":{"type":"string","description":"The field with the error"},"message":{"type":"string","description":"Error message"},"details":{"type":"string","description":"Additional details about the error"}}}},"responses":{"UnauthorizedError":{"description":"Unauthorized response due to an invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/payment-methods/token/{id}":{"delete":{"operationId":"removeToken","summary":"Remove Token","description":"Remove a previously tokenized card from the system.\n\n**IMPORTANT:** Once a payment method token is removed, it cannot be used for any future transactions, including:\n- New sales or authorizations\n- Referenced captures of existing authorizations\n- Referenced refunds of completed transactions\n- Any other transaction operations\n\n**Before removing a token, ensure that:**\n- All pending authorizations have been captured or voided\n- No future captures or refunds will be needed for transactions using this token\n- All business processes dependent on this token are complete\n\n**Post-removal effects:**\n- The `dateDeleted` timestamp will be immediately reflected in all future List Transactions responses for transactions that used this token\n- Sensitive card details (`maskedCardNumber`, `cardExpDate`) will be removed for security purposes\n- Historical transaction data (amounts, authorization codes, reference IDs) remains intact for reconciliation\n\n#### Webhook Events\n\nThe following webhook events will be triggered during the tokenized card removal process:\n- `token.removed` - when a previously tokenized card is removed from the system.\n","tags":["Tokens"],"parameters":[{"name":"id","in":"path","required":true,"description":"The ID of the tokenized card to remove.","schema":{"type":"string"}},{"name":"referenceId","in":"query","required":false,"description":"A reference ID to tag and track this transaction for reconciliation.","schema":{"type":"string"}}],"responses":{"200":{"description":"A successful card removal response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CardOnFileRemoveResponse"}}}},"400":{"description":"Request Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}}}}
````
