Build on Aemulus
Run skills, read their proof, and verify receipts programmatically. One API key, REST over HTTPS, verifiable by anyone.
Run a skill in one request
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" }curl https://aemulusai.com/api/v1/runs/run_… \
-H "Authorization: Bearer aem_live_…"
# → { "status":"completed", "output":{"total":"$42.00"}, "receiptHash":"…" }curl https://aemulusai.com/api/verify/run_…
# → { "matches": true, "batch": { "proofValid": true, "root": "…" } }Or skip the curl
A tiny, dependency-free client lives in the Aemulus repo (sdk/). Works anywhere fetch does - Node, browsers, Deno, edge.
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); // trueGive 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.
{
"mcpServers": {
"aemulus": {
"url": "https://aemulusai.com/api/mcp",
"headers": { "Authorization": "Bearer aem_live_…" }
}
}
}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.
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.
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.
Webhook events
Status events: { event, runId, skillId, status, receiptHash, at }. The opt-in run.output event carries the extracted data: { event, runId, skillId, output, at }.