Skip to content

Fund and withdraw collateral ​

A cardholder's cards share a collateral balance. You can fund it by sending an accepted token to the application's deposit address, or ask Agio to transfer funds from your configured treasury. Treasury-managed collateral can also be withdrawn back to that treasury.

Direct deposit ​

Read the funding address from the card application. Wait until deposit_address and deposit_chain_id are populated; do not substitute the collateral-admin wallet_address or a personal wallet.

graphql
query DepositAddress($applicationId: Int!) {
  AgioCard_card_application(where: { id: { _eq: $applicationId } }, limit: 1) {
    id
    card_user_id
    application_status
    deposit_address
    deposit_chain_id
  }
}
json
{ "applicationId": 42 }

Send the token accepted by your program to deposit_address on deposit_chain_id. Confirm the token contract with Agio during setup. Production collateral commonly uses USDC on Base (8453); development can use Ethereum Sepolia (11155111) or Base Sepolia (84532) and test tokens such as rUSD. Read the actual application chain rather than hardcoding the example network.

A deposit address is the collateral destination, separate from the wallet that administers it. Both treasury-managed and customer-supplied collateral-admin applications can receive a deposit address after contract provisioning.

After confirmation on-chain, check both the deposited token balance and resulting spending power. A confirmed transfer does not by itself establish that the issuer has credited the cardholder.

Check balances ​

The read surfaces use different units:

SurfaceUnits
AgioCard_vw_card_token_balance.token_balanceToken units
AgioCard_vw_card_token_balance.token_balance_usdUSD
AgioCard_vw_card_user_balance monetary fieldsInteger cents
cardBalance.balance monetary fieldsUSD
Funding and withdrawal amountCentsInteger cents

For example, spending power of $15.71 is 15.71 in cardBalance and 1571 in AgioCard_vw_card_user_balance.spending_power.

graphql
query CollateralBalances($where: AgioCard_vw_card_token_balance_bool_exp!) {
  AgioCard_vw_card_token_balance(where: $where) {
    deposit_address
    chain_name
    token_symbol
    token_balance
    token_balance_usd
    advance_rate
  }
}
json
{ "where": { "deposit_address": { "_eq": "<application deposit_address>" } } }

The balance view shows the collateral held on-chain. advance_rate records how much of the collateral's value can contribute to credit.

graphql
query SpendingBalance($cardUserId: String!) {
  AgioCard_vw_card_user_balance(where: { card_user_id: { _eq: $cardUserId } }) {
    card_user_id
    credit_limit
    collateral_balance
    spending_power
    balance_due
  }
}
json
{ "cardUserId": "<cardUserId from createPartnerCardUser>" }

Use the numeric Agio card ID for a live issuer balance:

graphql
query LiveBalance($cardId: Int!) {
  cardBalance(cardId: $cardId) {
    success
    id
    balance {
      creditLimit
      pendingCharges
      postedCharges
      spendingPower
      balanceDue
    }
    error
  }
}
json
{ "cardId": 86 }

Pending authorizations reduce spending power before a purchase settles. Use transaction events to explain changes to the cardholder.

Fund from treasury ​

cardFundSubClientFromTreasury transfers configured collateral from your treasury into a cardholder's collateral contract. Agio signs the transfer. The treasury configuration determines the chain; there is no chainId input.

Supply the issuer cardholder UUID as externalUserId: the application's card_user_id, also returned as cardApplicationExternalId. Do not use the provisioned cardUserId from createPartnerCardUser here.

graphql
mutation Fund($input: CardFundSubClientFromTreasuryInput!) {
  cardFundSubClientFromTreasury(input: $input) {
    success
    jobId
    errorCode
    error
  }
}
json
{ "input": { "externalUserId": "<cardUserId from createPartnerCardUser>", "amountCents": 50000 } }

50000 funds $500. success: true means the job was queued, not that funds arrived. Save jobId and poll it. Enqueue-time failures include INVALID_AMOUNT, SUBCLIENT_NOT_FOUND, and TREASURY_NOT_CONFIGURED.

