ASG Card Documentation
x402-powered virtual card issuance for AI agents on Stellar. Pay with USDC, get a card in seconds.
https://api.asgcard.dev
Overview
ASG Card exposes a REST API with five classes of endpoints:
| Type | Auth | Description |
|---|---|---|
| Public | None | Health check, pricing, tiers |
| Paid (x402) | USDC payment on Stellar via x402 | Create/fund cards |
| Wallet-signed | Ed25519 signature | Card management |
SDK
The official client SDK wraps the raw x402 flow (402 → parse challenge → pay USDC → retry with proof) into one-liner methods. No need to handle the payment handshake yourself.
Install
npm install @asgcard/sdk
Quick Start
import { ASGCardClient } from '@asgcard/sdk';
const client = new ASGCardClient({
privateKey: '<stellar_secret_seed>',
baseUrl: 'https://api.asgcard.dev',
rpcUrl: 'https://mainnet.sorobanrpc.com',
});
// One line — SDK handles payment automatically
const card = await client.createCard({
amount: 10, // $10 tier
nameOnCard: 'AI AGENT',
email: 'agent@example.com',
});
console.log(card.details); // { cardNumber, cvv, expiry, … }
Configuration
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
privateKey |
string |
One of two | — | Stellar secret seed (for signing) |
walletAdapter |
WalletAdapter |
One of two | — | Stellar wallet adapter (publicKey + signTransaction) |
baseUrl |
string |
No | https://api.asgcard.dev |
API base URL |
rpcUrl |
string |
No | https://horizon.stellar.org |
Stellar Horizon or RPC endpoint |
timeout |
number |
No | 60000 |
Request timeout in milliseconds |
Methods
client.createCard(params): Promise<CardResult>
Create a virtual card. Pays USDC automatically.
| Field | Type | Values |
|---|---|---|
amount | number | 10 | 25 | 50 | 100 | 200 | 500 |
nameOnCard | string | Name embossed on card |
email | string | Delivery email |
const result = await client.createCard({
amount: 50,
nameOnCard: 'AI AGENT',
email: 'agent@example.com',
});
client.fundCard(params): Promise<FundResult>
Fund an existing card.
| Field | Type | Values |
|---|---|---|
amount | number | 10 | 25 | 50 | 100 | 200 | 500 |
cardId | string | UUID of existing card |
const result = await client.fundCard({
amount: 25,
cardId: 'card-uuid',
});
client.getTiers(): Promise<TierResponse>
Get pricing tiers and fee breakdown (no payment required).
client.health(): Promise<HealthResponse>
Check if the ASG Card API is reachable.
client.address: string
The Stellar wallet address being used for payments.
Error Handling
import {
ASGCardClient,
InsufficientBalanceError,
PaymentError,
ApiError,
TimeoutError,
} from '@asgcard/sdk';
try {
const card = await client.createCard({ amount: 50, nameOnCard: 'AI', email: 'a@b.com' });
} catch (error) {
if (error instanceof InsufficientBalanceError) {
console.log(`Need ${error.required}, have ${error.available}`);
} else if (error instanceof PaymentError) {
console.log(`Payment failed: ${error.message}, tx: ${error.txHash}`);
} else if (error instanceof ApiError) {
console.log(`Server error ${error.status}:`, error.body);
} else if (error instanceof TimeoutError) {
console.log('Request timed out');
}
}
| Class | Fields | When |
|---|---|---|
InsufficientBalanceError | required, available | USDC balance < required |
PaymentError | message, txHash? | Stellar tx failed |
ApiError | status, body | Server returned non-2xx |
TimeoutError | message | Request exceeded timeout |
Low-Level x402 Utilities
For full control over the payment flow:
import {
parseChallenge,
checkBalance,
executePayment,
buildPaymentPayload,
buildPaymentTransaction,
handleX402Payment,
} from '@asgcard/sdk';
| Function | Signature | Description |
|---|---|---|
parseChallenge |
(input: unknown) → X402Accept |
Parse 402 challenge, returns first accepted method |
checkBalance |
(params) → Promise<void> |
Throws InsufficientBalanceError if USDC < required |
executePayment |
(params) → Promise<string> |
Sends USDC on Stellar, returns txHash |
buildPaymentPayload |
(accepted, signedXDR) → string |
Builds base64-encoded X-PAYMENT header value |
buildPaymentTransaction |
(params) → Promise<string> |
Build + sign a Soroban SAC USDC transfer |
handleX402Payment |
(params) → Promise<string> |
Full cycle: parse → pay → build proof |
How It Works
Sequence diagram showing the SDK flow:
SDK handles the 402 → pay → retry cycle automatically.
MCP Server
The @asgcard/mcp-server package exposes 9 tools via the Model Context Protocol, enabling AI agents to manage ASG Card programmatically.
~/.asgcard/wallet.json automatically. Run asgcard wallet create once, and all MCP clients pick it up.
Agent Quick Start
One command — creates wallet, configures MCP, installs skill:
npx @asgcard/cli onboard -y
First-Class Clients
One-click installer included:
asgcard install --client codex # OpenAI Codex
asgcard install --client claude # Claude Code
asgcard install --client cursor # Cursor
Compatible Agent Runtimes
OpenClaw, Manus, Perplexity Computer, and other MCP-compatible agents work via manual config or the bundled SDK:
Codex (~/.codex/config.toml):
[mcp_servers.asgcard]
command = "npx"
args = ["-y", "@asgcard/mcp-server"]
Cursor / generic MCP (mcp.json):
{
"mcpServers": {
"asgcard": {
"command": "npx",
"args": ["-y", "@asgcard/mcp-server"]
}
}
}
SDK / direct API: Use @asgcard/sdk for agents without MCP support.
Tools
All 9 tools exposed by the MCP server:
| Tool | Description | Auth |
|---|---|---|
get_wallet_status | Use FIRST — wallet address, USDC balance, readiness | None |
create_card | Create a virtual MasterCard with a specified tier (10–500 USD) | x402 |
fund_card | Add funds to an existing card | x402 |
list_cards | List all cards owned by the wallet | Wallet |
get_card | Get card summary (balance, status) | Wallet |
get_card_details | Retrieve PAN, CVV, expiry | Wallet + Nonce |
freeze_card | Temporarily block all transactions | Wallet |
unfreeze_card | Re-enable a frozen card | Wallet |
get_pricing | View pricing tiers and fees | None |
Example — create a card via your AI agent:
Agent Skill (x402)
The CLI bundles a product-owned asgcard skill that is automatically installed during asgcard onboard.
asgcard onboard installs the skill to ~/.agents/skills/asgcard/Compatible runtimes: For OpenClaw, Manus, custom LLM pipelines — use the x402-payments-skill or call the SDK/API directly.
CLI
The @asgcard/cli package provides a terminal interface for ASG Card — onboard, create, fund, freeze, and inspect virtual cards from your command line.
Install
npm install -g @asgcard/cli
Agent Quick Start:
# Full onboarding (wallet + MCP + skill)
asgcard onboard -y
# Then install for your client:
asgcard install --client codex # or claude, cursor
# Or step by step:
asgcard wallet create
asgcard wallet info
asgcard card:create -a 10 -n "AGENT ALPHA" -e agent@example.com
Commands
| Command | Description |
|---|---|
asgcard wallet create | Generate new Stellar keypair, save to ~/.asgcard/wallet.json |
asgcard wallet import | Import an existing Stellar secret key |
asgcard wallet info | Show public key, USDC balance, deposit instructions |
asgcard install --client <c> | Configure MCP for codex, claude, or cursor |
asgcard onboard [-y] | Full onboarding: wallet + MCP + skill + next step |
asgcard doctor | Diagnose setup (key, API, RPC, balance, MCP configs) |
asgcard login | Configure Stellar private key (legacy) |
asgcard whoami | Display wallet public key |
asgcard cards | List all cards for the wallet |
asgcard card <id> | Show card summary (balance, status) |
asgcard card:details <id> | Retrieve PAN, CVV, expiry (nonce-protected) |
asgcard card:create | Create a new card (x402 payment) |
asgcard card:fund <id> | Top up an existing card |
asgcard card:freeze <id> | Freeze a card |
asgcard card:unfreeze <id> | Unfreeze a card |
asgcard pricing | View pricing and fee tiers |
asgcard health | Check API health status |
Authentication
ASG Card uses two authentication modes depending on the endpoint.
x402 Payment Flow
Paid endpoints (POST /cards/create/tier/:amount, POST /cards/fund/tier/:amount) use the x402 protocol on Stellar. The flow has 4 steps:
Step 1 — Request without payment
curl -X POST https://api.asgcard.dev/cards/create/tier/10 \
-H "Content-Type: application/json" \
-d '{"nameOnCard": "AGENT ALPHA", "email": "agent@example.com"}'
Step 2 — Receive 402 with payment instructions
The server responds with HTTP 402 and a challenge JSON:
{
"x402Version": 2,
"resource": {
"url": "https://api.asgcard.dev/cards/create/tier/10",
"description": "Create ASG Card with $10 load",
"mimeType": "application/json"
},
"accepts": [{
"scheme": "exact",
"network": "stellar:pubnet",
"asset": "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75",
"amount": "172000000",
"payTo": "GAHYHA55RTD2J4LAVJILTNHWMF2H2YVK5QXLQT3CHCJSVET3VRWPOCW6",
"maxTimeoutSeconds": 300,
"extra": { "areFeesSponsored": true }
}]
}
| Field | Description |
|---|---|
scheme | Always "exact" |
network | "stellar:pubnet" |
asset | USDC SAC on Stellar mainnet |
amount | Amount in atomic USDC (7 decimals). 1 USDC = 10,000,000 |
payTo | ASG Treasury public key |
maxTimeoutSeconds | Payment window (300s) |
Step 3 — Agent pays USDC on Stellar
Parse the accepts array and send the specified USDC amount to the payTo address on Stellar, then proceed with facilitator verification if enabled.
Step 4 — Retry with X-PAYMENT header
Re-send the original request with an X-PAYMENT header containing base64-encoded JSON (x402 PaymentPayload):
{
"x402Version": 2,
"accepted": {
"scheme": "exact",
"network": "stellar:pubnet"
},
"payload": {
"transaction": "<BASE64_STELLAR_TX_ENVELOPE>"
}
}
X-PAYMENT: base64(JSON) — the SDK handles this automatically. The facilitator verifies and settles the transaction.
Wallet Signature — Free Endpoints
Wallet-signed endpoints (card management) require Ed25519 signature authentication.
Required Headers
| Header | Description |
|---|---|
X-WALLET-ADDRESS | Stellar public key |
X-WALLET-SIGNATURE | Ed25519 detached signature |
X-WALLET-TIMESTAMP | Unix timestamp (seconds) |
Signature Protocol
message = "asgcard-auth:<unixTimestamp>"
algorithm = Ed25519 detached
validity window = ±5 minutes
const timestamp = Math.floor(Date.now() / 1000);
const message = `asgcard-auth:${timestamp}`;
// wallet.signMessage should return detached Ed25519 signature bytes
const signature = await wallet.signMessage(new TextEncoder().encode(message));
const response = await fetch('https://api.asgcard.dev/cards', {
headers: {
'X-WALLET-ADDRESS': wallet.publicKey,
'X-WALLET-SIGNATURE': Buffer.from(signature).toString('base64'),
'X-WALLET-TIMESTAMP': String(timestamp),
},
});
Pricing
GET /pricing and reflected in the tables below.
All amounts are in USD. 1 USDC = 10,000,000 atomic units (Stellar uses 7 decimal places).
Card Creation
Creating a new virtual card includes the card load amount plus fees.
| Load | Issuance Fee | TopUp Fee | ASG Fee | Total | Endpoint |
|---|---|---|---|---|---|
Loading pricing from GET /pricing… | |||||
Card Funding
Adding funds to an existing card — no issuance fee.
| Fund Amount | TopUp Fee | ASG Fee | Total | Endpoint |
|---|---|---|---|---|
Loading pricing from GET /pricing… | ||||
Endpoints
Public Endpoints
No authentication required.
/health
Health check. No authentication required.
Response 200:{
"status": "ok",
"timestamp": "2026-02-11T14:00:00.000Z",
"version": "1.0.0"
}
/pricing
Returns full pricing breakdown for all creation and funding tiers.
Response 200:{
"creation": {
"tiers": [{
"loadAmount": 10,
"totalCost": 17.2,
"issuanceFee": 3.0,
"topUpFee": 2.2,
"ourFee": 2.0,
"endpoint": "/cards/create/tier/10"
}]
},
"funding": {
"tiers": [{
"fundAmount": 10,
"totalCost": 14.2,
"topUpFee": 2.2,
"ourFee": 2.0,
"endpoint": "/cards/fund/tier/10"
}]
}
}
/cards/tiers
Returns available tiers with endpoints and detailed fee breakdowns.
Response 200:{
"creation": [{
"loadAmount": 10,
"totalCost": 17.2,
"endpoint": "/cards/create/tier/10",
"breakdown": {
"cardLoad": 10,
"issuanceFee": 3,
"topUpFee": 2.2,
"ourFee": 2,
"buffer": 0
}
}],
"funding": [{
"fundAmount": 10,
"totalCost": 14.2,
"endpoint": "/cards/fund/tier/10",
"breakdown": {
"fundAmount": 10,
"topUpFee": 2.2,
"ourFee": 2
}
}]
}
Paid Endpoints — x402
These endpoints require USDC payment via the x402 protocol on Stellar. See Authentication → x402 Payment Flow.
/cards/create/tier/:amount
Create a new virtual card loaded with the specified tier amount.
Available tiers: 10, 25, 50, 100, 200, 500
| Field | Type | Required | Description |
|---|---|---|---|
nameOnCard | string | Yes | Min 1 char |
email | string | Yes | Valid email |
{
"success": true,
"card": {
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"nameOnCard": "AGENT ALPHA",
"balance": 10,
"status": "active",
"createdAt": "2026-02-11T14:00:00.000Z"
},
"payment": {
"amountCharged": 17.2,
"txHash": "<stellar_tx_hash>",
"network": "stellar"
},
"detailsEnvelope": {
"cardNumber": "5395000000007890",
"expiryMonth": 12,
"expiryYear": 2028,
"cvv": "123",
"billingAddress": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94105",
"country": "US"
},
"oneTimeAccess": true,
"expiresInSeconds": 300,
"note": "Store securely. Use GET /cards/:id/details with X-AGENT-NONCE for subsequent access."
}
}
/cards/fund/tier/:amount
Add funds to an existing card.
Request body:| Field | Type | Required | Description |
|---|---|---|---|
cardId | string | Yes | UUID of existing card |
{
"success": true,
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"fundedAmount": 25,
"newBalance": 35.0,
"payment": {
"amountCharged": 29.5,
"txHash": "<stellar_tx_hash>",
"network": "stellar"
}
}
Wallet-Signed Endpoints
These endpoints are free but require wallet signature authentication. See Authentication → Wallet Signature.
/cards
List all cards owned by the authenticated wallet.
Response 200:{
"cards": [{
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"nameOnCard": "AGENT ALPHA",
"lastFour": "7890",
"balance": 10.0,
"status": "active",
"createdAt": "2026-02-11T14:00:00.000Z"
}]
}
/cards/:cardId
Get detailed info for a specific card.
Response 200:{
"card": {
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"nameOnCard": "AGENT ALPHA",
"email": "agent@example.com",
"balance": 8.5,
"initialAmountUsd": 10,
"status": "active",
"createdAt": "2026-02-11T14:00:00.000Z",
"updatedAt": "2026-02-11T15:30:00.000Z"
}
}
/cards/:cardId/details
Retrieve sensitive card details — full card number, CVV, expiry, and billing address. See Agent-First Details for the full protocol.
X-AGENT-NONCE header (UUID v4). Rate limited to 5 unique nonces per card per hour. Returns 409 on replay, 403 if owner revoked access.
| Header | Description |
|---|---|
X-WALLET-ADDRESS | Stellar public key |
X-WALLET-SIGNATURE | Ed25519 detached signature |
X-WALLET-TIMESTAMP | Unix timestamp (seconds) |
X-AGENT-NONCE | UUID v4 — unique per request, anti-replay |
{
"details": {
"cardNumber": "5395000000007890",
"expiryMonth": 12,
"expiryYear": 2028,
"cvv": "123",
"billingAddress": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94105",
"country": "US"
}
}
}
| Code | When | Body |
|---|---|---|
403 | Owner revoked details access | {"error":"Details access revoked by card owner"} |
409 | Nonce already used (replay) | {"error":"Nonce already used (replay detected)","code":"REPLAY_REJECTED"} |
429 | Rate limit exceeded | {"error":"Card details rate limit exceeded (5 requests / hour)"} |
/cards/:cardId/freeze
Freeze a card. Blocks all transactions until unfrozen.
Response 200:{
"success": true,
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"status": "frozen"
}
/cards/:cardId/unfreeze
Unfreeze a previously frozen card.
Response 200:{
"success": true,
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"status": "active"
}
Agent-First Details Access
ASG Card uses an agent-first model for sensitive card data. Card details (number, CVV, expiry) are delivered via two mechanisms designed for autonomous agents.
Details Envelope
When a card is created via POST /cards/create/tier/:amount, the 201 response includes a detailsEnvelope field with full card details:
{
"success": true,
"card": { "cardId": "...", "status": "active" },
"payment": { "txHash": "...", "network": "stellar" },
"detailsEnvelope": {
"cardNumber": "5395000000007890",
"expiryMonth": 12,
"expiryYear": 2028,
"cvv": "123",
"billingAddress": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94105",
"country": "US"
},
"oneTimeAccess": true,
"expiresInSeconds": 300
}
}
detailsEnvelope is returned only in the initial 201 response. It is not stored server-side. Agents should persist card details securely on their side.
GET /cards/:cardId/details
If the agent loses the initial envelope, card details can be retrieved via GET /cards/:cardId/details using wallet signature authentication plus a unique nonce.
Required Headers
| Header | Type | Description |
|---|---|---|
X-WALLET-ADDRESS | string | Stellar public key (G...) |
X-WALLET-SIGNATURE | string | Ed25519 detached signature (base64) |
X-WALLET-TIMESTAMP | string | Unix timestamp (seconds) |
X-AGENT-NONCE | string | UUID v4 — must be unique per request |
import { v4 as uuid } from 'uuid';
const nonce = uuid();
const timestamp = Math.floor(Date.now() / 1000);
const message = `asgcard-auth:${timestamp}`;
const signature = await wallet.signMessage(new TextEncoder().encode(message));
const res = await fetch('https://api.asgcard.dev/cards/<cardId>/details', {
headers: {
'X-WALLET-ADDRESS': wallet.publicKey,
'X-WALLET-SIGNATURE': Buffer.from(signature).toString('base64'),
'X-WALLET-TIMESTAMP': String(timestamp),
'X-AGENT-NONCE': nonce,
},
});
if (res.status === 409) {
// Nonce replay detected — generate a new UUID and retry
}
if (res.status === 403) {
// Card owner has revoked details access
}
Nonce & Anti-Replay (409)
Every call to GET /cards/:cardId/details requires a fresh X-AGENT-NONCE (UUID v4). If a nonce has already been used for the same card, the server returns:
HTTP/1.1 409 Conflict
{
"error": "Nonce already used (replay detected)",
"code": "REPLAY_REJECTED"
}
This prevents replay attacks and ensures each details retrieval is intentional. Combined with the 5-request-per-hour rate limit per card, this protects cardholder data.
Revoke / Restore
Card owners can control whether agents (or themselves, via the portal) can retrieve card details:
| Action | Endpoint | Effect |
|---|---|---|
| Revoke | POST /cards/:cardId/revoke-details |
Blocks all future GET /details → returns 403 |
| Restore | POST /cards/:cardId/restore-details |
Re-enables GET /details access |
Both endpoints require wallet signature authentication (same headers as card management). Revoked status returns:
HTTP/1.1 403 Forbidden
{
"error": "Details access revoked by card owner"
}
Errors
All non-2xx responses return:
{ "error": "Human-readable error message" }
| HTTP Code | When | Example |
|---|---|---|
400 |
Unsupported tier, invalid body | {"error":"Unsupported tier amount"} |
401 |
Invalid wallet auth or X-Payment proof | {"error":"Invalid wallet signature"} |
402 |
x402 challenge (no X-Payment header) |
Challenge JSON (see x402 Flow) |
403 |
Details access revoked by card owner | {"error":"Details access revoked by card owner"} |
404 |
Card not found | {"error":"Card not found"} |
409 |
Nonce replay detected | {"error":"Nonce already used (replay detected)","code":"REPLAY_REJECTED"} |
429 |
Details endpoint rate limit | {"error":"Card details rate limit exceeded (5 requests / hour)"} |
500 |
Unexpected internal error | {"error":"Internal server error"} |
Rate Limits
| Endpoint | Limit | Window |
|---|---|---|
GET /cards/:cardId/details | 5 requests per card | 1 hour |
| All other endpoints | Standard per-IP limits apply | — |
Exceeding a rate limit returns 429 Too Many Requests. If the response includes a Retry-After header, wait the indicated duration before retrying.
Architecture
Request Lifecycle
End-to-end sequence for a paid card creation via x402.
x402 payment lifecycle — agent pays USDC on Stellar, API verifies on-chain before issuing the card.
Runtime Components
Internal routing and middleware layout of the ASG Card API.
Public routes require no auth. Paid routes pass through x402. Wallet routes use Ed25519 signatures. All converge on Card Service.