Auto SDKAuto DrivePay with AI3 & USDC

Pay with AI3 & USDC — Developer Integration

This page covers how to integrate permissionless Auto Drive credit purchasing into your own application using the credits API and one of two payment methods:

Payment methodAssetNetworkContract
AI3 (ai3_native)Native AI3Autonomys Auto-EVM (Chain ID 870)AutoDriveCreditsReceiver
USDC (usdc_eth)USDC (ERC-20)Ethereum mainnet (Chain ID 1)AutoDriveUSDCReceiver

Both methods use the same payment intent model and the same credits, limits, and expiry. The AI3 flow is described first; the differences for USDC are covered in Pay with USDC.

For the end-user purchase walkthroughs, see the Pay with AI3 guide and the Pay with USDC guide.

For a complete working example of Pay with AI3 in a real application, see Eulonomys — a reference app with full source code showing the payment intent flow, on-chain contract calls, and Auto Drive uploads end-to-end.


How It Works

The payment system is built around a payment intent model. An AI3 purchase follows these steps:

  1. (Optional) Your app fetches the current storage price (GET /intents/price) to show an estimate. The price reflects the live protocol-level DSN storage cost, read from the Autonomys consensus chain, plus a service fee.
  2. Your app asks the Auto Drive backend to create a payment intent (POST /intents) — a server-side record that locks the price per byte (shannonsPerByte) and binds a bytes32 intent ID to the authenticated account for 10 minutes.
  3. Your app calls payIntent(intentId) on the AutoDriveCreditsReceiver contract on Auto-EVM, sending shannonsPerByte × bytes AI3 as msg.value.
  4. Your app submits the transaction hash to the watch endpoint (POST /intents/:id/watch); the backend waits for 6 block confirmations and then grants the credits.
  5. Your app polls the intent (GET /intents/:id) until it reaches a terminal status.

Credits are granted as paymentAmount ÷ shannonsPerByte bytes (rounded down). They are available immediately after confirmation and can be used via the dashboard, the Auto SDK, or the REST API.


Smart Contracts

ContractPayment methodNetworkChain IDAddress
AutoDriveCreditsReceiverAI3Autonomys Auto-EVM8700xBa5bed8325183c0807EcBC80C82D40Dc7Ac5cbf7
AutoDriveUSDCReceiverUSDCEthereum mainnet10x9c508Fa594f52032D574137585DD2F8A48F92261

The AI3 contract is described below. For the USDC contract, see Smart Contract (USDC).

AutoDriveCreditsReceiver (AI3)

FieldValue
NetworkAutonomys Auto-EVM
Chain ID870
Address0xBa5bed8325183c0807EcBC80C82D40Dc7Ac5cbf7

View on Blockscout: AutoDriveCreditsReceiver

The chain ID, address, and payIntent ABI are also served by GET /intents/contract.

payIntent

function payIntent(bytes32 intentId) public payable whenNotPaused;

intentId is the id returned by POST /intents. Send shannonsPerByte × bytes as msg.value. The contract accepts any non-zero amount and does not check it against the quote: the backend grants credits in proportion to what was actually paid, so paying less buys less storage.

The call reverts with InvalidAmount(0) when msg.value is zero, and with EnforcedPause() when the protocol team has paused payments in the event of a critical issue.

Event

event IntentPaymentReceived(bytes32 indexed intentId, uint256 paymentAmount);

Emitted on successful payment. The backend reads this event from the transaction receipt to confirm the payment.


Payment Intent State Machine

