Fund a Card via Bank Transfer
Bank-rail (fiat) funding requires registering the cardholder's sending bank account, after which the cardholder initiates a transfer using your Program's inbound bank details. An optional trace notification can be sent to expedite straight-through processing. This guide walks through every step.
Prerequisites
- The card must be issued (
cardIdexists). - The cardholder must have
status = 1(Approved) — bank account registration is a cardholder-level operation. - Your Program's inbound bank credentials (account number, routing/sort code, BIC/SWIFT, reference format) must be retrieved from Account Management in the production environment — they are not available via the program-level API. See Funding & Deposits: Step 2.
Step 1: Register the cardholder's bank account
POST /cardholders/{cardholderId}/register-bank-account
This registers the sending bank account — the cardholder's own bank account from which they will initiate the transfer.
| Field | Required | Constraints | Description |
|---|---|---|---|
senderAccountName | Yes | max 50 chars | The name on the cardholder's bank account |
senderAccountNumber | Yes | 7–34 chars | Bank account number, IBAN, or equivalent |
senderRoutingNumber | Yes | 6–12 chars | Routing number, BIC/SWIFT, sort code, or equivalent |
Example request
{
"senderAccountName": "Jordan Reyes",
"senderAccountNumber": "GB29NWBK60161331926819",
"senderRoutingNumber": "NWBKGB2L"
}Response:
{
"depositId": 7741,
"statusText": "pending"
}The SMS OTP confirmation
Bank account registration is not completable via the API alone. After this call, the cardholder receives an SMS from an anonymous short code (by default) and must reply to that SMS to confirm the registration. The reply-to-confirm methodology is a deliberate security design: it prevents internal personnel from associating an arbitrary bank account with a card programmatically, which would otherwise create a mechanism for diverting incoming funds.
Design your application UX to instruct the cardholder to look out for and reply to this SMS before attempting to fund their card.
You only need to register a bank account once per cardholder per bank account. Once confirmed via SMS reply, subsequent deposits from that account are matched automatically.
Step 2: Cardholder initiates the transfer
The cardholder sends a bank transfer from their registered account to your Program's inbound bank account, using your Program's reference format to ensure the transfer is matched to the correct card.
Program inbound bank credentials are only available in Account Management (production). The account number, sort code/routing number, BIC/SWIFT, and required payment reference format for receiving deposits are not exposed by the program-level API. Retrieve them from Account Management and present them to the cardholder within your application's funding-instructions UX.
Typical reference format guidance (confirm with Axys):
- Include the
cardIdas the payment reference, or use a Program-specific reference format configured in Account Management - Instruct the cardholder to use the exact reference — missing or mismatched references slow matching and may require manual reconciliation
Preventing unallocated deposits — critical
Never direct a cardholder to send a transfer before completing bank account registration. Transfers that cannot be matched automatically are placed in a suspense account — not returned immediately and not credited to any card. This is most likely when a cardholder sends funds from an unregistered bank account, uses a wrong or missing payment reference, or targets a card that isn't
issuerCardStatus = 1(Active).Funds held in suspense create AML, KYT, and Source of Funds compliance exposure. Processing times and outcomes cannot be guaranteed, and unmatched funds may be returned to the originating source less correspondent banking or banking partner fees.
Always confirm bank account registration is complete (including the cardholder's SMS reply) and the card is Active before sharing your Program's funding instructions. See Funding & Deposits: Suspense accounts and unallocated deposits for the full prevention checklist and how to respond if funds are unallocated.
Step 3: Post a trace notification (optional but recommended)
POST /cards/{cardId}/bank-deposit
A trace notification pre-matches the transfer to the card, expediting straight-through processing. All fields are optional — include whatever details you have from the payment initiation:
| Field | Type | Description |
|---|---|---|
transactionAmount | integer | Expected amount (decimal-implied, e.g. 10000 = $100.00) |
transactionReference | string | The unique transaction reference or payment ID from the bank's remittance advice or transaction receipt |
senderAccountName | string | The sender's bank account name |
senderAccountNumber | string | The sender's account number, IBAN, or equivalent |
senderRoutingNumber | string | The sender's routing number, BIC/SWIFT, or sort code |
Example request
{
"transactionAmount": 50000,
"transactionReference": "WIRE-20240614-001",
"senderAccountName": "Jordan Reyes",
"senderAccountNumber": "GB29NWBK60161331926819",
"senderRoutingNumber": "NWBKGB2L"
}Response:
{
"depositId": 7742,
"statusText": "pending"
}The more fields you include, the more precisely the platform can match the inbound transfer. At minimum, transactionReference is the most useful single field if you have it.
Wallet-level bank deposits
To direct a bank deposit to a Wallet rather than a Card, use the equivalent endpoint:
POST /wallets/{walletId}/bank-deposit
The request and response schemas are identical. Funds land in the Wallet's fiat balance and can then be transferred to Cards or other Wallets — see Transfer Internal Fiat Funds.
Step 4: Monitor the balance
Poll GET /cards/{cardId}/balance to track the deposit:
flowchart LR
A["Transfer initiated"] --> B["Transfer arrives\nat Axys\n(correspondent banking)"]
B --> C["ledgerBalance increases\nstatus: PENDING"]
C --> D{"Clearing complete\n(~5 business days)"}
D --> E["availableBalance increases\nstatus: COMPLETE\nFunds spendable"]
| Stage | ledgerBalance | availableBalance |
|---|---|---|
| Transfer not yet arrived | Unchanged | Unchanged |
| Transfer received, clearing in progress | Increased | Unchanged |
| Transfer cleared and settled | Increased | Increased |
Clearing window. Bank-rail funds typically take 3–5 business days from transfer initiation to
availableBalanceupdate, depending on the sending country, correspondent banking relationships, and payment rail used (SWIFT, domestic ACH/BACS/SEPA, etc.). Funds inledgerBalancebut not yet inavailableBalanceare reserved and cannot be spent.
Recommended polling intervals for bank deposits
| Time since trace notification or expected transfer | Interval |
|---|---|
| Day 1 | Every 6–12 hours |
| Days 2–5 | Once daily |
Day 6+ without availableBalance update | Raise a support ticket referencing depositId |
Troubleshooting
| Issue | Likely cause | Resolution |
|---|---|---|
ledgerBalance not updated after 5+ business days | Transfer not yet received; reference mismatch; transfer returned by correspondent bank | Raise a support ticket with depositId, transactionReference, and transfer confirmation from your bank |
| Cardholder didn't receive the SMS OTP | Wrong phone number on the cardholder record; SMS delivery delay | Verify cellNum on the cardholder record; ask cardholder to wait up to 10 minutes; raise support ticket if not received |
| Cardholder lost the SMS OTP before replying | SMS expired or missed | Raise a support ticket to reset the bank account registration |
