Skip to main content
A debit card connects to OMS in two directions. As a funding source, OMS pulls US dollars from the card and delivers crypto to a wallet (a card buy). As a payout destination, OMS sells crypto and pushes the dollars to the card (a card sell). Both use the standard quote-and-transaction flow. You register a card once per customer. It becomes an external account with an ext_ ID, and every later quote references that ID. Cards must be enabled on your project, and the customer needs an active usd endorsement.

Choose an integration path

Your PCI DSS status decides how you capture card details:
  • Not PCI DSS compliant: you must use the OMS browser SDK. The card number, expiry, and CVC are typed into secure fields hosted by Very Good Security (VGS), which replaces the card data with aliases before it reaches OMS. Your systems only see the resulting ext_ ID.
  • PCI DSS compliant: you may call the API directly from your certified environment, or use the SDK to keep card data out of your PCI scope.
Both paths produce the same external account. After registration, quotes and transactions always run server to server with your secret key.

Registering a card

With the browser SDK

The OMS browser SDKs are in early access. Contact us to request access.
The browser SDK renders the secure card fields and attaches the risk session for you. Install the card SDK and its companion risk SDK:
You need a publishable key (pk_...) from the OMS Dashboard, and your project must be provisioned for risk collection. Your server exposes one authenticated endpoint that issues a customer token. It checks your own session, looks up the user’s OMS customer ID, calls POST /customers/{customerId}/token with your secret key, and returns the { token, expiresAt } response unchanged. Tokens expire after one minute, so issue a fresh one on every request. In the browser, wrap your app in OMSProvider and render DebitCardForm on the card page:
DebitCardForm includes the card fields, billing address inputs, and inline validation. onSuccess fires when OMS accepts the card and returns its ext_ ID. If a submission times out, the card may or may not have been saved: list the customer’s external accounts with GET /external-accounts?customerId=... and match the card’s last four digits and expiry before letting them try again.

With the API

PCI DSS compliant partners register a card with POST /external-accounts/cards. Cards have their own endpoint: the generic POST /external-accounts rejects type: card with CardMustUseCardEndpoint.
webSessionId and deviceFingerprint are required. They are a matched risk-session pair that the OMS risk SDK collects in the customer’s browser; pass them through unchanged and do not log or store them.

After registration

The billing address belongs to the card, not the customer’s KYC record, and the card processor uses it for address verification. If you omit it, OMS uses the customer’s residential address. The card number and CVV are write-only. The response returns only cardNumberLast4, cardProvider, cardType, expiryMonth, expiryYear, and billingAddressSource. Only debit cards are accepted. In sandbox, use the test card 4111 1111 1111 1111 with any future expiry and any three-digit CVC. A new card starts pending. Once the processor verifies it, the card moves to active and you can use it on quotes. Subscribe to externalAccount.statusChanged or poll GET /external-accounts/{id} to see the change. See External accounts for the full lifecycle.

Use a card on a quote

Reference the card as the quote source for a buy, or as the destination for a sell. The card side always uses asset: usd, and OMS infers the network.

Execute the transaction

Create the transaction with POST /transactions, passing the quote ID and the risk-session pair from the customer’s browser:
A card transaction without webSessionId is rejected with RiskSessionRequired. See Transactions for statuses and webhooks.
Card registration, quote, and transaction requests accept an Idempotency-Key header. Use one key per logical attempt and reuse it when you retry after a timeout.