AgnicPay

Resource Charges API

Charge users for any pay-per-use resource or service

Resource Charges API

Charge users directly for any pay-per-use resource. Unlike AI Gateway monetization (percentage-based fees), resource charges transfer the full payment amount directly to your wallet.

Perfect for Plaid API calls, file processing, data API access, or any metered service.


How It Works

Your App  →  AgnicPay /api/merchant/charge  →  User's Wallet

              Settlement to Your Wallet
  1. User consumes a resource in your application
  2. You call the charge API with the amount
  3. Payment is signed using the user's authorized token
  4. Settlement completes instantly to your merchant wallet
  5. You deliver the resource to the user

Endpoint

POST https://api.agnic.ai/api/merchant/charge

Authentication

The user must have authorized spending via one of these methods:

MethodHeaderDescription
API TokenX-Agnic-Token: agnic_tok_...User's API token
OAuthAuthorization: Bearer agnic_at_...OAuth access token

Request

Headers

HeaderRequiredDescription
X-Merchant-IdYesYour Privy DID (did:privy:...)
X-Merchant-WalletYesYour wallet address (0x...)

Body

{
  "amount": "100000",
  "reference": "order_123",
  "description": "Plaid API call"
}
FieldTypeRequiredDescription
amountstringYesUSDC amount in atomic units (6 decimals). "100000" = $0.10
referencestringNoYour correlation ID for tracking
descriptionstringNoDescription for logging/audit

Amount is in atomic units (6 decimals). To charge $1.00, use "1000000". To charge $0.01, use "10000".


Response

Success (200)

{
  "success": true,
  "transactionHash": "0x...",
  "amountUsd": 0.10,
  "network": "base",
  "merchantId": "did:privy:...",
  "merchantWallet": "0x...",
  "reference": "order_123",
  "explorerUrl": "https://basescan.org/tx/0x...",
  "request_id": "req_..."
}

Error Responses

Missing Headers (400)

{
  "error": "missing_merchant_id",
  "message": "X-Merchant-Id header is required"
}

Invalid Amount (400)

{
  "error": "invalid_amount",
  "message": "amount must be a positive integer (USDC atomic units)"
}

Limit Exceeded (400)

{
  "error": "daily_limit_exceeded",
  "message": "Daily limit exceeded. Spent: $5.00, Limit: $10.00",
  "spent": 5.0,
  "limit": 10.0
}

Network Not Allowed (400)

{
  "error": "network_not_allowed",
  "message": "Base network is required for merchant charges but not allowed by your token"
}

Code Examples

Python

import requests
 
def charge_user(user_token: str, amount_cents: int, reference: str = None):
    """
    Charge a user for a resource.
 
    Args:
        user_token: The user's AgnicPay API token
        amount_cents: Amount in cents (e.g., 10 = $0.10)
        reference: Your tracking reference
    """
    # Convert cents to atomic units (6 decimals)
    amount_atomic = amount_cents * 10000  # 10 cents = 100000 atomic
 
    response = requests.post(
        "https://api.agnic.ai/api/merchant/charge",
        headers={
            "X-Agnic-Token": user_token,
            "X-Merchant-Id": "did:privy:your-merchant-id",
            "X-Merchant-Wallet": "0xYourWalletAddress",
            "Content-Type": "application/json",
        },
        json={
            "amount": str(amount_atomic),
            "reference": reference,
            "description": "API usage charge",
        }
    )
 
    return response.json()
 
# Example: Charge $0.05 for a Plaid API call
result = charge_user(
    user_token="agnic_tok_USER_TOKEN",
    amount_cents=5,
    reference="plaid_txn_123"
)
 
if result.get("success"):
    print(f"Charged! TX: {result['transactionHash']}")
else:
    print(f"Error: {result.get('error')}")

TypeScript / JavaScript

async function chargeUser(
  userToken: string,
  amountCents: number,
  reference?: string
): Promise<ChargeResult> {
  // Convert cents to atomic units (6 decimals)
  const amountAtomic = amountCents * 10000;
 
  const response = await fetch('https://api.agnic.ai/api/merchant/charge', {
    method: 'POST',
    headers: {
      'X-Agnic-Token': userToken,
      'X-Merchant-Id': 'did:privy:your-merchant-id',
      'X-Merchant-Wallet': '0xYourWalletAddress',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: amountAtomic.toString(),
      reference,
      description: 'API usage charge',
    }),
  });
 
  return response.json();
}
 
// Example: Charge $0.10 for file processing
const result = await chargeUser(userToken, 10, 'file_proc_456');
 
