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 cardUserId returned by createPartnerCardUser as externalUserId. The same UUID appears as card_user_id on the cardholder and as cardApplicationExternalId on the application.

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.