PENDING ──► CONFIRMED ──► COMPLETED
   │            │
   │            ├──► OVER_CAP  (account's 100 GB purchased credit limit exceeded)
   │            │
   │            └──► FAILED    (payment could not be converted to credits, e.g. too small to buy one byte)
   │
   └──► EXPIRED   (no payment received before the price lock ended)

The API returns statuses in lowercase (pending, confirmed, completed, expired, failed, over_cap). The SDK converts them to uppercase.

Always create a fresh intent immediately before paying. Prices reflect live protocol storage cost and can change between requests.


API Reference

Base URL (mainnet): https://mainnet.auto-drive.autonomys.xyz/api

Authentication: endpoints marked as authenticated need both headers below. The API key must belong to a Google-authenticated account.

Authorization: Bearer <your-api-key>
X-Auth-Provider: apikey

Get the Storage Price

GET /intents/price

Returns the current price. No authentication required.

Example response:

{
  "price": 4273583470658,
  "pricePerGB": 4588.73,
  "usd": {
    "usdPerAi3": 0.00139,
    "pricePerGBUsd": 6.39,
    "asOf": "2026-10-07T18:08:26.587Z",
    "lastTradeAt": "2026-10-06T01:38:35.000Z",
    "stale": false
  },
  "usdUnavailableReason": null
}
FieldDescription
pricePrice per byte, in Shannons (1 AI3 = 10^18 Shannons)
pricePerGBPrice per GiB (1,073,741,824 bytes), in AI3
usdUSD estimate, or null when no AI3/USD rate is available. For display only — never use it to settle a payment
usdUnavailableReasonWhy usd is null; null when usd is present

Get the Contract Info

GET /intents/contract

Returns the Auto-EVM chain ID, the AutoDriveCreditsReceiver address, and the minimal payIntent ABI. No authentication required.

Example response:

{
  "chainId": 870,
  "contractAddress": "0xBa5bed8325183c0807EcBC80C82D40Dc7Ac5cbf7",
  "payIntentAbi": [
    {
      "inputs": [{ "name": "intentId", "type": "bytes32" }],
      "name": "payIntent",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }
  ]
}

Create a Payment Intent

POST /intents

Locks the current price per byte for the authenticated account for 10 minutes and returns the intent. Authenticated.

Request body (optional for AI3):

{
  "requestedBytes": "10737418240"
}
FieldDescription
requestedBytesOptional for AI3. Size of the purchase as a decimal string. When sent, Auto Drive checks it against the per-account cap and rejects an over-cap purchase before anything is paid
paymentMethodai3_native (default) or usdc_eth. See Pay with USDC

Response:

{
  "id": "0xabc123...",
  "userPublicId": "user-public-id",
  "status": "pending",
  "paymentMethod": "ai3_native",
  "shannonsPerByte": "4273583470658",
  "expiresAt": "2026-10-07T12:10:00.000Z"
}

The response does not include a total amount. Compute it yourself: msg.value = shannonsPerByte × bytes. Pass id as the intentId argument to payIntent.

Errors:

StatusCodeMeaning
400—requestedBytes is invalid or larger than the whole credit cap, or paymentMethod is not recognised
401-Authorization was sent without a usable X-Auth-Provider, or the API key was rejected
403CREDIT_CAP_EXCEEDEDThe purchase would exceed the per-account cap (only when requestedBytes is sent)
404-No credentials were sent, or the account cannot buy credits (for example, it is not Google-authenticated). The body is plain text Not Found, not JSON
500-The storage price could not be read, or another server error. Retry later
503-The storage price resolved to zero, or the authentication service is unavailable. Retry later

Get an Intent

GET /intents/:id

Returns the intent, including its status. Authenticated. Only the owner of the intent can read it (403 otherwise). Returns 410 once the price lock has lapsed on an intent that has no recorded transaction hash; see Polling after the Price Lock.


Confirm a Transaction

POST /intents/:id/watch

Submits the on-chain transaction hash. Authenticated. The backend waits for 6 confirmations before crediting the account.

Request body:

{
  "txHash": "0xdef456..."
}

txHash must be a 0x-prefixed 32-byte hex hash. Returns 204 No Content on success, 400 for a missing or malformed hash, and 410 if the intent has expired.


Integration Patterns

Pattern 1 — App-managed credits

Your application holds a funded Auto-EVM wallet and purchases credits on behalf of users. Useful for apps that abstract storage costs away entirely. The SDK makes the Auto Drive API calls; your wallet library sends the transaction.

import { createAutoDriveApi } from '@autonomys/auto-drive'
import { NetworkId } from '@autonomys/auto-utils'
import { ethers } from 'ethers'
 
const api = createAutoDriveApi({
  apiKey: process.env.AUTO_DRIVE_API_KEY!,
  network: NetworkId.MAINNET,
})
 
async function purchaseCredits(signer: ethers.Signer, sizeBytes: number) {
  // 1. Create intent — price locked for 10 minutes.
  //    The SDK computes ai3AmountWei = shannonsPerByte × sizeBytes for you.
  const intent = await api.createPaymentIntent(sizeBytes, { checkCreditCap: true })
 
  // 2. Call payIntent on Auto-EVM, sending the computed amount
  const { payIntentAbi } = await api.getPaymentContractInfo()
  const contract = new ethers.Contract(intent.contractAddress, payIntentAbi as ethers.InterfaceAbi, signer)
  const tx = await contract.payIntent(intent.intentId, { value: BigInt(intent.ai3AmountWei) })
  await tx.wait()
 
  // 3. Submit tx hash — backend waits for 6 confirmations, then grants credits
  await api.watchPaymentTransaction(intent.intentId, tx.hash)
 
  // 4. Wait for the intent to reach a terminal status
  const status = await api.waitForPaymentCompletion(intent.intentId)
  return { txHash: tx.hash, status } // 'COMPLETED' | 'EXPIRED' | 'FAILED' | 'OVER_CAP'
}

If you call the REST API directly instead of using the SDK, send the authentication headers on POST /intents, GET /intents/:id, and POST /intents/:id/watch.

Pattern 2 — User-managed credits

Direct users to the Auto Drive dashboard to purchase credits themselves using the Pay with AI3 guide or the Pay with USDC guide. Your application uses the AutoSDK with the user’s API key — no on-chain interaction required in your code.

This is the lower-complexity option and is suitable for most applications.

Checking the Credit Cap Before Payment (SDK)

By default, api.createPaymentIntent(sizeBytes) does not send the purchase size to Auto Drive. Pass { checkCreditCap: true } to send it as requestedBytes. Auto Drive then rejects a purchase that would exceed the per-account cap before anything is paid, instead of the intent ending as OVER_CAP after an on-chain payment.

import { PaymentApiError } from '@autonomys/auto-drive'
 
try {
  const intent = await api.createPaymentIntent(sizeBytes, { checkCreditCap: true })
} catch (error) {
  if (
    error instanceof PaymentApiError &&
    (error.code === 'CREDIT_CAP_EXCEEDED' || error.status === 400)
  ) {
    // Nothing was paid. Try a smaller size.
  }
}

A purchase that does not fit in the remaining headroom fails with CREDIT_CAP_EXCEEDED (403). A single purchase larger than the whole cap fails with HTTP 400 and no code. See Handling Errors for all PaymentApiError codes.


Pay with USDC

Credits can also be purchased with USDC on Ethereum mainnet. The intent model is the same as for AI3, with three differences:

  • The intent is created with paymentMethod: "usdc_eth" and an explicit requestedBytes. The backend returns the exact USDC amount to pay as quotedTokenAmount, locked for 10 minutes.
  • USDC is an ERC-20 token, so the payer first approves the AutoDriveUSDCReceiver contract and then calls payIntentWithToken(intentId, amount). There is no msg.value.
  • The payment is made on Ethereum mainnet, not on Auto-EVM. The chain, receiver address, and token address are served by the API at GET /payments/usdc/target — always read them from there (the SDK does this for you) rather than hardcoding them.

How It Works

With the SDK, steps 1 and 2 are a single call to api.createUsdcPaymentIntent(sizeBytes).

  1. Your app fetches the payment target (GET /payments/usdc/target): chain ID, receiver address, USDC token address, token decimals, and the number of confirmations the backend waits for.
  2. Your app creates a USDC payment intent (POST /intents with paymentMethod: "usdc_eth" and requestedBytes). The backend converts the AI3 storage cost to USDC at the current AI3/USD rate, adds a small quote margin, and returns quotedTokenAmount (USDC base units, 6 decimals).
  3. The payer calls approve(receiverAddress, quotedTokenAmount) on the USDC token contract, then payIntentWithToken(intentId, quotedTokenAmount) on AutoDriveUSDCReceiver.
  4. Your app submits the payment transaction hash to the watch endpoint; the backend waits for the required Ethereum confirmations (currently 6) and then grants the credits.

Credits are granted in proportion to the USDC actually received. Paying exactly quotedTokenAmount grants exactly requestedBytes.

Smart Contract (USDC)

FieldValue
ContractAutoDriveUSDCReceiver
NetworkEthereum mainnet
Chain ID1
Address0x9c508Fa594f52032D574137585DD2F8A48F92261
Accepted tokenUSDC — 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48

View on Etherscan: AutoDriveUSDCReceiver

The same values are served by GET /payments/usdc/target. In code, read them from there (the SDK does this for you) rather than hardcoding them.

payIntentWithToken

function payIntentWithToken(bytes32 intentId, uint256 amount) external nonReentrant whenNotPaused;

intentId is the id returned by POST /intents. amount is the USDC amount in base units (USDC has 6 decimals) — pass quotedTokenAmount exactly. The caller must have approved the receiver for at least amount beforehand; the contract pulls the tokens with transferFrom.

The whenNotPaused modifier allows the protocol team to pause payments in the event of a critical issue.

Event

event IntentTokenPaymentReceived(
    bytes32 indexed intentId,
    address token,
    uint256 amount,
    address indexed payer
);

Emitted on successful payment. amount is the amount actually received by the contract, and payer is the account whose USDC allowance funded the payment.

On-chain errors

ErrorDescription
InvalidAmount(uint256)amount is zero, or nothing was received
EnforcedPause()Contract is paused by the owner
ERC-20 allowance / balance errorsThe payer did not approve enough USDC, or does not hold enough USDC

API Reference (USDC)

Get the Payment Target

GET /payments/usdc/target

Returns where to send a USDC payment. Authenticated (see API Reference for the headers). Returns 403 USDC_PAYMENTS_DISABLED when USDC is not available on this deployment.

Example response:

{
  "chainId": 1,
  "receiverAddress": "0x9c508Fa594f52032D574137585DD2F8A48F92261",
  "tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  "tokenDecimals": 6,
  "confirmations": 6,
  "settleGraceMs": 120000
}

Create a USDC Payment Intent

POST /intents

Authenticated (see API Reference for the headers).

Request body:

{
  "paymentMethod": "usdc_eth",
  "requestedBytes": "10737418240"
}

requestedBytes is required for USDC intents (decimal string). It is also checked against the 100 GB per-account cap before the intent is created.

Response:

{
  "id": "0xabc123...",
  "userPublicId": "user-public-id",
  "status": "pending",
  "paymentMethod": "usdc_eth",
  "shannonsPerByte": "4273583470658",
  "quotedTokenAmount": "67071093",
  "quotedAi3Shannons": "45887253108005714001920",
  "usdRateAtCreation": "1392047389045704",
  "expiresAt": "2026-10-07T12:10:00.000Z"
}

Pass id as the intentId argument and quotedTokenAmount as the amount argument of payIntentWithToken. In this example, 67071093 base units = 67.071093 USDC for 10 GiB (about 45,887 AI3 at 0.00139 USD per AI3, plus the quote margin). usdRateAtCreation is the raw AI3/USD rate scaled by 10^18, without the margin, and is for reporting only.

USDC-specific errors:

StatusCodeMeaning
400—requestedBytes missing or invalid (including larger than the whole credit cap), or paymentMethod not recognised
403USDC_PAYMENTS_DISABLEDUSDC is not available to this account or deployment — pay with AI3
403CREDIT_CAP_EXCEEDEDThe purchase would exceed the 100 GB per-account cap — buy less
503USDC_PAYMENTS_UNAVAILABLEUSDC is closed for now — pay with AI3, or retry later
503PRICE_ORACLE_UNAVAILABLE / PRICE_UNSTABLENo reliable AI3/USD rate right now — retry unchanged

Confirm a Transaction

Same endpoint as for AI3: POST /intents/:id/watch with the payIntentWithToken transaction hash (not the approval hash). If the price lock has already lapsed, this call can return 410. The payment can still be credited from the on-chain event, so continue to wait for completion.

SDK Functions (USDC)

Requires @autonomys/auto-drive v1.6.16 or later. Earlier versions do not include the USDC functions and exports listed below.

@autonomys/auto-drive wraps the USDC endpoints. As with AI3, the SDK does not sign anything: it returns the quote and where to pay, plus typed ABIs, and your wallet library sends the transactions.

Function / exportDescription
api.getUsdcPaymentTarget()Returns the UsdcPaymentTarget (chain ID, receiver, token, decimals, confirmations, settleGraceMs)
api.createUsdcPaymentIntent(sizeBytes)Creates a price-locked USDC intent for sizeBytes (number or bigint). Fetches the target for you and returns a UsdcPaymentIntent
api.watchPaymentTransaction(intentId, txHash)Same as for AI3 — pass the payIntentWithToken hash
api.waitForPaymentCompletion(intentId, { settleGraceMs })Same as for AI3 — pass intent.settleGraceMs so a 410 after the lock lapses does not end the wait early
erc20ApprovalAbiMinimal ERC-20 ABI: approve, allowance, balanceOf
usdcReceiverAbiMinimal ABI for payIntentWithToken(bytes32, uint256)
PaymentApiErrorError thrown on non-OK payment responses, with status and code

See Available Functions for the full type definitions.

Integration Example (USDC)

The example below uses an Auto Drive API key and a funded Ethereum wallet (USDC for the payment, ETH for gas). All Auto Drive calls need the API key, so run this on your server.

import {
  createAutoDriveApi,
  erc20ApprovalAbi,
  PaymentApiError,
  usdcReceiverAbi,
} from '@autonomys/auto-drive'
import { NetworkId } from '@autonomys/auto-utils'
import { createPublicClient, createWalletClient, http, type Hex } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
 
async function purchaseCreditsWithUsdc(sizeBytes: bigint) {
  const api = createAutoDriveApi({
    apiKey: process.env.AUTO_DRIVE_API_KEY!,
    network: NetworkId.MAINNET,
  })
 
  // 1. Lock a USDC price. The SDK also fetches where to pay — never hardcode it.
  const intent = await api.createUsdcPaymentIntent(sizeBytes)
  // intent.usdcAmount          — exact amount in base units (6 decimals)
  // intent.usdcAmountFormatted — human-readable amount, e.g. "27.498"
  // intent.chainId, intent.tokenAddress, intent.receiverAddress — where to pay
  if (intent.chainId !== mainnet.id) throw new Error(`Unexpected USDC chain ${intent.chainId}`)
 
  const account = privateKeyToAccount(process.env.PAYER_PRIVATE_KEY as Hex)
  const transport = http(process.env.ETH_RPC_URL)
  const publicClient = createPublicClient({ chain: mainnet, transport })
  const walletClient = createWalletClient({ account, chain: mainnet, transport })
 
  const amount = BigInt(intent.usdcAmount)
  const token = intent.tokenAddress as Hex
  const receiver = intent.receiverAddress as Hex
 
  // 2. Approve the receiver to pull the USDC (skip if the allowance is enough)
  const allowance = await publicClient.readContract({
    address: token,
    abi: erc20ApprovalAbi,
    functionName: 'allowance',
    args: [account.address, receiver],
  })
  if (allowance < amount) {
    const approveHash = await walletClient.writeContract({
      address: token,
      abi: erc20ApprovalAbi,
      functionName: 'approve',
      args: [receiver, amount],
    })
    await publicClient.waitForTransactionReceipt({ hash: approveHash })
  }
 
  // 3. Pay the intent. No value: the receiver pulls the approved USDC.
  //    Wait for the receipt first, so block time does not use up the 410 grace.
  const txHash = await walletClient.writeContract({
    address: receiver,
    abi: usdcReceiverAbi,
    functionName: 'payIntentWithToken',
    args: [intent.intentId as Hex, amount],
  })
  const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash })
  if (receipt.status !== 'success') throw new Error(`USDC payment reverted: ${txHash}`)
 
  // 4. Tell Auto Drive about the payment transaction (not the approval)
  try {
    await api.watchPaymentTransaction(intent.intentId, txHash)
  } catch (error) {
    // 410: the price lock has lapsed. The payment can still settle, so continue.
    if (!(error instanceof PaymentApiError && error.status === 410)) throw error
  }
 
  // 5. Wait for credits. settleGraceMs keeps polling through a 410 after the lock lapses.
  const status = await api.waitForPaymentCompletion(intent.intentId, {
    settleGraceMs: intent.settleGraceMs,
  })
 
  return { txHash, status } // 'COMPLETED' | 'EXPIRED' | 'FAILED' | 'OVER_CAP'
}

