{"openapi":"3.1.0","info":{"title":"ActionLayer tasks — ZeroClick seller API","description":"ActionLayer runs real-world web tasks end to end: submit a natural-language task plus the end user's email, then poll until it finishes. Flat price per task.\n\nAgents call these endpoints through the ZeroClick pay URL. ZeroClick adds the `zc-request-id` / `zc-agent-id` / `zc-signature` headers — agents never send them directly.\n\n## Task states\n\n| `state` | Meaning | Agent action |\n|---|---|---|\n| `pending` | queued or in progress | keep polling |\n| `blocked_on_user` | input or approval needed | read `info_request`, see below |\n| `completed` | done | read `result` / `reason` |\n| `failed` | could not finish | read `reason` |\n| `cancelled` | cancelled | none |\n\n`reason` is the single human-readable summary on terminal states.\n\n## Payment asks (`info_request.payment_status`)\n\nApproval always happens in the end user's Link app, never in chat.\n\n| `payment_status` | Meaning | Agent action |\n|---|---|---|\n| `wallet_required` | no payment wallet connected | tell the user: sign in at actionlayer.io with the submitted email and connect their Link wallet on the Connections page (they are also emailed directly); proceeds automatically once connected |\n| `pending_info` | info fields must be answered first | reply with `info.field_values` |\n| `pending_wallet_approval` | approval push live in the user's Link app | tell the user to approve in Link; keep polling |\n| `approval_expired` | approval window lapsed | reply with `info.payment_approved: true` for a fresh push |\n| `approved` / `revealed` | payment approved | keep polling |\n| `rejected` | user declined | task fails with `payment_rejected` |\n\nTo decline a payment on the user's behalf, reply with `info.payment_approved: false` — the task terminates immediately.\n\n## Billing\n\nOne task is charged per accepted submission. If the submitted email has task credit with ActionLayer, the request is free for the agent. Replays of the same `zc-request-id` are never charged twice. Purchases made inside a task are paid by the end user from their own connected wallet — the per-task fee covers the service only.\n\nAsks expire (`info_request.expires_at`, a few hours) — relay them to your user promptly; an expired ask fails the task.\n","version":"1.0.0"},"paths":{"/zeroclick/tasks":{"post":{"tags":["zeroclick"],"summary":"Submit a task","description":"Accepts a task for asynchronous execution and returns the task object — poll `GET /zeroclick/tasks/{id}` for progress. Retried requests with the same `zc-request-id` return the original task and are never charged twice.","operationId":"submit_task_zeroclick_tasks_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ZeroClickTaskRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketResponse"}}}},"400":{"description":"Malformed email address."},"401":{"description":"ZeroClick signature verification failed."},"402":{"description":"The agent's plan can't cover one task. ZeroClick handles the payment challenge; retry after paying.","content":{"application/json":{"example":{"error":"payment_required","serviceSlug":"tasks","usage":[{"meterSlug":"task","quantity":1}]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/zeroclick/tasks/{ticket_id}":{"get":{"tags":["zeroclick"],"summary":"Poll a task","description":"Current state of a submitted task, including an `events` transcript. Unbilled; scoped to the submitting agent. Poll every 15–60s while the task is live — payment approvals are also reconciled on poll, so polling is never wasted. When `state` is `blocked_on_user`, read `info_request` and relay the ask to your user promptly (asks expire at `info_request.expires_at`).","operationId":"get_zeroclick_task_zeroclick_tasks__ticket_id__get","parameters":[{"name":"ticket_id","in":"path","required":true,"schema":{"type":"string","title":"Ticket Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketDetailResponse"}}}},"401":{"description":"ZeroClick signature verification failed."},"402":{"description":"The request carried no `zc-agent-id`. The call is free (`usage` is empty) — ZeroClick runs its identity handshake and retries with `zc-agent-id` attached.","content":{"application/json":{"example":{"error":"payment_required","serviceSlug":"tasks","usage":[]}}}},"404":{"description":"Unknown task id, or not this agent's task."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/zeroclick/tasks/{ticket_id}/reply":{"post":{"tags":["zeroclick"],"summary":"Answer a blocked_on_user ask","description":"Resume a task waiting on user input. Plain asks (no `info_request` on the task): answer with `message` or `field`+`value`, setting `sensitive: true` for secrets. Structured asks (`info_request` present): send `info.field_values` whose keys exactly match `requested_fields` plus every `questions[].key`, all values non-empty. Payment asks are approved in the end user's Link app, never here — `info.payment_approved` only declines a charge (false) or re-sends a lapsed approval push (true). Unbilled; scoped to the submitting agent.","operationId":"reply_zeroclick_task_zeroclick_tasks__ticket_id__reply_post","parameters":[{"name":"ticket_id","in":"path","required":true,"schema":{"type":"string","title":"Ticket Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyTaskRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketResponse"}}}},"401":{"description":"ZeroClick signature verification failed."},"402":{"description":"The request carried no `zc-agent-id`. The call is free (`usage` is empty) — ZeroClick runs its identity handshake and retries with `zc-agent-id` attached.","content":{"application/json":{"example":{"error":"payment_required","serviceSlug":"tasks","usage":[]}}}},"404":{"description":"Unknown task id, or not this agent's task."},"409":{"description":"The task isn't waiting on a reply, or the payment ask is in a state the reply doesn't match."},"410":{"description":"The ask expired before the reply arrived."},"422":{"description":"Reply field validation failed (mismatched or empty field_values)."}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InfoReplyData":{"properties":{"payment_approved":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Payment Approved","description":"Payment-ask control. `false`: decline the charge and end the task (`error=payment_rejected`). `true`: when `payment_status=approval_expired`, send a fresh approval request to the user's Link app. The approval itself always happens in the Link app, never here."},"field_values":{"additionalProperties":{"type":"string"},"type":"object","title":"Field Values","description":"Map of `{requested_field_name: value}`. Keys must exactly match `info_request.requested_fields` plus every `info_request.questions[].key`; values must be non-empty."}},"type":"object","title":"InfoReplyData","description":"Structured answers for a task's `info_request`. Either the end user or their agent can supply these."},"ReplyTaskRequest":{"properties":{"message":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Message"},"field":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Field","description":"Machine-readable name of the requested item — e.g. `sms_code`. `value` is stored under this key."},"value":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Value"},"sensitive":{"type":"boolean","title":"Sensitive","description":"Set true when `value` is a secret (2FA code, password): encrypted at rest, purged when the task ends, redacted in logs.","default":false},"info":{"anyOf":[{"$ref":"#/components/schemas/InfoReplyData"},{"type":"null"}],"description":"Required when the task carries an `info_request`; ignored otherwise."}},"type":"object","title":"ReplyTaskRequest","description":"Body for answering a `blocked_on_user` ask: a free-form `message`, or `field`+`value` for a specific requested item, or `info` when the task carries an `info_request`."},"TicketDetailResponse":{"properties":{"id":{"type":"string","title":"Id"},"state":{"type":"string","title":"State"},"goal":{"type":"string","title":"Goal"},"flow":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flow"},"max_budget_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Budget Usd"},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url"},"result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Result"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"info_request":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Info Request"},"events":{"items":{"$ref":"#/components/schemas/TicketEventResponse"},"type":"array","title":"Events","default":[],"description":"Chronological transcript of status updates."}},"type":"object","required":["id","state","goal","flow","max_budget_usd","webhook_url","result","error","created_at","updated_at","completed_at"],"title":"TicketDetailResponse","description":"A task plus its `events` transcript."},"TicketEventResponse":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","title":"Type"},"from_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From State"},"to_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To State"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","type","from_state","to_state","payload","created_at"],"title":"TicketEventResponse","description":"One status update in a task's transcript."},"TicketResponse":{"properties":{"id":{"type":"string","title":"Id"},"state":{"type":"string","title":"State","description":"`pending | blocked_on_user | completed | failed | cancelled` — see the document description for how to handle each."},"goal":{"type":"string","title":"Goal","description":"The submitted task text; answered questions are folded in over time."},"flow":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flow"},"max_budget_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Budget Usd","description":"Not used on this surface; always null."},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url","description":"Not used on this surface; always null."},"result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Result","description":"Structured output on completion: `{output, summary}`."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Machine-readable failure code when state is failed (e.g. `payment_rejected`)."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Human-readable summary, set when state is completed or failed."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"info_request":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Info Request","description":"Present while the task awaits user action: `{amount_usd?, merchant_name?, payment_status?, payment_error?, requested_fields, questions, note?, created_at, expires_at}`. See the document description for `payment_status` handling."}},"type":"object","required":["id","state","goal","flow","max_budget_usd","webhook_url","result","error","created_at","updated_at","completed_at"],"title":"TicketResponse","description":"A task. `state` is one of `pending | blocked_on_user | completed | failed | cancelled`; `completed` and `failed` carry a human-readable `reason`. When `state` is `blocked_on_user`, `info_request` describes what's needed."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"ZeroClickTaskRequest":{"properties":{"task":{"type":"string","maxLength":100000,"minLength":1,"title":"Task","description":"The task, in natural language. Include every detail the task needs — addresses, dates, quantities, preferences."},"email":{"type":"string","maxLength":254,"minLength":3,"title":"Email","description":"The end user's email address. The task is attached to the ActionLayer account for this address (created if missing), and it's where the user is contacted if a payment needs their approval — get it right."}},"type":"object","required":["task","email"],"title":"ZeroClickTaskRequest","description":"Submit-task body — deliberately minimal: the task text carries\nevery detail; there are no other knobs on this surface."}}}}