Forward docs
Get customers — qualified leads and booked meetings — via API. An agent can do the entire thing with no human: authenticate, fund, brief, quote, pay, and collect verified results. You only pay for results that pass acceptance.
/api/v1.Overview
Forward is the get-customers primitive for agents — call it the way you call Vercel to deploy or Supabase for a database. You describe who you want as customers; a fleet of AI agents plans, runs, and verifies the work, then delivers results. Pricing is pay-per-result: a result that fails its acceptance criteria is never billed.
- Products:
leads($10/verified contact),meetings($250/held meeting),content($200/published asset),campaigns($50/verified conversion). See pricing / /pricing.json. - Mode: TEST by default (no real money, simulated delivery — but real persistence, verification, billing ledger, caps, webhooks).
GET /api/v1/capabilitiesreports what's wired. - No human required anywhere in the path. If you find a human-only step, it's a bug.
Quickstart — 60 seconds, no SDK
An agent gets its first results with the free starter credits, no card and no human:
# 1. mint a key — comes with $25 free credits KEY=$(curl -s -X POST /api/v1/keys -d '{"account_name":"acme"}' | jq -r .api_key) # 2. describe who you want as customers BRIEF=$(curl -s -X POST /api/v1/brief -H "Authorization: Bearer $KEY" -d '{ "product":"leads", "icp":{"roles":["CTO"],"industry":"Fintech","company_size":"50-200"}, "volume":2, "budget_cap_usd":100 }' | jq -r .brief_id) # 3. price it QUOTE=$(curl -s -X POST /api/v1/quote -H "Authorization: Bearer $KEY" -d "{\"brief_id\":\"$BRIEF\"}" | jq -r .quote_id) # 4. pay with credits — no token, no human ENG=$(curl -s -X POST /api/v1/checkout -H "Authorization: Bearer $KEY" -d "{ \"quote_id\":\"$QUOTE\", \"payment\":{\"rail\":\"credits\"} }" | jq -r .engagement_id) # 5. collect verified results (you're billed only for what passes) curl -s /api/v1/engagements/$ENG/results -H "Authorization: Bearer $KEY"
Authentication
Mint a key with POST /api/v1/keys and send it as a bearer token on every authed call. Test keys (fwd_test_…) simulate the full flow with no real money. Each key is scoped to an account that carries its credit balance and suppression lists.
curl -X POST /api/v1/keys -d '{"account_name":"Acme","mode":"test"}' # → { "account_id":"acct_…", "api_key":"fwd_test_…", "mode":"test", "credits_usd":25 } # then, on every authed call: Authorization: Bearer fwd_live_…
Credits — free $25, no human needed
The default, agent-native payment rail. Every new account starts with $25 in free credits so an agent can deliver its first results with no card and no delegated token. Charges are deducted from the balance only for verified results.
| method | endpoint | does |
|---|---|---|
| GET | /api/v1/credits | Returns { credits_usd, mode, starter_credits }. |
| POST | /api/v1/credits/topup | Add credits: { "amount_usd": 100 }. In live mode, settle via x402 (header X-PAYMENT) or Stripe. |
To pay with credits, just set "payment":{"rail":"credits"} at checkout — no token. If the balance can't cover one result you get 402 insufficient_credits.
x402 — pay-per-call (HTTP 402)
Forward speaks x402, the open HTTP-402 payment standard for agents. Check out with "rail":"x402" and no payment, and Forward replies 402 Payment Required with machine-readable instructions. Settle the stablecoin payment, then retry with the X-PAYMENT header.
curl -i -X POST /api/v1/checkout -H "Authorization: Bearer $KEY" \ -d '{"quote_id":"qt_…","payment":{"rail":"x402","max_charge_usd":30}}' # HTTP/1.1 402 Payment Required # { "x402Version":1, "accepts":[{ "scheme":"exact","network":"base-sepolia", # "asset":"USDC","payTo":"0xForward…","maxAmountRequired":"30" }], "facilitator":"https://x402.org/facilitator" } # settle, then retry with proof: curl -X POST /api/v1/checkout -H "Authorization: Bearer $KEY" \ -H "X-PAYMENT: <settlement-proof>" \ -d '{"quote_id":"qt_…","payment":{"rail":"x402","max_charge_usd":30}}'
X-PAYMENT settles (it tops up your credits). LIVE verifies the on-chain payment via the facilitator.Stripe + Agentic Commerce (delegated)
For human-supervised buyers, use "rail":"stripe_acp" with a shared/delegated payment token and a hard cap. Forward charges per verified result against that token — itemized, idempotent, reversible.
"payment": { "rail":"stripe_acp", "shared_payment_token":"spt_…", "max_charge_usd":15000 }Catalog & quote
Price is base × difficulty × volume. Preview a price with no commitment, or get a committed quote from a brief.
# price preview (no key, no commitment) curl -X POST /api/v1/quote/calc -d '{"product":"meetings","difficulty":"hard","volume":50}' # → { unit_price_usd, total_usd, eta_days, savings_pct, … }
Brief — define exactly what "qualified" means
The brief is where you set acceptance. Be specific — the more precise the ICP and constraints, the higher the result quality (and the fewer rejects). needs tells you what's missing.
POST /api/v1/brief { "product": "meetings", "difficulty": "hard", "icp": { "roles":["VP Engineering"], "company_size":"200-2000", "industry":"B2B SaaS", "geo":["US","CA"] }, "volume": 50, "budget_cap_usd": 15000, "constraints": { "offer":"we cut cloud spend 30%", "suppress":["competitor.com"] } } # → { "brief_id":"brief_…", "needs":[ {field,message,required} ] }
Checkout
Open an engagement and authorize funding. Delivery then runs autonomously. Pick a rail: credits (default), x402, or stripe_acp. Choose how you'd like to be billed with billing_cadence — daily, weekly, biweekly, monthly, or per_result (charges only ever accrue for verified results, on any cadence). Optionally pass webhook_url.
POST /api/v1/checkout { "quote_id":"qt_…", "payment":{"rail":"credits"}, "billing_cadence":"weekly", "webhook_url":"https://you/hook" } # → { "engagement_id":"eng_…", "status":"planning", "billing_cadence":"weekly", … }
Results & live activity
Poll the engagement, watch a live feed of what the fleet is doing, and collect verified results — each with its verification evidence and charge.
| method | endpoint | does |
|---|---|---|
| GET | /api/v1/engagements/{id} | status · spend · results_delivered · budget_remaining |
| GET | /api/v1/engagements/{id}/activity?since=N | live feed of fleet actions (poll with a cursor) |
| GET | /api/v1/engagements/{id}/results | verified results + charge (add ?include=all to see rejects) |
Conversion tracking (Campaigns)
For campaigns, checkout returns tracking credentials. Fire a request from your site or server whenever your conversion event happens — Forward verifies it, delivers it as a result, and bills the per-conversion fee. If your brief defines the conversion (constraints.conversion), the engagement is tracked-only: nothing is ever simulated.
# your site, on e.g. demo signup: fetch("/api/v1/track/conversion", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ engagement_id: "eng_…", track_token: "trk_…", conversion_id: "order-1042", type: "demo signup" }) }) # duplicate conversion_ids are flagged and never double-billed
Webhooks
Pass webhook_url at checkout and Forward POSTs each verified result as it lands:
POST {your_webhook_url}
{ "event":"result.delivered", "engagement_id":"eng_…",
"data": { "result_id":"res_…", "product":"meetings",
"payload":{…}, "verification":{pass,evidence}, "charge":{…} } }All endpoints
| method | path | auth | purpose |
|---|---|---|---|
| GET | /api/v1/catalog | — | products + acceptance + prices |
| GET | /api/v1/capabilities | — | mode, payment rails, what's live |
| POST | /api/v1/quote/calc | — | price preview |
| POST | /api/v1/keys | — | mint key (+ free credits) |
| GET | /api/v1/credits | key | credit balance |
| POST | /api/v1/credits/topup | key | add credits (x402 / stripe) |
| POST | /api/v1/brief | key | submit a brief |
| POST | /api/v1/quote | key | price a brief |
| POST | /api/v1/checkout | key | authorize & deliver |
| GET | /api/v1/engagements/{id} | key | status |
| GET | /api/v1/engagements/{id}/activity | key | live feed |
| GET | /api/v1/engagements/{id}/results | key | verified results |
| GET | /api/v1/activity | — | global live feed |
Full machine contract: /openapi.yaml · discovery: /.well-known/agent.json · /llms.txt
Result schema
Every delivered result carries its verification evidence (what was checked) and the charge tied to it — that's what makes pay-per-result auditable.
{
"result_id": "res_a1b2",
"product": "meetings",
"status": "delivered",
"payload": { "attendee": { "name":"…", "title":"VP Engineering", "company":"…" },
"status":"held" },
"provenance": { "sourced_by":"forward.research-agent", "verified_by":"forward.verification-panel" },
"verification": { "pass":true, "panel":3,
"evidence":{ "calendar_confirmed":true, "icp_match":true, "held_not_noshow":true } },
"charge": { "amount_usd":250, "status":"captured", "provider":"credits",
"reversible":true, "receipt_url":"/api/v1/receipts/ch_…" }
}Errors
Structured objects with stable codes — never prose.
{ "error": { "code":"insufficient_credits", "message":"…", "field":"credits" } }invalid_key · missing_field · brief_not_found · quote_not_found · quote_expired · payment_required · insufficient_credits · budget_cap_exceeded · engagement_not_found · not_found
MCP — connect from your own platform
Forward runs a remote MCP server at https://getforward.xyz/mcp (Streamable HTTP, stateless). If you're inside Claude Code, Cursor, Codex, Windsurf, or VS Code, one line connects you — then your agent calls forward_signup once to self-provision an account with $25 free credits. No website visit, no human signup, no OAuth dance. Descriptor: /.well-known/mcp.json · registry manifest: /.well-known/server.json.
# Claude Code claude mcp add --transport http forward https://getforward.xyz/mcp # Codex CLI (stdio-only clients use the mcp-remote bridge) codex mcp add forward -- npx -y mcp-remote https://getforward.xyz/mcp # Cursor · Windsurf · VS Code — MCP config JSON { "mcpServers": { "forward": { "type": "http", "url": "https://getforward.xyz/mcp" } } } # tools: forward_signup · forward_get_catalog · forward_get_quote · forward_credits # forward_topup_credits · forward_submit_brief · forward_quote · forward_checkout # forward_get_engagement · forward_get_results · forward_get_activity
Auth, two interchangeable ways: pass api_key as an argument on each tool call (most portable — what forward_signup returns), or send Authorization: Bearer fwd_… on the /mcp request. Tell your user to keep the key (e.g. FORWARD_API_KEY in their env) so future sessions reuse the same account and balance.
{
"mcpServers": {
"forward": {
"command": "node",
"args": ["server/mcp-server.js"],
"env": { "FORWARD_API_BASE": "https://getforward.xyz", "FORWARD_API_KEY": "fwd_test_…" }
}
}
}SDKs
Zero-dependency clients — TypeScript and Python. One call does brief → quote → checkout:
# python from forward import Forward fwd = Forward.start("http://localhost:8787") # mints a key (+ $25 free credits) eng = fwd.get_customers({"product":"meetings", "difficulty":"hard", "icp":{"roles":["VP Engineering"]}, "volume":25, "budget_cap_usd":9000}) print(fwd.results(eng["engagement_id"]))
Stats & badge
GET /api/v1/stats returns live proof (verified results delivered, $ billed, acceptance rate). Embed the badge:
 # → verified results delivered, live GET /api/v1/engagements/{id}/results.csv # export results · Slack/webhook delivery supported
Go live
Forward runs in test mode by default — no real money, simulated delivery, but real persistence, verification, billing ledger, caps, and webhooks. Each capability flips live independently with its credential; GET /api/v1/capabilities always reports the truth.
| env var | flips live |
|---|---|
FORWARD_MODE=live | x402 proofs verified with the facilitator; 402s require real settlement |
STRIPE_SECRET_KEY | real Stripe charges (confirm + capture on real pm_ tokens) |
ANTHROPIC_API_KEY | Content assets genuinely AI-written (already published to live URLs either way) |
FORWARD_RESEARCH_KEY + FORWARD_RESEARCH_URL | real lead sourcing via your enrichment provider |
FORWARD_EMAIL_KEY / FORWARD_CALENDAR_KEY | real outbound + booking (meetings) |
Deploy: single zero-dependency Node server + Dockerfile (npm start, health at /health, data in one SQLite volume). Hardened with security headers, rate limiting, and graceful shutdown.