Handling Errors

When Auto Drive refuses a payment request, the SDK throws a PaymentApiError with the HTTP status and, for coded errors, a machine-readable code. Branch on code, not on message.

codeStatusWhat to do
PRICE_ORACLE_UNAVAILABLE503Retry the same request later
PRICE_UNSTABLE503Retry the same request later
USDC_PAYMENTS_UNAVAILABLE503USDC is closed for now — pay with AI3, or retry later
USDC_PAYMENTS_DISABLED403USDC is not available to you — pay with AI3
CREDIT_CAP_EXCEEDED403The purchase would exceed your credit cap — buy less

Some failures have no code, so check status:

  • 400 — the request is invalid, for example the size is larger than the whole credit cap.
  • 404 on /intents: no credentials were sent, or the account cannot buy credits. Accounts that are not Google-registered get this, not GOOGLE_ACCOUNT_REQUIRED. The body is plain text Not Found, not JSON.
  • 500: a server-side failure, for example the storage price could not be read. Retry later.
  • 410 — the price lock has lapsed. See Polling after the price lock.
import { PaymentApiError } from '@autonomys/auto-drive'
 
try {
  const intent = await api.createUsdcPaymentIntent(sizeBytes)
  // ...pay as above
} catch (error) {
  if (!(error instanceof PaymentApiError)) throw error
  switch (error.code) {
    case 'PRICE_ORACLE_UNAVAILABLE':
    case 'PRICE_UNSTABLE':
      // retry later
      break
    case 'USDC_PAYMENTS_DISABLED':
    case 'USDC_PAYMENTS_UNAVAILABLE':
      // fall back to api.createPaymentIntent (AI3)
      break
    default:
      throw error
  }
}

