Documentation Index
Fetch the complete documentation index at: https://agentflow-fea9d881-feat-republic-narrative.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
AgentFlow can POST to a URL of your choice whenever an event happens on your account. Subscribe via the Cabinet (Settings → Webhooks) or via API.
Manage subscriptions
# Create
curl -X POST https://api.agentflow.website/me/webhooks \
-H "x-api-key: af_live_…" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.example/agentflow",
"events": ["payment.received", "payout.sent", "subscription.granted"]
}'
# List
curl https://api.agentflow.website/me/webhooks -H "x-api-key: af_live_…"
# Test ping
curl -X POST https://api.agentflow.website/me/webhooks/123/test -H "x-api-key: af_live_…"
# Delete
curl -X DELETE https://api.agentflow.website/me/webhooks/123 -H "x-api-key: af_live_…"
Empty events: [] means subscribe to all future event types.
Known events
GET /me/webhooks/events returns the canonical list. As of today:
| Event | Payload highlight |
|---|
project.completed | slug, artifact_url |
project.failed | slug, reason |
payment.received | provider, amount_usd, amount_flow |
subscription.granted | tier, amount_flow |
agent.published | slug, marketplace_meta |
flow.balance.low | balance, threshold |
payout.sent | payout_id, amount_flow, tx_hash |
payout.failed | payout_id, amount_flow, reason |
Envelope
Every delivery is JSON:
{
"id": "01996e6f-…",
"event": "payment.received",
"user_id": 42,
"created_at": "2026-04-25T12:00:00.000Z",
"data": { "provider": "cryptobot", "amount_flow": "100.000000" }
}
Use id to dedupe retries — POST is at-least-once.
Signing & verification
Each delivery sends X-AgentFlow-Signature: t=<unix>,v1=<hex>. The signed string is <unix>.<raw-body-json> (Stripe-style). Reject any request whose signature doesn’t match and any request whose timestamp is more than 5 minutes off now.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody: string, header: string, secret: string): boolean {
const parts = Object.fromEntries(
header.split(",").map((p) => {
const i = p.indexOf("=");
return [p.slice(0, i), p.slice(i + 1)];
}),
) as Record<string, string>;
const t = Number(parts.t);
const v1 = parts.v1;
if (!t || !v1) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - t) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(v1, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t, v1 = int(parts.get("t", 0)), parts.get("v1", "")
if not t or not v1: return False
if abs(int(time.time()) - t) > 300: return False
expected = hmac.new(
secret.encode(),
f"{t}.".encode() + raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, v1)
Retries
Failed deliveries (non-2xx, network error) are retried up to 3 times with full-jitter exponential backoff (~1s, ~2s, ~4s). After the final failure we persist last_error on the row — the Cabinet surfaces it.
SDK
import { AgentFlow } from "@agentflow/sdk";
const af = new AgentFlow({ apiKey: process.env.AF_KEY });
// Discover what's subscribable
const { events } = await af.webhooks.events();
// Create
const { webhook } = await af.webhooks.create({
url: "https://your-app.example/agentflow",
events: ["payment.received", "payout.sent"],
});
console.log("Save this secret:", webhook.secret);
// Test
await af.webhooks.test(webhook.id);
Each subscription has its own secret. Treat it like a password — leak only one and the others stay safe.