Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.awaithumans.dev/llms.txt

Use this file to discover all available pages before exploring further.

POST /api/tasks
Authorization: Bearer <ADMIN_API_TOKEN>
Content-Type: application/json
Most users go through the SDK’s await_human() rather than calling this directly. Use this endpoint when:
  • You’re building a non-SDK client
  • You want to create a task without polling (e.g. fire-and-forget with callback_url)
  • You’re testing the wire format

Request

{
  "task": "Approve $250 refund?",
  "payload": {
    "amount_usd": 250,
    "customer_id": "cus_demo"
  },
  "payload_schema": {
    "type": "object",
    "properties": { "amount_usd": {"type": "integer"}, "customer_id": {"type": "string"} },
    "required": ["amount_usd", "customer_id"]
  },
  "response_schema": {
    "type": "object",
    "properties": { "approved": {"type": "boolean"}, "notes": {"type": "string"} },
    "required": ["approved"]
  },
  "form_definition": null,
  "timeout_seconds": 900,
  "idempotency_key": "refund:order-12345",
  "assign_to": {"email": "alice@acme.com"},
  "notify": ["slack:#approvals", "email:alice@acme.com"],
  "verifier_config": null,
  "redact_payload": false,
  "callback_url": null
}
FieldTypeRequiredDescription
taskstringyesHuman-readable description (shown in UIs).
payloadobjectyesData the human reviews.
payload_schemaJSON SchemayesSchema for payload. The dashboard validates against it.
response_schemaJSON SchemayesSchema for the response. Drives the form UI.
form_definitionobject | nullnoChannel-specific UI hints (the SDK extracts this from Pydantic / Zod). Optional — falls back to generic JSON Schema rendering.
timeout_secondsintyes60 to 2,592,000.
idempotency_keystringyesApplication-controlled. See Idempotency.
assign_toobject | nullnoRouting. See Routing.
notifystring[] | nullnoChannel destinations.
verifier_configobject | nullnoServer-side verification. See Verifier.
redact_payloadboolnoDefault false. Skips audit logging of response keys + verifier prompt.
callback_urlstring | nullnoURL to POST to on terminal status (Temporal/LangGraph adapters use this).

Response

HTTP/1.1 201 Created
Content-Type: application/json
{
  "id": "tsk_4f8a2c1e9b...",
  "idempotency_key": "refund:order-12345",
  "status": "created",
  "task": "Approve $250 refund?",
  "payload": { ... },
  "payload_schema": { ... },
  "response_schema": { ... },
  "assign_to": { "email": "alice@acme.com" },
  "assigned_to_email": "alice@acme.com",
  "response": null,
  "verifier_result": null,
  "verification_attempt": 0,
  "timeout_seconds": 900,
  "redact_payload": false,
  "created_at": "2026-05-12T08:00:00.000Z",
  "updated_at": "2026-05-12T08:00:00.000Z",
  "completed_at": null,
  "timed_out_at": null,
  "completed_by_email": null,
  "completed_via_channel": null
}

Idempotency behavior

Strict Stripe-style: same key, same task, always. If any task with idempotency_key already exists — terminal or not — the response is 200 OK with the existing task. The client should treat this as success and proceed to poll. If the existing task is already terminal, the next poll returns immediately with the stored response (for completed) or the appropriate terminal status (for timed_out, cancelled, verification_exhausted), and the SDK translates those into typed errors. This is what makes direct-mode await_human() resumable across agent restarts: a re-invocation with the same key after the human completed during your downtime returns the stored decision instead of creating a duplicate ticket. To create a fresh task for the same logical event (e.g. a yesterday’s task timed out and you want a new review today), pass a distinct key — convention is to suffix with a retry counter:
# In real code these come from your own state:
order_id = "order_12345"
attempt = 2
idempotency_key = f"refund:{order_id}:retry-{attempt}"

Errors

Statuserror_codeCause
400(validation)Body shape mismatch.
401Missing or wrong Authorization header.
403Caller is a logged-in non-operator.
422(validation)timeout_seconds out of range, etc.

Notification side-effects

Channel notifications fire in a BackgroundTask AFTER the response is sent — a slow Slack API call won’t block your task creation, and a Slack outage won’t fail a successful task write. The dashboard sees the task immediately regardless.

SDK equivalent

# Python
from pydantic import BaseModel
from awaithumans import await_human_sync


class RefundPayload(BaseModel):
    amount_usd: int
    customer_id: str


class RefundDecision(BaseModel):
    approved: bool
    notes: str = ""


decision = await_human_sync(
    task="Approve $250 refund?",
    payload_schema=RefundPayload,
    payload=RefundPayload(amount_usd=250, customer_id="cus_demo"),
    response_schema=RefundDecision,
    timeout_seconds=900,
    idempotency_key="refund:order-12345",
    assign_to="alice@acme.com",
    notify=["slack:#approvals", "email:alice@acme.com"],
)