Polling after the Price Lock

GET /intents/:id answers 410 once the price lock has lapsed on an intent that has no recorded transaction hash — for example when watchPaymentTransaction itself got a 410. With a recorded hash, the 410 starts only when Auto Drive stops accepting payments for the intent, currently 20 minutes after expiresAt. A payment sent near the end of the lock can still be credited after the first 410.

With settleGraceMs set, waitForPaymentCompletion keeps polling through 410 for that long and only then returns 'EXPIRED'. A successful read in between resets the clock. Without it, a 410 throws a PaymentApiError, as in earlier SDK versions. This option works for AI3 intents too.

  • 'EXPIRED' from this path means the SDK stopped waiting. It does not prove the payment is lost — check the account’s credits before telling a user the payment failed.
  • The grace is measured from the first 410 the SDK sees, so wait for the payment receipt before calling waitForPaymentCompletion.
  • Keep timeoutMs (default 5 minutes) larger than the time left on the lock plus settleGraceMs, or the call can throw a timeout error before it returns 'EXPIRED'.

Things to Know

  • Always pay usdcAmount (quotedTokenAmount) exactly. Paying a different amount is accepted, but credits are granted in proportion to what was received.
  • Pay before the intent expires. Auto Drive still credits a payment for a short while after the 10-minute lock (currently up to 20 minutes after expiresAt), but do not rely on it. A payment that arrives later is not credited automatically; it is recorded and recoverable through support.
  • Do not send AI3 to a USDC intent (or USDC to an AI3 intent). Payments in the wrong asset are not credited automatically.
  • USDC can be temporarily closed. New USDC intents may be refused (USDC_PAYMENTS_UNAVAILABLE, PRICE_ORACLE_UNAVAILABLE, PRICE_UNSTABLE) for short periods. An intent that was already quoted stays payable until it expires. Fall back to AI3 if needed.

Using Credits After Purchase

Once purchased, credits are consumed automatically when uploading via the @autonomys/auto-drive SDK. No additional configuration is needed.

import { createAutoDriveApi } from '@autonomys/auto-drive'
import { NetworkId } from '@autonomys/auto-utils'
 
const api = createAutoDriveApi({ apiKey: 'your-api-key', network: NetworkId.MAINNET })
 
// Credits are deducted automatically on upload
const cid = await api.uploadFileFromInput(file, {
  password: 'optional-encryption-password', // enable AES-256-GCM encryption
})
 
console.log('Uploaded CID:', cid)

Credits are consumed in FIFO expiry order — the soonest-to-expire credits are spent first.


Limits and Expiry

ParameterValue
Maximum purchased credits100 GB per account
Credit expiry90 days from grant date
Intent timeout10 minutes
Confirmation blocks6 (Auto-EVM for AI3, Ethereum for USDC)

The 100 GB cap applies to AI3 and USDC purchases combined.


Further Reading