Skip to content
Pre-launch$AEMU isn't live yet - everything is free to use. The token launches soon on pump.fun.
Aemulus
Open protocol

Build on Aemulus

Run skills, read their proof, and verify receipts programmatically. One API key, REST over HTTPS, verifiable by anyone.

POST/api/v1/runs
GET/api/v1/runs/:id
GET/api/v1/skills
GET/api/verify/:runId
GET/api/batch/:id/bundle
Quickstart

Run a skill in one request

Run a skill
curl -X POST https://aemulusai.com/api/v1/runs \
  -H "Authorization: Bearer aem_live_…" \
  -H "Content-Type: application/json" \
  -d '{"skillId":"skl_…","input":{"vendor":"Acme","amount":"1499"}}'
# → { "id": "run_…", "status": "running" }
Poll the run + read extracted output
curl https://aemulusai.com/api/v1/runs/run_… \
  -H "Authorization: Bearer aem_live_…"
# → { "status":"completed", "output":{"total":"$42.00"}, "receiptHash":"…" }
Verify the receipt - no key, anyone can
curl https://aemulusai.com/api/verify/run_…
# → { "matches": true, "batch": { "proofValid": true, "root": "…" } }
TypeScript SDK

Or skip the curl

A tiny, dependency-free client lives in the Aemulus repo (sdk/). Works anywhere fetch does - Node, browsers, Deno, edge.

sdk/ - run, read output, verify
import { Aemulus } from "@/sdk";

const aemulus = new Aemulus({ apiKey: process.env.AEMULUS_KEY! });

// run across one input - or loop a CSV for the next hundred
const run = await aemulus.runAndWait("skl_…", { vendor: "Acme", amount: "1499" });
console.log(run.status);   // "completed"
console.log(run.output);   // { total: "$42.00" }

// anyone can verify the receipt - no key
const v = await aemulus.verify(run.id);
console.log(v.batch?.proofValid);  // true
MCP server

Give your agent verifiable hands

Aemulus is a Model Context Protocol server - point any MCP client (Claude, your agent) at it and the marketplace becomes callable tools: list_skills, run_skill, get_run, verify_receipt. The agent runs real browser tasks and gets back proof.

MCP client config
{
  "mcpServers": {
    "aemulus": {
      "url": "https://aemulusai.com/api/mcp",
      "headers": { "Authorization": "Bearer aem_live_…" }
    }
  }
}
Authentication

API keys

Keys authenticate as your wallet - your skills, quota, and earnings all apply. Send as a Bearer token.

Connect your wallet to create an API key. Keys belong to your wallet - only you can ever see them, and they're hidden the moment you sign out.

Nothing here is shown until you connect your wallet.
Webhooks

Get pinged when a run finishes

Subscribe a URL to run.completed, run.needs_review, run.failed, or run.output (extracted results, as a data destination) - each HMAC-signed so you can trust it.

Verify the signature (Node)
import { createHmac, timingSafeEqual } from "node:crypto";

// header: "t=<unix>,sha256=<hex>"  - signed payload is `${t}.${rawBody}`
const [tPart, sigPart] = req.headers["x-aemulus-signature"].split(",");
const t = tPart.slice(2), sig = sigPart.slice(7);
// reject stale/replayed deliveries (5-min tolerance)
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) throw new Error("stale");
const mac = createHmac("sha256", WHSEC).update(t + "." + rawBody).digest("hex");
const ok = timingSafeEqual(Buffer.from(sig), Buffer.from(mac));
// status events → { event, runId, skillId, status, receiptHash, at }
// run.output    → { event, runId, skillId, output, at }

Connect your wallet to register webhooks - they belong to your wallet.

Nothing here is shown until you connect your wallet.
Reference

Webhook events

run.completedA run finished successfully
run.needs_reviewA step needs human input
run.failedA run errored out
run.outputA run captured extracted data (output destination)

Status events: { event, runId, skillId, status, receiptHash, at }. The opt-in run.output event carries the extracted data: { event, runId, skillId, output, at }.

Status codes

200OK
400Invalid request body
401Missing or invalid API key
403Insufficient $AEMU balance or missing key scope
404Skill / run / batch not found
409Idempotency-Key already in progress
429Rate limit or daily quota reached