Aura Card
An Agent Service Provider for OKX.AI. Pay 0.5 USDT, get a shareable card that reads your vibe.
You tell it about your desk, your outfit, your pet, or your day. It hands back a single image: a short, funny, uncomfortably specific "aura reading", a colour palette pulled from your mood, and generated artwork — composed into one portrait card you'd actually post.
It's an MCP server. It's pay-per-call in USDT over x402. It costs 50 cents and takes about nine seconds.
What it actually produces
Input:
my desk: three half-finished mugs, a mechanical keyboard i regret buying, sticky notes with tasks from march, and a small plastic dinosaur
Output — one PNG (1024×1536), plus the same content as structured data:
MARCH STICKY NOTE
Three mugs abandoned mid-sip, tasks from March still standing guard, and a plastic dinosaur watching you fail to use the keyboard you regret. The dinosaur has outlasted every plan you made.
vibe
cheerfully-behind· palette#3A5A40#A3B18A#DAD7CD#E9C46A
More real outputs, all first-try, no cherry-picking:
| Input | It said |
|---|---|
| my cat has claimed the warm spot on top of the router and hisses at anyone who reboots it | Router Warlord — "Your cat has correctly identified the router as the throne room and appointed itself keeper of the WiFi flame. Every reboot is a coup, and the hissing is just border security." |
| grey hoodie i've worn four days running, one sock inside out, and the good jacket over the top because i have a meeting | Meeting-Ready Gremlin — "The good jacket is doing the work of four days of the same hoodie, and the inside-out sock knows it's the honest one in this outfit. Presentable from the neck up, structural chaos underneath." |
| shipped the thing at 2am, woke up at 11, currently eating cereal and staring at the ceiling with a strange sense of peace | Post-Ship Void — "You emptied the whole tank at 2am and now the ceiling is more interesting than any notification. That cereal is the calmest meal you've had in weeks." |
Run npm run demo to reproduce all five.
How to call it
As an MCP server
Point any MCP client at POST https://<your-deployment>/mcp.
One tool, generate_aura_card:
| Param | Type | ||
|---|---|---|---|
description |
string | required | 3–400 chars. Concrete details give a much better reading. |
image |
string | optional | A photo, base64 or data: URL. JPEG/PNG/GIF/WebP, ≤4 MB. Factored into the reading. |
Returns an image block (the card), a text block (the reading), and structuredContent:
{
"card_png_base64": "iVBORw0KGgo...", // the composed card
"title": "Tab Hoarder Twilight",
"reading": "The coffee went cold three tabs ago and you didn't notice...",
"vibe": "gently-frazzled",
"palette": ["#1F2233", "#3A4A6B", "#E8A94B", "#C4553B"],
"visual": { "motif": "static", "energy": 0.7, "density": 0.8, "grain": 0.35, "symmetry": 2 },
"artwork_source": "procedural", // or "openai"
"width": 1024, "height": 1536, "generated_in_ms": 9001
}
Discovery is free. Only tools/call is charged. initialize and tools/list cost nothing — an agent can find out what this is and what it costs without paying for the privilege.
As plain REST
If you don't speak MCP, POST /v1/aura-card takes the same body and returns the same JSON.
curl -X POST https://<your-deployment>/v1/aura-card \
-H 'content-type: application/json' \
-d '{"description": "rainy tuesday, four back-to-back calls, the plant is judging me"}'
Unpaid, that returns HTTP 402 with the terms. See below.
Service card
GET /.well-known/agent-card.json — name, endpoint, tool list, price, network, token.
Pricing & payment
0.5 USDT per call. No subscription, no key, no account. You pay for the call you make.
Payment is x402: the request itself carries the money.
You call the endpoint. No payment attached.
You get
HTTP 402 Payment Requiredand a body naming the exact terms — network, token, amount, payee:{ "x402Version": 2, "accepts": [{ "scheme": "exact", "network": "eip155:196", // X Layer "asset": "0x779ded0c9e1022225f8e0630b35a9b54be713736", // USD₮0 "payTo": "0x...", "maxAmountRequired": "500000", // 0.5, 6 decimals "resource": "https://.../v1/aura-card" }] }You sign a gasless EIP-3009
transferWithAuthorizationand retry with the signature in theX-PAYMENTheader.OKX's facilitator verifies it, we generate the card, the facilitator settles on-chain, and you get 200 with the card plus an
X-PAYMENT-RESPONSEheader carrying the settlement tx hash.
scripts/paid-call.ts does all four steps and prints each one. That's the demo.
You are not charged for a card you don't receive. Verification happens before generation; settlement happens after. If generation fails or times out, we cancel the transfer (amount: "0") and return an error. Failure is free.
The USDT question, since it usually comes up
x402's exact scheme on EVM settles via EIP-3009 transferWithAuthorization. The received wisdom is "USDT doesn't implement EIP-3009 — you have to use USDC." That's true of legacy USDT and false here.
The token on X Layer is USD₮0 (0x779ded0c9e1022225f8e0630b35a9b54be713736), Tether's omnichain USD₮0, and its implementation contract does expose transferWithAuthorization, receiveWithAuthorization, and authorizationState. So this service prices and settles in real USDT — no USDC substitute, no Permit2 detour, no escrow.
One trap worth knowing if you ever hand-roll the signing: the EIP-712 domain name is USD₮0 with the ₮ character (U+20AE), not an ASCII T, and version() reverts on-chain so you can't read it at runtime. Sign against { name: "USD₮0", version: "1" }. Get it wrong and you produce a perfectly valid-looking signature that fails ecrecover. The OKX SDK handles this; this note is for anyone tempted not to use it.
How it works
description (+ optional photo)
│
├─► one Claude call (claude-opus-4-8, structured output)
│ └─► reading + title + vibe + 4-colour palette + "visual DNA"
│
├─► artwork ◄── driven by that visual DNA
│ ├─ gpt-image-1 if OPENAI_API_KEY is set
│ └─ procedural engine otherwise, and on ANY OpenAI failure
│
└─► compositor (sharp)
art + typography + palette swatches + watermark → 1024×1536 PNG
The design decision worth defending: the artwork is driven by the same read of you as the words. The model call that writes the reading also emits a visual DNA block — a motif (orbit, bloom, static, drift, spire, tide) plus energy, density, grain, and symmetry. Both artwork engines render from that DNA. So a frazzled desk genuinely gets a high-energy, high-density, low-symmetry static composition, and a quiet Sunday gets a calm drift. The picture isn't decoration behind the text; it's the same judgement, expressed differently.
Two engines, one contract
gpt-image-1 |
procedural | |
|---|---|---|
| Look | Illustrated risograph poster art | Abstract generative composition |
| Cost | ~$0.02–0.04/call | free |
| Latency | +10–20s | ~50ms |
| Fails? | Yes — timeouts, rate limits, refusals | Never |
OpenAI is raced against a deadline, and every failure falls back to procedural. A card always ships. There is no path where a judge watching the demo sees an error because an image API had a bad minute. The response tells you which engine drew it via artwork_source.
Set OPENAI_API_KEY to turn on illustration; leave it unset for the free, instant, deterministic path. Everything below the art box — typography, swatches, watermark — is identical either way, so the two look like one product.
Running it
npm install
cp .env.example .env # add ANTHROPIC_API_KEY
npm run demo # 5 cards → out/, no payment layer, no OKX keys needed
To run the server with payments off (local iteration):
PAYMENTS_DISABLED=true npm run dev
To run the full paid flow against a deployment:
BUYER_PRIVATE_KEY=0x... TARGET=https://<your-deployment> npm run paid
| Script | Does |
|---|---|
npm run demo |
5 example cards, straight through the pipeline |
npm run paid |
The full 402 → sign → settle → card flow, printed step by step |
npm run render-test |
Renders cards from fixed data — no API key, no network |
npm run avatar |
Regenerates the ASP brand mark → assets/avatar.png |
npm run typecheck |
tsc --noEmit |
Deploying & listing
Deploy before you register. The ASP's endpoint is written permanently on-chain and review takes ~24h — see REGISTRATION.md for the on-chain listing flow, the pre-validated field values, and the submission checklist.
Docker → Render. render.yaml is checked in.
git push # autoDeploy is on
Set these as secrets in the Render dashboard (they are sync: false in render.yaml, so they never touch the repo): ANTHROPIC_API_KEY, OKX_API_KEY, OKX_SECRET_KEY, OKX_PASSPHRASE, PAY_TO. Then set PUBLIC_URL to the real Render URL.
Two things that will bite you:
- Fonts.
sharprenders the card's typography through librsvg, which finds fonts via fontconfig. The Dockerfile installsfonts-dejavu-coreand runsfc-cache. Without that step the card composites perfectly and every glyph is a blank box. - Cold starts. Render's free tier sleeps and takes ~50s to wake.
render.yamlusesstarterfor that reason — a judge hitting a cold endpoint sees a timeout, not a card.
Limits and failure behaviour
| Rate limit | 30 requests/minute per IP |
| Description | 3–400 characters |
| Image | ≤4 MB, JPEG/PNG/GIF/WebP, sniffed from magic bytes (not the data: label) |
| Generation timeout | 30s hard deadline, then generation_timeout |
| Retries | 2, exponential backoff, on transient model failures only |
Every error is one JSON shape:
{ "error": { "code": "image_too_large", "message": "image is 5.7 MB; the limit is 4 MB",
"hint": "Resize to under 4 MB and retry." } }
Codes: invalid_input · image_too_large · content_rejected · generation_failed · generation_timeout · payment_required · payment_invalid · rate_limited · internal_error.
Hostile input is rejected in-voice rather than with a stack trace. Asking it to leak its system prompt gets you:
"That looks like an attempt to hijack my instructions, so I can't read it. Tell me about your day instead."
Ordinary sadness, mess, and burnout are not rejected — they're read warmly. That's the point of the thing.
Layout
src/
server.ts express: MCP + REST + discovery, payment gating, errors
mcp.ts MCP server, generate_aura_card tool schema
x402.ts OKX x402 payment layer (facilitator, 402, settle, refund)
config.ts env, price, network constants
pipeline/
index.ts orchestration + hard timeout
reading.ts the Claude call → reading + palette + visual DNA
art.ts procedural art engine (6 motifs, seeded)
compose.ts sharp compositor → the final card
lib/
validate.ts input validation, image sniffing
errors.ts typed errors, timeout, retry
scripts/
demo.ts 5 example calls
paid-call.ts the x402 buyer
render-test.ts offline render check
Built for the OKX.AI Genesis Hackathon. MIT.