Poll funding status ​

graphql
query FundingStatus($jobId: String!) {
  cardFundSubClientStatus(jobId: $jobId) {
    success
    jobId
    status
    txHash
    failedCode
    failedReason
    attempts
    errorCode
    errorMessage
  }
}
json
{ "jobId": "<funding jobId>" }
StatusAction
pending, runningWait and poll again
submittedTransfer may be on-chain; keep checking the existing job
completedStore txHash and confirm the cardholder's balance
failedRead failedCode and failedReason; resolve the failure before another funding request

failedCode reports worker failures such as COLLATERAL_CONTRACT_NOT_READY, TREASURY_WALLET_MISMATCH, or TREASURY_NOT_CONFIGURED. COLLATERAL_CONTRACT_NOT_READY means the cardholder's collateral contract did not exist yet; check status with cardFundSubClientStatus and submit again later. The query's errorCode reports a failure to read the job itself, such as NOT_FOUND; it is separate from the transfer outcome.

Uncertain transfer outcome

A job can remain submitted with failedCode: "FUNDING_SEND_INDETERMINATE" after an uncertain broadcast. Do not enqueue another funding request: the first transfer may have landed. Reconcile its transaction and collateral history or contact Agio. Funding does not have a caller-supplied idempotency key.

Withdraw to treasury ​

cardWithdrawForPartner withdraws collateral into your configured treasury wallet. The treasury must be the cardholder's collateral admin, as configured with createWallet: true during application setup. It cannot sign a withdrawal from a customer's self-custody collateral admin.

The destination is fixed by your treasury configuration. The request has no destination-address field. Unlike treasury funding, this operation returns its on-chain result directly.

graphql
mutation Withdraw($input: CardWithdrawForPartnerInput!) {
  cardWithdrawForPartner(input: $input) {
    success
    transactionHash
    spendingPowerCents
    requestedCents
    retryAfterSec
    errorCode
    error
  }
}
json
{
  "input": {
    "externalUserId": "<cardUserId from createPartnerCardUser>",
    "amountCents": 50000,
    "dryRun": true
  }
}

dryRun: true checks inputs, scope, treasury configuration and issuer spending power only. It does not simulate the on-chain withdrawal, so a passing dry run does not guarantee the real call succeeds. An on-chain failure returns success: false with an error message and no errorCode. Read spendingPowerCents when present; a dry run does not reserve funds or guarantee that a later withdrawal succeeds. To submit, use the same operation with dryRun: false. Store transactionHash on success and reconcile the collateral decrease.

An optional chainId defaults to the treasury's configured chain. Use a chain configured for your program. The optional vasp field supports a receiving-VASP Travel Rule integration; coordinate that setup with Agio if you need it.

Error codeAction
INVALID_AMOUNTSupply a positive integer amountCents
SUBCLIENT_NOT_FOUNDCheck the cardUserId and partner scope
TREASURY_NOT_CONFIGUREDContact Agio to configure the treasury
INSUFFICIENT_SPENDING_POWERCompare spendingPowerCents with requestedCents
CARD_SIGNING_IN_FLIGHTWait for retryAfterSec before retrying
INTERNAL_ERRORCheck configuration and reconcile any uncertain outcome with Agio

For bulk funding, multisend sends from a smart wallet to several deposit addresses in one batch. Collateral webhooks report deposits and withdrawals without an individual card ID.

Transfer between cardholders ​

cardTransferForPartner moves collateral from one of your cardholders to another of your cardholders. Your treasury signs as the source cardholder's collateral admin. The withdrawal goes from the source collateral contract straight to the recipient's card deposit address. Agio reads that address from the card program. The request has no destination-address field.

graphql
mutation Transfer($input: CardTransferForPartnerInput!) {
  cardTransferForPartner(input: $input) {
    success
    transferId
    status
    transactionHash
    chainId
    amountCents
    feeMillicents
    retryAfterSec
    errorCode
    error
  }
}
json
{
  "input": {
    "source": "<a reference to the sending cardholder>",
    "recipient": "<a reference to the receiving cardholder>",
    "amountCents": 2500,
    "idempotencyKey": "transfer-2026-10-12-0001"
  }
}