if (result.success) {
  console.log(`Charged! TX: ${result.transactionHash}`);
  // Proceed with file processing
} else {
  console.error(`Charge failed: ${result.error}`);
}

cURL

curl -X POST https://api.agnic.ai/api/merchant/charge \
  -H "Content-Type: application/json" \
  -H "X-Agnic-Token: agnic_tok_USER_TOKEN" \
  -H "X-Merchant-Id: did:privy:your-merchant-id" \
  -H "X-Merchant-Wallet: 0xYourWalletAddress" \
  -d '{
    "amount": "100000",
    "reference": "order_123",
    "description": "Plaid API call"
  }'

Use Cases

Plaid API Integration

Charge users when they link bank accounts or fetch transactions:

def link_bank_account(user_token: str, user_id: str):
    # 1. Charge the user first
    charge = charge_user(
        user_token=user_token,
        amount_cents=50,  # $0.50 per link
        reference=f"plaid_link_{user_id}"
    )
 
    if not charge.get("success"):
        raise Exception(f"Payment failed: {charge.get('error')}")
 
    # 2. Only proceed if payment succeeded
    plaid_response = plaid_client.link_token_create(...)
 
    return {
        "link_token": plaid_response.link_token,
        "charge_tx": charge["transactionHash"]
    }

File Processing Service

Charge per file or per MB processed:

def process_file(user_token: str, file_size_mb: int, file_id: str):
    # Price: $0.01 per MB
    amount_cents = file_size_mb * 1
 
    charge = charge_user(
        user_token=user_token,
        amount_cents=amount_cents,
        reference=f"file_{file_id}"
    )
 
    if charge.get("success"):
        # Process the file
        result = process_file_internal(file_id)
        return {"status": "processed", "tx": charge["transactionHash"]}
    else:
        return {"status": "payment_failed", "error": charge.get("error")}

Data API Access

Charge per query or per record:

def query_data(user_token: str, query: str, query_id: str):
    # Estimate cost based on query complexity
    estimated_cost_cents = estimate_query_cost(query)
 
    charge = charge_user(
        user_token=user_token,
        amount_cents=estimated_cost_cents,
        reference=f"query_{query_id}"
    )
 
    if charge.get("success"):
        results = execute_query(query)
        return {"data": results, "cost": estimated_cost_cents / 100}
    else:
        raise PaymentError(charge.get("message"))

Amount Conversion

USD AmountAtomic UnitsCode
$0.0110000"10000"
$0.0550000"50000"
$0.10100000"100000"
$0.50500000"500000"
$1.001000000"1000000"
$5.005000000"5000000"

Formula: atomic_units = usd_amount * 1,000,000


Spending Limits

User tokens have spending limits that apply to resource charges:

Limit TypeDescription
Per-TransactionMaximum amount for a single charge
DailyTotal charges allowed per day
MonthlyTotal charges allowed per month

If a charge exceeds any limit, the API returns an error with details about the limit and current spending.


Viewing Your Earnings

Track resource charge earnings at pay.agnic.ai/earnings:

  • Resource Charges Total: Sum of all charges received
  • Total Charges: Number of successful charges
  • Charge History: Date, amount, reference, status, and transaction hash

Resource charges appear separately from AI Gateway fee earnings, making it easy to track different revenue streams.


Constraints

ConstraintDetails
NetworkBase mainnet only (no testnet)
CurrencyUSDC only
SettlementInstant to merchant wallet
MinimumNo minimum (but gas costs apply)

Best Practices

  1. Charge before delivering - Always confirm payment success before providing the resource
  2. Use references - Include meaningful references for reconciliation
  3. Handle failures gracefully - Show users clear error messages for limit or balance issues
  4. Consider UX - Inform users of costs before charging

Error Handling

def safe_charge(user_token: str, amount_cents: int, reference: str):
    try:
        result = charge_user(user_token, amount_cents, reference)
 
        if result.get("success"):
            return {"ok": True, "tx": result["transactionHash"]}
 
        error = result.get("error")
 
        if error == "daily_limit_exceeded":
            return {"ok": False, "reason": "Daily spending limit reached"}
        elif error == "amount_exceeds_limit":
            return {"ok": False, "reason": "Amount too high for this token"}
        elif error == "network_not_allowed":
            return {"ok": False, "reason": "Token doesn't allow Base network"}
        else:
            return {"ok": False, "reason": result.get("message", "Unknown error")}
 
    except Exception as e:
        return {"ok": False, "reason": str(e)}

Next Steps

On this page