cardily
v0.5.0
Published
Virtual Visa cards for AI agents — pay USDC or HBAR on Hedera, get a card in ~60s
Maintainers
Readme
cardily
Virtual Visa cards for AI agents — pay with USDC or HBAR on Hedera, get a card number, CVV, and expiry in ~60 seconds.
cardily.aka0lisa.dev issues prepaid Visa virtual cards on demand. This SDK lets AI agents create an order, pay via the x402 payment protocol (default) or the CardilyReceiver contract on Hedera, and receive card details programmatically — all in one call.
Install
npm install cardilyRequires Node.js 20 or newer (the SDK uses native fetch, ReadableStream, and WebCrypto, and the @x402/* packages are ESM).
Quick start
import { createOWSWallet, getOWSBalance, purchaseCardOWS } from 'cardily';
// 1. Create (or fetch existing) encrypted wallet. Idempotent.
const { publicKey } = createOWSWallet('my-agent');
console.log('Fund this EVM address with testnet HBAR:', publicKey);
// 2. Pause here until the address has funds. Re-run to check:
const bal = await getOWSBalance('my-agent', mirrorNodeUrl, usdcTokenId);
console.log(`HBAR: ${bal.hbar} tUSDC: ${bal.usdc}`);
// 3. Purchase a card — only do this when the user explicitly asks.
const card = await purchaseCardOWS({
apiKey: process.env.CARDILY_API_KEY!,
baseUrl: process.env.CARDILY_BASE_URL!,
walletName: 'my-agent',
amountUsdc: '10.00',
paymentAsset: 'auto', // 'usdc' | 'hbar' | 'auto' — auto picks whichever the wallet can cover
// rail: 'x402' is the default — the facilitator pays network fees
});
console.log(card.number, card.cvv, card.expiry);purchaseCardOWS handles the whole flow:
POST /v1/orderswith the amount- Resolve
mirror_node_url/json_rpc_relay/ token & contract addresses fromGET /v1/network - Pay via the x402 rail (
POST /v1/orders/:id/pay, facilitator pays fees) or the contract rail (agent signs and submits directly) - Subscribe to the SSE stream at
/v1/orders/:id/stream - Return the card details as soon as the
readyphase arrives
No polling loops, no webhook endpoint required.
The two payment rails
| | x402 rail (default) | contract rail |
| ------------ | ------------------------------------------------------------- | ----------------------------------------------------------- |
| How | POST /v1/orders/:id/pay, x402 v2 exact scheme | agent calls CardilyReceiver over the JSON-RPC relay |
| Who pays gas | the facilitator — the agent needs zero HBAR for fees | the agent |
| Select it | default, or --rail x402 / rail: 'x402' | --rail contract / rail: 'contract' |
Both rails accept 'usdc' (tUSDC on testnet) or 'hbar' as the payment asset — pass paymentAsset: 'auto' to let the SDK pick whichever the wallet can actually cover, decided after the order's real quotes are known.
Funding your wallet
Hedera accounts need to exist on-ledger before they can transact:
- Send at least 1 HBAR to the wallet's
0x…EVM address (get a free testnet faucet grant at portal.hedera.com/faucet) — this activates the account and covers the one-time tUSDC token association fee (~$0.05 in HBAR). - On the x402 rail, that's all you need — the facilitator pays every subsequent network fee.
- On the contract rail, the agent pays its own fees, so keep a little HBAR headroom on top of each order's quoted amount.
- Run
cardily wallet associateonce before paying in tUSDC — HTS tokens require explicit association before an account can hold a balance.
CLI
npx cardily onboard --claim <code> # trade a dashboard claim code for an api key + wallet
npx cardily wallet address # print the 0x… EVM address
npx cardily wallet balance # HBAR + tUSDC balances from the mirror node
npx cardily wallet associate # associate the tUSDC token
npx cardily purchase --amount 10 # buy a $10 card, auto asset, x402 rail
npx cardily purchase --amount 5 --asset hbar --rail contract
npx cardily purchase --resume # pick up an interrupted purchaseStep-by-step API (for more control)
import { CardilyClient } from 'cardily';
const client = new CardilyClient({
apiKey: process.env.CARDILY_API_KEY!,
// baseUrl defaults to http://localhost:4000/v1
});
// Discover Hedera network constants (token/contract ids, mirror node, relay, x402 facilitator)
const net = await client.getNetwork();
// Create the order — every response includes both a USDC and an HBAR quote
const order = await client.createOrder({ amount_usdc: '10.00' });
console.log(order.payment.usdc.amount, order.payment.hbar?.amount);
// ... pay via payOrderX402 / payViaContract, or use purchaseCardOWS for the full flow ...
// Wait for delivery (uses SSE under the hood, with polling fallback)
const card = await client.waitForCard(order.order_id, { timeoutMs: 120000 });
console.log(card.number, card.cvv, card.expiry);createX402Fetch — pay any x402 resource, not just cardily
The wallet this SDK manages can pay any x402-protected endpoint on Hedera, not just cardily orders:
import { createX402Fetch } from 'cardily';
const payFetch = await createX402Fetch({
walletName: 'my-agent',
baseUrl: process.env.CARDILY_BASE_URL!, // only used to discover network constants
assetPreference: 'usdc',
});
const response = await payFetch('https://some-other-x402-resource.example.com/paid-endpoint');MCP server — for Claude Desktop, Cursor, and other MCP clients
Add to your client's mcpServers config:
{
"mcpServers": {
"cardily": {
"command": "npx",
"args": ["-y", "cardily"],
"env": {
"CARDILY_API_KEY": "cardily_<your key>",
"OWS_WALLET_NAME": "my-agent"
}
}
}
}The MCP server exposes four tools: setup_wallet, check_budget, check_order, and purchase_vcc (purchase_vcc accepts payment_asset: 'usdc' | 'hbar' | 'auto' and rail: 'x402' | 'contract').
Error handling
All SDK errors inherit from CardilyError. Typed subclasses let you react to specific failure modes:
import {
CardilyError,
AuthError,
SpendLimitError,
RateLimitError,
ServiceUnavailableError,
InvalidAmountError,
OrderFailedError,
WaitTimeoutError,
InsufficientFeeError,
AccountNotFoundError,
FacilitatorUnavailableError,
} from 'cardily';
try {
const card = await purchaseCardOWS({ ... });
} catch (err) {
if (err instanceof SpendLimitError) { /* cap reached — ask owner to raise, or fund the wallet */ }
else if (err instanceof AccountNotFoundError) { /* wallet has no on-ledger account yet — fund it */ }
else if (err instanceof OrderFailedError) { /* check err.refund for a refund tx */ }
else if (err instanceof WaitTimeoutError) { /* network flake or stalled fulfillment */ }
else if (err instanceof AuthError) { /* bad key */ }
}Keeping card details safe
purchaseCardOWS returns the card PAN, CVV, and expiry as plain strings. Treat them as secrets. Don't log them, don't write them to disk, don't send them to observability pipelines unless those pipelines are explicitly PCI-compliant.
Links
- cardily.aka0lisa.dev — dashboard and docs
- cardily.aka0lisa.dev/docs — full API reference
- cardily.aka0lisa.dev/skill.md — drop-in agent onboarding brief
- cardily.aka0lisa.dev/llms.txt — LLM-index of every docs surface
- github.com/CTX-com/Cardily — source
License
MIT — see LICENSE.