chainId and tokenAddress are optional. They default to your treasury configuration. Both cardholders must have one collateral contract on that chain that holds that token, and the token must be a USD stablecoin.

Cardholder references ​

source and recipient each take any of these four forms. You do not name the form.

FormExample field
Agio user IDuserId of the cardholder
Cardholder IDcardUserId from createPartnerCardUser
Card IDthe card's external UUID
Card number (PAN)the full card number, with or without spaces or dashes

A card ID or a card number refers to the cardholder's shared balance. It is not a separate card balance. A card reference must point to a current card with status active. Agio does not follow a replaced card to its replacement.

A cardholder can never send to itself. When source and recipient resolve to the same cardholder, through any mix of IDs or card numbers, the call fails with SAME_CARDHOLDER. Both cardholders must be approved and active, and each must have an active card. Otherwise the call fails with CARDHOLDER_NOT_ACTIVE or CARD_NOT_ACTIVE. Agio never stores, logs or returns a card number.

When several forms match the same cardholder, the reference resolves to that cardholder. When a reference matches two different cardholders, the call fails with AMBIGUOUS_REFERENCE. A reference that matches no cardholder in your scope returns CARDHOLDER_NOT_FOUND, also when the cardholder belongs to another partner.

Status and safe retries ​

statusMeaning
processingThe withdrawal is submitted, or its outcome is still being reconciled
in_transitThe withdrawal is final on-chain, and the recipient's collateral credit is not recorded yet
completeThe token transfer is final and the card program credited the recipient
errorNo funds moved. Read errorCode and error

success is true only while the transfer has no open errorCode. To read the latest status, send the same request again with the same idempotencyKey. A status read does not check the cards or the treasury configuration again. The call returns the existing transfer and never moves funds twice. A different payload with a used key fails with IDEMPOTENCY_KEY_REUSED. Each source cardholder has at most one transfer in processing. A second transfer from the same source returns TRANSFER_IN_PROGRESS until the first one leaves processing. When the withdrawal outcome is unknown, the transfer stays in processing with errorCode: WITHDRAWAL_UNCONFIRMED, and Agio reconciles it. Do not send a new transfer to replace it.

Fees ​

A transfer has no fee unless your card fee schedule has an active card_transfer rule. Such a rule can charge a flat amount, a percentage of the amount, or both. The source cardholder pays it on the card fee ledger. Agio quotes the fee when it creates the transfer and returns it in feeMillicents. The fee counts against the source cardholder's withdrawable collateral. The recipient's credit from a transfer does not add a load fee.

Error codeAction
INVALID_AMOUNTSupply a positive integer amountCents
INVALID_IDEMPOTENCY_KEYUse 16 to 64 letters, digits, dashes or underscores
CARDHOLDER_NOT_FOUNDCheck the reference and your partner scope
AMBIGUOUS_REFERENCEUse a reference that names one cardholder, such as the cardholder ID
CARD_NOT_ACTIVEUse an active current card. Each cardholder needs one
CARDHOLDER_NOT_ACTIVEBoth cardholders must be approved and active
SAME_CARDHOLDERSource and recipient must be different cardholders with different contracts
CONTRACT_NOT_FOUNDCheck the chain and token for both cardholders
CONTRACT_AMBIGUOUSContact Agio. The cardholder has more than one contract for the token
UNSUPPORTED_TOKENUse the USD stablecoin collateral token of your program
CARD_INSUFFICIENT_COLLATERALLower the amount. Pending card spend and open fees are not withdrawable
TREASURY_NOT_CONFIGUREDContact Agio to configure the treasury
IDEMPOTENCY_KEY_REUSEDUse a new key for a new transfer
TRANSFER_IN_PROGRESSWait until the open transfer from this source leaves processing
CARD_SIGNATURE_ACTIVEWait for retryAfterSec, then retry with a new key
WITHDRAWAL_UNCONFIRMEDDo not retry. Agio reconciles the transfer