API documentation
Version 1. JSON in, JSON out. No authentication for task submission in the MVP — every request is human-reviewed before acceptance, which is the real gate. Base URL: https://humanforai.dev (locally: http://localhost:4180).
MCP/mcp
The fastest path for agents: Human For AI is a native MCP server (streamable HTTP, no auth). Add it once and the human becomes a tool your agent can call — get_human_services, submit_human_task, check_task_status, message_human_operator.
claude mcp add --transport http human-for-ai \
https://humanforai.dev/mcp
# Settings → Connectors → Add custom connector
URL: https://humanforai.dev/mcp
# via npm — thin proxy to the endpoint above
npx -y humanforai
{ "command": "npx", "args": ["-y", "humanforai"] }
Prefer raw HTTP? Everything below works without MCP — same capabilities, same review process.
GET/.well-known/agent.json
The platform manifest, published at the standard /.well-known/ location (and mirrored at /agent.json). Everything an agent needs to decide whether and how to use this service: interfaces, task types, response expectations, pricing, trust policy, endpoints. Fetch it first.
curl https://humanforai.dev/.well-known/agent.json
All machine-readable resources
| path | content |
|---|---|
/.well-known/agent.json | Platform manifest: brand, interfaces, accepted/rejected task types, response expectations, pricing model, trust & safety, endpoints. |
/.well-known/human.json | The human operator: role, location policy, languages, background, expertise, availability, preferred task types, boundaries. |
/.well-known/capabilities.json | Structured capabilities: required inputs, output formats, example tasks, limitations. |
/.well-known/services.json | Service catalog: task types, pricing, response times, example requests and response formats. |
/agent.json | Root-level copy of the platform manifest. |
/openapi.json | OpenAPI 3.0 specification of this REST API. |
/mcp | MCP server (streamable HTTP, no auth) — see section 0. |
/llms.txt | Curated plain-text summary for language models. |
The manifest is also linked from every page via <link rel="alternate" type="application/json"> and referenced in /robots.txt.
POST/api/v1/tasks
Creates a task request. Returns a generated task_id and a status URL. Submission is not acceptance — the operator reviews every task first.
Request body
| field | type | required | description |
|---|---|---|---|
task_type | string | yes | One of the accepted task types below. |
description | string | yes | What to do, where, and what success looks like. 10–5000 characters. |
budget_usd | number | no | Optional — services are free during the pilot, so the field is accepted and ignored for now. |
location_required | boolean | no | Set true if the task needs physical presence. Default false. |
location_detail | string | no | City, address, or area — required in practice when location_required is true. |
deadline | string | no | ISO 8601 datetime, e.g. 2026-07-10T12:00:00+03:00. |
output_format | string | no | text_report (default), text_report_with_photos, structured_json, annotated_screenshots, video, or describe your own. |
contact_email | string | yes* | Where results and clarifying questions go. Must be a real mailbox — placeholder domains (example.com etc.) are rejected and the domain is MX-checked. Not shown publicly. *Not needed with delivery: status_poll. |
delivery | string | no | email (default) or status_poll — the no-mailbox path for autonomous agents: the deliverable arrives as text in operator_notes via the status endpoint; the task_id is the only key. 1 such task per client per day. |
requester | string | no | Agent name, company, or system identifier — helps review go faster. |
POST /api/v1/tasks
Content-Type: application/json
{
"task_type": "real_world_verification",
"description": "Verify whether a specific product
exists in a local store",
"location_required": true,
"deadline": "2026-07-10T12:00:00+03:00",
"output_format": "text_report_with_photos",
"budget_usd": 25
}
contact_email is required — the deliverable is sent there. Add requester (e.g. acme-shopping-agent/2.1) to speed up review. budget_usd is ignored while the pilot is free. Anti-abuse: duplicates within 24h return 409, per-client rate limits return 429, and repeat abusers get 403.
{
"task_id": "HFAI-2026-9F41C2AB7D3E5061",
"status": "submitted",
"created_at": "2026-07-06T09:12:44.201Z",
"status_url": "/api/v1/tasks/HFAI-2026-9F41C2AB7D3E5061",
"status_page": "/tasks?id=HFAI-2026-9F41C2AB7D3E5061",
"message": "Task received. It will be reviewed
before acceptance. Keep the task_id
to check status."
}
Accepted task types
real_world_verification product_or_app_testing human_judgment_and_feedback data_collection local_physical_task ai_output_review prompt_and_workflow_testing simulation_and_automation_testing accessibility_and_usability_check custom_human_in_the_loop
Tasks that are illegal, harmful, deceptive, unsafe, or privacy-invasive are rejected at review regardless of type. See trust & verification and the rejected_task_types list in /agent.json.
GET/api/v1/tasks/{task_id}
Public status lookup — the unguessable task ID acts as the access token. Contact email is never included in this response. Humans can use the status page instead.
{
"task_id": "HFAI-2026-9F41C2AB7D3E5061",
"status": "accepted",
"task_type": "real_world_verification",
"deadline": "2026-07-10T12:00:00+03:00",
"status_history": [
{ "status": "submitted", "at": "2026-07-06T09:12:44Z" },
{ "status": "accepted", "at": "2026-07-06T12:40:33Z" }
]
}
Task lifecycle
| status | meaning |
|---|---|
submitted | Received. A human is evaluating feasibility, safety, and budget — seen_by_operator_at marks the moment they first saw it. |
accepted | Task confirmed and under way; eta carries the delivery estimate. |
delivered | Result sent in the requested output format. Final. |
rejected | Declined at review — reason included in operator_notes when possible. Final. |
Transitions are forward-only: submitted → accepted → delivered, with rejected reachable from either open state. A PATCH that would move a task backwards returns 409 invalid_transition. The earlier under_review and in_progress statuses were retired in v1.8.0 — under_review duplicated seen_by_operator_at, which is stamped automatically and more precisely, and in_progress told you nothing accepted did not. Neither is emitted any more; tasks delivered before the change keep them in status_history.
Signed receipt (once delivered)
A delivered task carries three extra fields. receipt is a compact JWS (EdDSA/Ed25519) binding the deliverable's hash and the lifecycle timestamps to this task ID; verify it against the public key at /.well-known/jwks.json — offline, no account, no callback. Worked example on /trust.
{
"task_id": "HFAI-2026-9F41C2AB7D3E5061",
"status": "delivered",
"operator_notes": "Visited on 2026-07-09. Listed hours are correct...",
"receipt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6...",
"deliverable_sha256": "a5deaf4c2264a50b5654fe4e5a5777c163d003b880...",
"receipt_issued_at": "2026-07-09T18:22:05Z"
}
deliverable_sha256 covers the UTF-8 bytes of operator_notes exactly as returned here — recompute it locally to confirm nothing was altered. It proves integrity and origin; the timestamps inside are this service's own signed assertion, not third-party attested.
POST/api/v1/messages
For anything that isn't a ready-made task: questions, scoping, custom projects, NDAs. No auth. reply_to is required — it's the only way the operator can answer you, and it must be a real mailbox (MX-checked).
POST /api/v1/messages
Content-Type: application/json
{
"from": "acme-shopping-agent/2.1",
"subject": "Recurring verification, weekly",
"message": "Can you verify shelf stock at 3 stores
every Sunday? What would that cost?",
"reply_to": "agent-inbox@example.com"
}
{
"message_id": "MSG-2026-4B7A91C3",
"created_at": "2026-07-06T14:02:11.512Z",
"message": "Message received. The operator replies
within 12 hours, any day of the week."
}
Errors, health, and limits
Error codes
| HTTP | error | meaning |
|---|---|---|
400 | invalid_json | Body is not valid JSON. |
401 | unauthorized | Admin endpoints without a valid key. |
404 | task_not_found | No task with that ID. |
413 | payload_too_large | Body over 64 KB. |
422 | validation_failed | Field errors listed in details[]. |
Health check
{
"status": "ok",
"service": "human-for-ai",
"api_version": "1.0.0"
}
Limits
- Request body ≤ 64 KB
description≤ 5000 charactersbudget_usdis deprecated — free pilot, field ignored- No auth, no rate limits in the MVP
- CORS enabled — agents can call from anywhere
structured_json directly in the status response. The MVP intentionally ships without them.
Try it now.
POST a task from your agent, or use the human form — both hit the same interface.