{"openapi":"3.1.0","info":{"title":"Microworker API","version":"1.0.0","description":"Post and fund tasks, track automatic payments and reject submissions from your own server.\n\nAuthenticate with an API key from https://microworker.app/developers/ — send it as `Authorization: Bearer mwk_…` (or `X-API-Key`). Keys are for servers: never put one in a web page or an app.\n\nA key has scopes: `read` (always), `tasks` (create and edit tasks; pause, resume, cancel, edit, slow down and post notes on live ones), `fund` (publish using your balance, and change a live task's places and pay), `review` (reject submissions only) and `conversions` (confirm sign-ups for automatic payment on postback tasks). Employers cannot manually approve submissions. Existing Review keys need a new key with `conversions` to confirm sign-ups.\n\nMoney is in integer cents (`*_minor`). The platform fee is 5% plus $1.00 per task; verified employers pay no fee. Rewards start at $0.02 per worker ($0.05 when the task asks for a screenshot or a file). Current figures are always on GET https://microworker.app/api/version.\n\nLimits: 120 requests a minute per key, 30 of them for funding, changing a live task's places and pay, rejection and conversion confirmations. Errors are `{ \"error\": \"<code>\" }` with an HTTP status.\n\nWebhooks: add an https endpoint and we POST signed events to it — see the webhooks section and the WebhookEvent schema. Conversion postbacks: post a task with verify_method \"postback\" and confirm each real sign-up with POST /conversions.","contact":{"email":"contact@microworker.xyz","url":"https://microworker.app/developers/"},"termsOfService":"https://microworker.app/terms/","license":{"name":"Proprietary","url":"https://microworker.app/terms/"},"x-logo":{"url":"https://microworker.app/android-chrome-512x512.png","backgroundColor":"#FFFFFF","altText":"Microworker"}},"externalDocs":{"description":"Guides, examples and API keys","url":"https://microworker.app/developers/"},"servers":[{"url":"https://microworker.app/api/v1"}],"security":[{"apiKey":[]},{"apiKeyHeader":[]}],"tags":[{"name":"Account","description":"Who the key belongs to, and the task categories."},{"name":"Pricing","description":"What to pay per worker, before you post."},{"name":"Tasks","description":"Create, edit, fund, pause and cancel your tasks."},{"name":"Live tasks","description":"A funded task while it runs: change its words, slow it down, post notes to the people working on it, change its places and pay. Pause, resume and cancel are PATCH /tasks/{id}."},{"name":"Review","description":"Follow submissions, reject proof and confirm conversions. Approved work is paid automatically."},{"name":"Money","description":"Your balance, ledger and funding invoices. Funds are added on the website."},{"name":"Webhooks","description":"Signed events sent to your server."}],"paths":{"/me":{"get":{"tags":["Account"],"operationId":"getMe","summary":"The account and key making this call","description":"Who the key belongs to, the scopes it carries and its rate limits. Every key has `read`, so this is a cheap way to check that a key works.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Funding answers `verified_required` (with `category` and `plan`) for a kind of task only a plan may post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/categories":{"get":{"tags":["Account"],"operationId":"listCategories","summary":"Task categories","description":"The `id`s accepted as `category_id` when creating a task.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/pricing/guide":{"post":{"tags":["Pricing"],"operationId":"priceGuide","summary":"Recommend a reward per worker","description":"What similar work pays, from the posting itself: what it asks for (tests, surveys, sign-ups, installs, joins, follows…), how many steps and proofs it has, a duration it states, where workers must be, the devices they must be on, and whether it is only for your own workers. Screenshot and file proofs include estimated capture/upload handling, even for the first proof and on duration-based recommendations; text, URLs and copied codes have no media handling premium. Every factor comes back in `reasons`. A recommendation only — you set the price, from the floor (`floor_minor`) up. The same guide is on the website's Post a task page.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceGuideRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceGuide"}}}},"400":{"description":"Invalid input (`validation_failed`, with zod `issues`), or a named rule such as `reward_below_minimum`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` — see the RateLimit headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/wallet":{"get":{"tags":["Money"],"operationId":"getWallet","summary":"Balance and the 50 newest ledger entries","description":"Balances in minor units: `available_minor` can fund tasks; `escrowed_minor` is held for tasks that are live. Funds are added on the website (crypto, via the hosted checkout) — the API spends them.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletResponse"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks":{"get":{"tags":["Tasks"],"operationId":"listTasks","summary":"Your tasks, newest first","description":"Every task this account has posted, drafts included, newest first. `total` counts the tasks that match `status`. `pending_count` is work waiting for review; `held_count` is the part of it the automatic checks held, which is not paid unless you look at it.","parameters":[{"name":"status","in":"query","description":"Only tasks in this status. Leave it out for every status; an unrecognised value is ignored.","schema":{"type":"string","enum":["draft","open","paused","completed","cancelled","expired"]}},{"name":"page","in":"query","description":"Page number, from 1.","schema":{"type":"integer","minimum":1,"default":1}},{"name":"pageSize","in":"query","description":"Tasks per page, 5–100. A value outside the range is clamped to it.","schema":{"type":"integer","minimum":5,"maximum":100,"default":25}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/TaskSummary"}},"total":{"type":"integer"},"page":{"type":"integer"},"pageSize":{"type":"integer"}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]},"post":{"tags":["Tasks"],"operationId":"createTask","summary":"Create a task (as a draft)","description":"Creates a DRAFT: nothing is charged and no worker can see it until you fund it. `fee_minor` in the answer is a preview; the fee actually charged is worked out again when you fund.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreated"}}}},"400":{"description":"Invalid input (`validation_failed`, with zod `issues`), or a named rule such as `reward_below_minimum`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Funding answers `verified_required` (with `category` and `plan`) for a kind of task only a plan may post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` — see the RateLimit headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}":{"get":{"tags":["Tasks"],"operationId":"getTask","summary":"One of your tasks, with submission counts","description":"The whole task (instructions, proof requirements, targeting and attachments, and once it is live your `notes`, its `pace` and when its words last changed, `edited_at`) and how many of its submissions are in each state; `approved` includes automatic approvals. A task that is not yours answers 404 `task_not_found`.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"task":{"$ref":"#/components/schemas/Task"},"counts":{"$ref":"#/components/schemas/SubmissionCounts"}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]},"patch":{"tags":["Tasks"],"operationId":"updateTask","summary":"Edit a draft, or pause / resume / cancel a task","description":"Field edits are accepted while the task is a draft; on a live task they answer 409 `invalid_task_state` — its words, pace, notes, places and pay have calls of their own (the Live tasks section).\n\nOn a live task send only `action` (other fields sent with it are ignored):\n- `pause` — an open task leaves the board and nobody new can start it. Workers who already started can still send their proof.\n- `unpause` — resume a paused task. Its links are checked again first: 422 `task_link_broken` (with `url`, `kind` and `detail`) when one no longer works — fix it with POST /tasks/{id}/edit, then resume. 409 `task_under_review` while staff hold it.\n- `cancel` — end a draft, open or paused task and refund the unspent escrow to your balance. Refused with 409 `cannot_cancel_with_pending_submissions` while proof waits for its review, and `cannot_cancel_with_open_disputes` while a dispute is open.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskUpdate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"refunded_minor":{"type":"integer","description":"On cancel: what came back to your balance."},"match_credit_returned_minor":{"type":"integer","description":"On cancel, when switch-offer match credit was left in the task: it goes back to your offer, not your balance."},"proofs":{"type":"array","items":{"$ref":"#/components/schemas/ProofRequirement"}}}}}}},"400":{"description":"Invalid input (`validation_failed`, with zod `issues`), or a named rule such as `reward_below_minimum`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Funding answers `verified_required` (with `category` and `plan`) for a kind of task only a plan may post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`invalid_task_state` (a field edit on a live task, pausing a task that is not open, resuming one that is not paused), `task_under_review`, `cannot_cancel_with_pending_submissions`, `cannot_cancel_with_open_disputes`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`task_link_broken` with `url`, `kind` and `detail` — resuming a task whose link no longer works. It stays paused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` — see the RateLimit headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}/media":{"post":{"tags":["Tasks"],"operationId":"addTaskMedia","summary":"Attach pictures or video to a draft","description":"multipart/form-data with one or more `media` files. Workers see them with the instructions. Limits (count, size, types) are in `task_media` on GET https://microworker.app/api/version.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"media":{"type":"array","items":{"type":"string","format":"binary"}},"step_index":{"type":"array","items":{"type":"integer"},"description":"Optional, one per file: the instruction step (0-based) the file belongs to."}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"}}}}}}},"400":{"description":"Invalid input (`validation_failed`, with zod `issues`), or a named rule such as `reward_below_minimum`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The object is in the wrong state for this call, e.g. `invalid_task_state`, `submission_not_pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}/media/{mediaId}":{"delete":{"tags":["Tasks"],"operationId":"removeTaskMedia","summary":"Remove an attachment from a draft","description":"Drafts only: a funded task's attachments cannot change (409 `invalid_task_state`). Answers with the draft's remaining attachments.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}},{"name":"mediaId","in":"path","required":true,"description":"Media id","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"}}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The object is in the wrong state for this call, e.g. `invalid_task_state`, `submission_not_pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}/fund":{"post":{"tags":["Tasks"],"operationId":"fundTask","summary":"Fund a draft and publish it","description":"Moves reward × workers + the platform fee from your available balance into escrow and publishes the task. The posting is screened first: a refusal is a 422 `content_policy_violation` and no money moves. Some kinds of task (gambling, software downloads, and categories with a `min_plan`) need a live plan: without one the answer is a 403 `verified_required` with `category` and `plan`, and the task stays a draft. A task for `my_workers` answers 409 `team_empty` while you have saved no worker, and a task for a project's team (`audience: \"project\"`) while its project has nobody in it; nothing is charged. A team-first task opens to everyone `backup_hours` after funding (`open_to_all_at`). A `max_per_country` at or above the worker count being funded is cleared, since it would limit nothing. A first task, or one the screen flags, may publish with `under_review: true`. Needs the `fund` scope.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"escrow_minor":{"type":"integer"},"under_review":{"type":"boolean"},"match_credit_minor":{"type":"integer","description":"Present only when switch-offer match credit (microworker.app/migrate) paid part of the worker pay."}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"`insufficient_balance` — add funds on the website first. Or `verified_plan_required` when API access needs the verified plan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Funding answers `verified_required` (with `category` and `plan`) for a kind of task only a plan may post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The object is in the wrong state for this call, e.g. `invalid_task_state`, `submission_not_pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Refused by a rule: `content_policy_violation` (with `category` and sometimes `reason`), `code_answer_required`, `verify_chat_not_found`, …","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` — see the RateLimit headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"fund","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}/submissions":{"get":{"tags":["Review"],"operationId":"listSubmissions","summary":"Submissions on one of your tasks","description":"Each carries the automatic checks' verdict: `check_status` is `clear`, `warn` or `hold` (held proof is excluded from automatic payment), and `check_flags` says what was found. The `approved` filter includes automatic approvals; `auto_approved` selects only automatic approvals. Screenshot paths are fetched with GET /proofs/{file}.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}},{"name":"status","in":"query","description":"Only submissions in this status: `approved` includes automatic approvals, `auto_approved` selects only those. Leave it out for every status.","schema":{"type":"string","enum":["claimed","pending","approved","rejected","auto_approved"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"task_id":{"type":"integer"},"rows":{"type":"array","items":{"$ref":"#/components/schemas/Submission"}}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/submissions/{id}/reject":{"post":{"tags":["Review"],"operationId":"rejectSubmission","summary":"Reject a submission","description":"No money moves and the slot goes back to the pool for another worker. Say why — the worker sees `rejection_reason`.","parameters":[{"name":"id","in":"path","required":true,"description":"Submission id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"rejection_reason":{"type":"string","maxLength":1000}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"Invalid input (`validation_failed`, with zod `issues`), or a named rule such as `reward_below_minimum`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Funding answers `verified_required` (with `category` and `plan`) for a kind of task only a plan may post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The object is in the wrong state for this call, e.g. `invalid_task_state`, `submission_not_pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` — see the RateLimit headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"review","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/proofs/{file}":{"get":{"tags":["Review"],"operationId":"getProofFile","summary":"Download a proof screenshot","description":"For a path in a submission's `screenshot_paths` (`/uploads/proofs/<file>`), request `/proofs/<file>`. Only the task's owner can fetch it.","parameters":[{"name":"file","in":"path","required":true,"description":"The file name: the last part of a path in the submission's `screenshot_paths`.","schema":{"type":"string"}}],"responses":{"200":{"description":"The image","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/deposits":{"get":{"tags":["Money"],"operationId":"listDeposits","summary":"Your funding invoices, newest first","description":"The crypto top-ups started on the website, newest first, each with its status and, once one was chosen, the coin and network. Funds are added on the website; the API only reads them.","parameters":[{"name":"limit","in":"query","description":"How many invoices to return, 1–50. A value outside the range is clamped to it.","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/Deposit"}}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/deposits/{id}":{"get":{"tags":["Money"],"operationId":"getDeposit","summary":"One funding invoice","description":"One of your funding invoices. An id that is not yours answers 404 `deposit_not_found`.","parameters":[{"name":"id","in":"path","required":true,"description":"Deposit id","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"deposit":{"$ref":"#/components/schemas/Deposit"}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}/edit":{"post":{"tags":["Live tasks"],"operationId":"editLiveTask","summary":"Change a live task's words","description":"Change the title, the steps, each proof requirement's note and which step each attachment belongs to, on an open or paused task. Send only what changes; the answer is the task as it now is.\n\nThe KINDS of proof and their number stay as they are — a worker may be half way through sending exactly those — so `proof_notes` has one entry per requirement, in order. Money and who can take the task do not change here: places and pay are POST /tasks/{id}/resize.\n\nThe new words are checked the way funding checks them: the content rules, kinds of task only a plan may post, the links the edit adds, whether a worker here can do it, and — for words the screen flags — the reviewer. A refusal changes nothing. `dry_run: true` runs every check and changes nothing: try first.\n\nWhen the words change, the version before is kept, and the people working on the task (a claim still open, or proof sent back for a fix) are told. `message` is what they are told; it is also posted as a note. Proof already sent is judged against the version its worker started with.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveEditBody"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveEditResult"}}}},"400":{"description":"`validation_failed`: a field is out of range (title 5–140 characters, instructions 20–8000, a proof note up to 500, `message` up to 500), with `issues` or `field`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Or `verified_required` with `category` and `plan`: the new words make it a kind of task only a plan may post. Nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`task_not_found` (not yours), or `media_not_found` — a `media_steps` id that is not one of this task's attachments.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`invalid_task_state` (not open or paused), `task_under_review` (held for a staff review), `proof_kinds_locked` with `count` (`proof_notes` needs exactly that many entries), `edit_needs_review` (the new words need a person to look at them first; nothing changed), `task_changed` (the task changed while the checks ran: read it again and retry), `too_many_notes` with `max` (the `message` would be one note too many).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`content_policy_violation` with `category` (and sometimes `reason`), `task_link_broken` with `url`, `kind` and `detail` (a link the edit adds does not work), `task_other_platform` with `names`, `task_material_missing` with `material`, `task_not_doable` with `reason`. Nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` (this key), or `too_many_attempts` — too many changes from your server's address in an hour, or more than 10 notes in an hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`live_jobs_unavailable` — managing live tasks is switched off for maintenance. Nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}/pace":{"post":{"tags":["Live tasks"],"operationId":"setTaskPace","summary":"Slow a task down","description":"At most `limit` new workers an hour or a day (`window`, kept as it was when left out); `limit: null` takes the brake off. Every claim made inside the window counts as a start, whatever became of it. While the window is full the task leaves the board for anyone not already on it, new-task alerts wait, and a worker who tries to start is refused with 409 `task_paced` and `opens_at`. Draft, open or paused tasks; `limit` 1–1000.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["limit"],"properties":{"limit":{"type":["integer","null"],"minimum":1,"maximum":1000,"description":"New workers per window; null = as fast as they come."},"window":{"type":"string","enum":["hour","day"]}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"pace":{"$ref":"#/components/schemas/TaskPace"}}}}}},"400":{"description":"`validation_failed` with `field`: `limit` must be 1–1000 or null (`max` says the top), `window` `hour` or `day`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`task_not_found` — not yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`invalid_task_state` — the task is completed, cancelled or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` (this key — see the RateLimit headers), or `too_many_attempts`: too many changes from your server's address in an hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`live_jobs_unavailable` — managing live tasks is switched off for maintenance. Nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}/notes":{"post":{"tags":["Live tasks"],"operationId":"addTaskNote","summary":"Post a note to the people working on a task","description":"A short message (1–500 characters) shown on the task page to everyone who can see the task, sent to whoever is working on it, and given to the proof reviewer with the time it was posted — a worker who started before a note is never refused only because of it. E-mail addresses and phone numbers are hidden before it is stored (`masked: true`); links and @handles stay. At most 20 notes on a task at a time and 10 an hour across your tasks. Open or paused tasks.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"body":{"type":"string","minLength":1,"maxLength":500}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"note":{"$ref":"#/components/schemas/TaskNote"},"notified":{"type":"integer","description":"How many people working on it were told."}}}}}},"400":{"description":"`validation_failed` with `field` and `max`: the note is empty, or longer than 500 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`task_not_found` — not yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`invalid_task_state` (not open or paused), or `too_many_notes` with `max` — take one down first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`content_policy_violation` with `category`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` (this key), or `too_many_attempts` — 10 notes in the last hour (taken-down ones included), or too many changes from your server's address in an hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`live_jobs_unavailable` — managing live tasks is switched off for maintenance. Nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}/notes/{noteId}":{"delete":{"tags":["Live tasks"],"operationId":"removeTaskNote","summary":"Take a note down","description":"The note leaves the task page and the proof reviewer stops reading it. It cannot be put back — post a new one. It frees its place among the task's notes, but still counts toward the hourly limit.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}},{"name":"noteId","in":"path","required":true,"description":"Note id, from the task's `notes` or the answer that posted it","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`task_not_found` (not yours), or `note_not_found` — not one of this task's notes, or already taken down.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` (this key — see the RateLimit headers), or `too_many_attempts`: too many changes from your server's address in an hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`live_jobs_unavailable` — managing live tasks is switched off for maintenance. Nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/tasks/{id}/resize":{"post":{"tags":["Live tasks"],"operationId":"resizeTask","summary":"Change a live task's places and pay","description":"Fewer or more places (`slots_total`) and higher pay (`reward_minor`) on an open or paused task. Pay can only go up. Places cannot go below the places in use (started, waiting for review or paid), and one place must stay unpaid.\n\nOnly the difference moves: fewer places or a smaller fee bring money back to your available balance (`refund_minor`); more places or higher pay take it from your balance into the task's escrow (`extra_minor` — 402 `insufficient_balance` when it is not there). Workers who started get the new pay; proof already sent keeps the price it was sent at. The fee is worked out again on the new budget at your terms now (verified employers pay none).\n\n`dry_run: true` answers the same thing and changes nothing. Needs the `fund` scope, and counts toward the money limit — dry runs too.","parameters":[{"name":"id","in":"path","required":true,"description":"Task id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResizeBody"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResizeResult"}}}},"400":{"description":"`validation_failed` — neither `slots_total` nor `reward_minor` was sent, or one is out of range.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"`insufficient_balance` — add funds on the website first. Or `verified_plan_required` when API access needs the verified plan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`task_not_found` — not yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`invalid_task_state` (not open or paused), `task_has_open_disputes` (wait for the dispute to be decided), `reward_cannot_decrease` with `min_minor` (the pay now), `slots_below_taken` with `min_slots` (the fewest places it can have).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`reward_below_minimum` with `min_minor`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` (this key — see the RateLimit headers), or `too_many_attempts`: too many changes from your server's address in an hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"fund","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/webhooks":{"get":{"tags":["Webhooks"],"operationId":"listWebhooks","summary":"Your webhook endpoints","description":"Every endpoint on this account, newest first, including any switched off after repeated failures (`disabled_at`), and the event names you can subscribe to (`events`). A secret is shown only once, when its endpoint is created.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}},"events":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]},"post":{"tags":["Webhooks"],"operationId":"createWebhook","summary":"Add an endpoint","description":"We POST each event you subscribe to, signed with the `secret` in this answer — the only time it is shown. An https URL on the public internet; up to five endpoints. Events: `submission.created`, `submission.approved`, `submission.rejected`, `task.completed` (all of them when `events` is left out).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","maxLength":500},"events":{"type":"array","items":{"type":"string","enum":["submission.created","submission.approved","submission.rejected","task.completed"]}}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreated"}}}},"400":{"description":"Invalid input (`validation_failed`, with zod `issues`), or a named rule such as `reward_below_minimum`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Funding answers `verified_required` (with `category` and `plan`) for a kind of task only a plan may post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The object is in the wrong state for this call, e.g. `invalid_task_state`, `submission_not_pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/webhooks/{id}":{"delete":{"tags":["Webhooks"],"operationId":"deleteWebhook","summary":"Remove an endpoint and its delivery log","description":"Deletes the endpoint and its delivery log; deliveries still waiting are dropped. An endpoint that is not yours answers 404 `webhook_not_found`.","parameters":[{"name":"id","in":"path","required":true,"description":"Webhook id","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Funding answers `verified_required` (with `category` and `plan`) for a kind of task only a plan may post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/webhooks/{id}/test":{"post":{"tags":["Webhooks"],"operationId":"testWebhook","summary":"Send a `ping` event to one endpoint","description":"Queues a `ping` event, signed like every other event, to check your endpoint and your signature code. It goes out within a few seconds; follow it in GET /webhooks/{id}/deliveries. An endpoint that was switched off (`disabled_at`) receives nothing: add it again.","parameters":[{"name":"id","in":"path","required":true,"description":"Webhook id","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"queued":{"type":"boolean"},"event_id":{"type":"string"}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Funding answers `verified_required` (with `category` and `plan`) for a kind of task only a plan may post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"tasks","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"operationId":"listDeliveries","summary":"Recent deliveries to one endpoint","description":"The 30 newest deliveries to this endpoint: status (`pending`, `delivered` or `failed`), attempts, the last HTTP status your server answered and when the next retry is due. A delivery that fails is retried with backoff for about two days.","parameters":[{"name":"id","in":"path","required":true,"description":"Webhook id","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}}}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"read","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/conversions":{"post":{"tags":["Review"],"operationId":"confirmConversion","summary":"Confirm a conversion (pays the worker)","description":"For a task posted with `verify_method: \"postback\"`. Each worker who claims it gets a personal link — your `verify_target` with `mw_click=<code>` added. Store that code when they sign up; when the sign-up is real, send it here and the worker is paid on the spot. Safe to retry: a code that was already paid answers `already: true`. Needs the `conversions` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionResult"}}}},"400":{"description":"Invalid input (`validation_failed`, with zod `issues`), or a named rule such as `reward_below_minimum`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`api_key_required` or `invalid_api_key`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`insufficient_scope` (the key lacks the scope named in `required`), `account_banned`, `account_suspended` or `email_not_verified`. Funding answers `verified_required` (with `category` and `plan`) for a kind of task only a plan may post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or not yours — the API never confirms that another account's object exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The object is in the wrong state for this call, e.g. `invalid_task_state`, `submission_not_pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limit_exceeded` — see the RateLimit headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"conversions","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}}},"webhooks":{"submission.created":{"post":{"summary":"submission.created","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges it; anything else is retried with backoff for about two days."}}}},"submission.approved":{"post":{"summary":"submission.approved","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges it; anything else is retried with backoff for about two days."}}}},"submission.rejected":{"post":{"summary":"submission.rejected","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges it; anything else is retried with backoff for about two days."}}}},"task.completed":{"post":{"summary":"task.completed","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Any 2xx acknowledges it; anything else is retried with backoff for about two days."}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"mwk_…","description":"An API key from /developers, as `Authorization: Bearer mwk_…`."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key","description":"The same API key, sent as `X-API-Key: mwk_…` instead of the Bearer header."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"A stable machine-readable code."},"issues":{"type":"array","items":{"type":"object"}}},"additionalProperties":true},"Me":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"integer"},"email":{"type":"string"},"display_name":{"type":"string"},"verified_employer":{"type":"boolean"}}},"key":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","tasks","fund","review","conversions"]}}}},"limits":{"type":"object","properties":{"rpm":{"type":"integer"},"money_rpm":{"type":"integer"}}}}},"Category":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"name":{"type":"string"},"min_plan":{"type":["string","null"],"enum":["verified","company",null],"description":"The plan a task in this category needs before it can be funded; null for any account."}}},"ProofRequirement":{"type":"object","required":["type"],"properties":{"must_show":{"type":"string","maxLength":100,"description":"Screenshot rows only: text the screenshot must show (a group or channel name, a username). We read each screenshot on our own server and hold a submission that does not show it."},"type":{"type":"string","enum":["screenshot","text","url","file","code"],"description":"`code`: a completion code you show at the end of your own flow; a matching code pays on the spot. At most one per task."},"note":{"type":"string","maxLength":500,"description":"What the proof must show. Workers are judged against it."},"answer":{"type":"string","maxLength":200,"writeOnly":true,"description":"For `code` only: the code (5–64 letters or digits). Stored hashed and never returned; on an edit, omit it to keep the stored one."}}},"TargetProfile":{"type":["object","null"],"description":"Who can do this (2026-10-07): only workers whose Skills & assets match see the task and can claim it (others get 403 `profile_not_matching`). Within a part ANY choice will do; every part given must be met. null or every part empty = anybody. Each part given raises the price guide ×1.2, at most ×2.","properties":{"languages":{"type":"array","maxItems":5,"items":{"type":"object","required":["code"],"properties":{"code":{"type":"string","enum":["en","ar","arq","arz","ary","kab","tzm","fr","es","pt","de","it","nl","pl","ro","ru","uk","tr","fa","ps","ur","sd","pa","hi","bn","ne","si","ta","te","mr","gu","ml","kn","id","ms","fil","vi","th","my","zh","ja","ko","sw","ha","yo","ig","pcm","am","so","om","zu","xh","af","tw"]},"min":{"type":"string","enum":["basic","good","fluent","native"],"default":"good","description":"This level or better."}}}},"skills":{"type":"array","items":{"type":"string","enum":["video_editing","graphic_design","writing","translation","transcription","voice","photos","data_entry","research","social_media","app_testing","ai_rating","coding"]}},"wants":{"type":"array","items":{"type":"string","enum":["games","surveys","social","signups","testing","videos","clipping","ai_data","writing","research"]},"description":"The kinds of work they asked for."},"game_platforms":{"type":"array","items":{"type":"string","enum":["android","ios","pc","console"]}},"apps":{"type":"array","items":{"type":"string","enum":["tiktok","instagram","youtube","facebook","x","telegram","whatsapp","snapchat","discord","reddit","linkedin","pinterest","threads","twitch"]}},"channel":{"type":["object","null"],"description":"Runs a page or channel of at least `min_followers` (a band), on one of `platforms` (empty = any).","properties":{"platforms":{"type":"array","items":{"type":"string","enum":["youtube","tiktok","instagram","facebook","x","telegram","twitch","snapchat","other"]}},"min_followers":{"type":"string","enum":["under_1k","1k_10k","10k_100k","100k_1m","over_1m"],"default":"under_1k"}}}}},"TaskCreate":{"type":"object","required":["title","instructions","reward_minor","slots_total"],"properties":{"title":{"type":"string","minLength":5,"maxLength":140},"instructions":{"type":"string","minLength":20,"maxLength":8000,"description":"The steps, one per line. `**bold**` and `*italic*` render; nothing else does."},"category_id":{"type":"integer"},"proofs":{"type":"array","minItems":1,"maxItems":5,"items":{"$ref":"#/components/schemas/ProofRequirement"}},"reward_minor":{"type":"integer","minimum":1,"maximum":100000,"description":"Pay per approved worker, in cents. The floor is `min_reward_minor` on /version, or `min_reward_screenshot_minor` when the task asks for a screenshot or a file."},"slots_total":{"type":"integer","minimum":1,"maximum":100000,"description":"How many workers."},"auto_approve_hours":{"type":"integer","minimum":0,"maximum":720,"default":0,"description":"Defaults to 0 when omitted, and is never more than 24: a larger value is accepted and stored as 24. Proof is paid as soon as the automatic checks are clear; anything they question is paid within 24 h unless you reject it first (sooner when this window is shorter); held proofs wait for a person. 0 = instant review. GET /version `payment` has the current figures."},"target_countries":{"type":"array","maxItems":60,"items":{"type":"string","description":"ISO 3166-1 alpha-2"},"description":"Only workers in these countries. Empty = anywhere."},"target_devices":{"type":"array","maxItems":5,"items":{"type":"string","enum":["android","ios","desktop","windows","mac"]},"description":"Only workers on these devices — the device they open and claim the task on. `windows` and `mac` are computers running that system (`desktop` is any computer). Empty, or android + ios + a computer, = any device."},"audience":{"type":"string","enum":["everyone","my_workers","project"],"default":"everyone","description":"`my_workers`: only the workers you saved from your own tasks' review page can see and claim it (funding answers 409 `team_empty` until you have saved one). `project`: your project's team first (needs `project_id`; 400 `project_required` without one), then everyone after `backup_hours`."},"hiring":{"type":"boolean","default":false,"description":"A test task: you are choosing workers from it for more work. Workers see it on the task."},"project_id":{"type":"integer","description":"One of your projects (not archived; 404 `project_not_found` otherwise). The task is listed on the project; with `audience: \"project\"` its team gets it first."},"backup_hours":{"type":"integer","enum":[0,6,24,72],"default":24,"description":"For `audience: \"project\"`: hours after funding before the spots the team has not taken open to everyone. 0 = never."},"max_per_country":{"type":["integer","null"],"minimum":1,"description":"At most this many of the workers from any one country — counted by the country each worker connects from when they start the task. null (or omitted) = no limit; a value at or above `slots_total` limits nothing and is stored as null. A worker whose country's places are all taken is refused with 403 `country_quota_full`."},"target_profile":{"$ref":"#/components/schemas/TargetProfile"},"verify_method":{"type":"string","enum":["telegram_join","postback"],"description":"`telegram_join`: the platform checks the join itself. `postback`: your server confirms each conversion with POST /conversions, and that pays the worker."},"verify_target":{"type":"string","maxLength":500,"description":"Required with verify_method. For telegram_join the group or channel (@mygroup); for postback the https landing URL each worker starts from."},"expires_at":{"type":"string","format":"date-time"}}},"TaskUpdate":{"type":"object","properties":{"title":{"type":"string","minLength":5,"maxLength":140},"instructions":{"type":"string","minLength":20,"maxLength":8000},"proofs":{"type":"array","minItems":1,"maxItems":5,"items":{"$ref":"#/components/schemas/ProofRequirement"}},"category_id":{"type":["integer","null"]},"target_countries":{"type":["array","null"],"items":{"type":"string"}},"target_devices":{"type":["array","null"],"items":{"type":"string","enum":["android","ios","desktop","windows","mac"]}},"audience":{"type":"string","enum":["everyone","my_workers","project"]},"hiring":{"type":"boolean"},"project_id":{"type":["integer","null"],"description":"null takes the draft out of its project."},"backup_hours":{"type":"integer","enum":[0,6,24,72]},"max_per_country":{"type":["integer","null"],"minimum":1,"description":"null clears it. Checked against the worker count the edit leaves: at or above it, it is stored as null."},"target_profile":{"$ref":"#/components/schemas/TargetProfile","description":"null clears it (anybody)."},"action":{"type":"string","enum":["pause","unpause","cancel"],"description":"`pause` (open → paused), `unpause` (paused → open, once its links answer again) or `cancel` (draft, open or paused → cancelled, the unspent escrow refunded). Any other field sent with it is ignored."}}},"TaskCreated":{"type":"object","properties":{"id":{"type":"integer"},"budget_minor":{"type":"integer"},"fee_minor":{"type":"integer"},"total_minor":{"type":"integer"},"target_countries":{"type":"array","items":{"type":"string"}},"proofs":{"type":"array","items":{"$ref":"#/components/schemas/ProofRequirement"}},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"}},"target_devices":{"type":"array","items":{"type":"string"}},"audience":{"type":"string","enum":["everyone","my_workers","project"]},"hiring":{"type":"boolean"},"project_id":{"type":["integer","null"]},"project_name":{"type":["string","null"]},"backup_hours":{"type":["integer","null"]},"open_to_all_at":{"type":["string","null"]},"max_per_country":{"type":["integer","null"]},"target_profile":{"$ref":"#/components/schemas/TargetProfile"}}},"TaskSummary":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"status":{"type":"string","enum":["draft","open","paused","completed","cancelled","expired"]},"moderation_status":{"type":"string","enum":["approved","pending","rejected"]},"moderation_reason":{"type":["string","null"]},"reward_minor":{"type":"integer"},"slots_total":{"type":"integer"},"slots_filled":{"type":"integer"},"slots_taken":{"type":"integer"},"budget_minor":{"type":"integer"},"fee_minor":{"type":"integer"},"escrow_minor":{"type":"integer"},"pending_count":{"type":"integer"},"held_count":{"type":"integer","description":"Pending submissions the automatic checks held."},"project_id":{"type":["integer","null"]},"project_name":{"type":["string","null"]},"backup_hours":{"type":["integer","null"]},"open_to_all_at":{"type":["string","null"]},"max_per_country":{"type":["integer","null"]},"created_at":{"type":"string"},"funded_at":{"type":["string","null"]}}},"Task":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"integer"},"title":{"type":"string"},"instructions":{"type":"string"},"status":{"type":"string"},"moderation_status":{"type":"string"},"reward_minor":{"type":"integer"},"slots_total":{"type":"integer"},"slots_filled":{"type":"integer"},"slots_taken":{"type":"integer"},"budget_minor":{"type":"integer"},"fee_minor":{"type":"integer"},"escrow_minor":{"type":"integer"},"auto_approve_hours":{"type":["integer","null"]},"target_countries":{"type":"array","items":{"type":"string"}},"proofs":{"type":"array","items":{"$ref":"#/components/schemas/ProofRequirement"}},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"}},"target_devices":{"type":"array","items":{"type":"string"}},"audience":{"type":"string","enum":["everyone","my_workers","project"]},"hiring":{"type":"boolean"},"project_id":{"type":["integer","null"]},"project_name":{"type":["string","null"]},"backup_hours":{"type":["integer","null"]},"open_to_all_at":{"type":["string","null"],"description":"When a team-first task opens to everyone (set at funding)."},"max_per_country":{"type":["integer","null"],"description":"At most this many workers from any one country; null = no limit."},"target_profile":{"$ref":"#/components/schemas/TargetProfile"},"created_at":{"type":"string"},"funded_at":{"type":["string","null"]},"expires_at":{"type":["string","null"]},"notes":{"type":"array","items":{"$ref":"#/components/schemas/TaskNote"},"description":"Your notes to the people working on it that are still up, newest first."},"pace":{"oneOf":[{"$ref":"#/components/schemas/TaskPace"},{"type":"null"}],"description":"The brake set with POST /tasks/{id}/pace; null = as fast as workers come."},"edited_at":{"type":["string","null"],"format":"date-time","description":"When its words were last changed while it was live (POST /tasks/{id}/edit); null when never."}}},"LiveEditBody":{"type":"object","description":"Send only what changes. The kinds of proof, the money and who can take the task are not editable here.","properties":{"title":{"type":"string","minLength":5,"maxLength":140},"instructions":{"type":"string","minLength":20,"maxLength":8000,"description":"The steps, one per line, as when the task was created."},"proof_notes":{"type":"array","minItems":1,"maxItems":5,"items":{"type":["string","null"],"maxLength":500},"description":"One entry per proof requirement, in the order GET /tasks/{id} lists `proofs`: its new note, or null or \"\" to clear it. A list of another length answers 409 `proof_kinds_locked` with `count`."},"media_steps":{"type":"array","maxItems":100,"description":"Move attachments between steps.","items":{"type":"object","required":["id","step_index"],"properties":{"id":{"type":"integer","description":"An attachment of this task (`media[].id`)."},"step_index":{"type":["integer","null"],"minimum":0,"description":"The step (0-based) it belongs to; null = the task as a whole."}}}},"message":{"type":"string","maxLength":500,"description":"What to tell the people working on it about the change. Sent to them and posted as a note, with e-mail addresses and phone numbers hidden. Left out or blank: they are told the task was updated."},"dry_run":{"type":"boolean","default":false,"description":"Run every check and answer what would happen; change nothing."}}},"LiveEditResult":{"type":"object","properties":{"ok":{"type":"boolean"},"unchanged":{"type":"boolean","description":"Present (true) when nothing sent differs from the task as it is: nothing was written and nobody was told."},"dry_run":{"type":"boolean","description":"Present (true) on a dry run: every check passed and nothing changed."},"task":{"$ref":"#/components/schemas/Task"},"notified":{"type":"integer","description":"How many people working on it were told — on a dry run, how many would be."},"note":{"oneOf":[{"$ref":"#/components/schemas/TaskNote"},{"type":"null"}],"description":"The note made from `message`, when one was sent."}}},"TaskPace":{"type":"object","description":"Slow down: at most `limit` new workers per `window`. Every claim made inside the window counts, whatever became of it. While the window is full the task is off the board for anyone not already on it, and a worker who tries to start is refused with 409 `task_paced` and `opens_at`.","properties":{"limit":{"type":["integer","null"],"minimum":1,"maximum":1000,"description":"null = as fast as workers come."},"window":{"type":"string","enum":["hour","day"]},"used":{"type":"integer","description":"Workers who started inside the current window."},"opens_at":{"type":["string","null"],"format":"date-time","description":"While the window is full: when the next place opens. null otherwise."}}},"TaskNote":{"type":"object","properties":{"id":{"type":"integer"},"body":{"type":"string","description":"As stored: e-mail addresses and phone numbers replaced by •••."},"created_at":{"type":"string","format":"date-time"},"masked":{"type":"boolean","description":"Contact details were hidden before it was stored."}}},"ResizeBody":{"type":"object","description":"At least one of `slots_total` and `reward_minor`.","anyOf":[{"required":["slots_total"]},{"required":["reward_minor"]}],"properties":{"slots_total":{"type":"integer","minimum":1,"maximum":100000,"description":"How many workers in all, paid and started included."},"reward_minor":{"type":"integer","minimum":1,"maximum":100000,"description":"Pay per worker, in cents. Only up."},"dry_run":{"type":"boolean","default":false,"description":"Answer what would happen; change nothing."}}},"ResizeResult":{"type":"object","properties":{"ok":{"type":"boolean"},"dry_run":{"type":"boolean"},"changed":{"type":"boolean","description":"false when the numbers sent are the task's numbers already."},"task":{"type":"object","description":"The task as the change leaves it (on a dry run: would leave it).","properties":{"id":{"type":"integer"},"status":{"type":"string"},"slots_total":{"type":"integer"},"slots_taken":{"type":"integer"},"slots_filled":{"type":"integer"},"reward_minor":{"type":"integer"},"budget_minor":{"type":"integer"},"fee_minor":{"type":"integer"},"escrow_minor":{"type":"integer"}}},"refund_minor":{"type":"integer","description":"Back to your available balance."},"match_credit_returned_minor":{"type":"integer","description":"Present when switch-offer match credit (microworker.app/migrate) was in the task: it goes back to your offer, not your balance."},"extra_minor":{"type":"integer","description":"Taken from your available balance into the task's escrow."},"claims_raised":{"type":"integer","description":"Workers who had started (not yet sent) and now get the new pay."},"available_minor":{"type":"integer","description":"Your available balance — now on a dry run, after the change otherwise."},"enough":{"type":"boolean","description":"false when `extra_minor` is more than `available_minor`: the real call would answer 402 `insufficient_balance`."}}},"SubmissionCounts":{"type":"object","properties":{"claimed":{"type":["integer","null"]},"pending":{"type":["integer","null"]},"approved":{"type":["integer","null"]},"rejected":{"type":["integer","null"]}}},"Media":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"integer"},"kind":{"type":"string"},"url":{"type":"string"},"step_index":{"type":["integer","null"]}}},"CheckFlag":{"type":"object","additionalProperties":true,"properties":{"c":{"type":"string","description":"What was found: copied_proof, reused_own_proof, example_image, blank_image, duplicate_file, tiny_image, unreadable_image, same_text, same_link, link_from_task, placeholder_text, too_fast (sent in `e` seconds; the task asks for `n`)."},"s":{"type":"string","enum":["warn","hold"]},"same_task":{"type":"boolean"}}},"Submission":{"type":"object","properties":{"id":{"type":"integer"},"worker_id":{"type":"integer"},"worker_name":{"type":"string"},"status":{"type":"string","enum":["claimed","pending","approved","rejected","auto_approved"]},"proof_text":{"type":["string","null"]},"proof_url":{"type":["string","null"]},"screenshot_paths":{"type":"array","items":{"type":"string"}},"reward_minor":{"type":"integer"},"rejection_reason":{"type":["string","null"]},"submitted_at":{"type":["string","null"]},"reviewed_at":{"type":["string","null"]},"created_at":{"type":"string"},"verify_status":{"type":["string","null"],"description":"How the platform verified it by itself (a code, a Telegram join), if it did."},"verify_detail":{"type":["string","null"]},"check_status":{"type":["string","null"],"enum":["clear","warn","hold","released",null]},"check_flags":{"type":"array","items":{"$ref":"#/components/schemas/CheckFlag"}},"worker_username":{"type":["string","null"]},"claimed_at":{"type":["string","null"]},"auto_approve_at":{"type":["string","null"],"description":"The end of its review window."},"auto_pay_at":{"type":["string","null"],"description":"When it will be paid automatically; null when it will not be (held, decided, a conversion waiting for confirmation, or a task under staff review)."},"time_on_task_sec":{"type":["integer","null"],"description":"Seconds between the worker first opening the task (or claiming it) and sending this."},"time_on_task_from":{"type":["string","null"],"enum":["view","claim",null]},"in_team":{"type":"boolean","description":"The worker is one of your saved workers (the ones a `my_workers` task is for)."},"worker_approved":{"type":"integer","description":"The worker's approved submissions across the platform."},"worker_rejected":{"type":"integer","description":"The worker's rejected submissions across the platform."},"project_ids":{"type":"array","items":{"type":"integer"},"description":"Your projects (not archived) this worker is already in."}}},"WalletResponse":{"type":"object","properties":{"wallet":{"type":"object","additionalProperties":true,"properties":{"available_minor":{"type":"integer"},"escrowed_minor":{"type":"integer"}}},"entries":{"type":"array","items":{"type":"object","additionalProperties":true}}}},"Deposit":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"integer"},"provider":{"type":"string"},"amount_minor":{"type":"integer"},"status":{"type":"string"},"txid":{"type":["string","null"]},"created_at":{"type":"string"}}},"Webhook":{"type":"object","properties":{"id":{"type":"integer"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string"},"disabled_at":{"type":["string","null"],"description":"Set when 50 deliveries in a row failed; add the endpoint again once it works."},"disabled_reason":{"type":["string","null"]},"last_success_at":{"type":["string","null"]},"last_failure_at":{"type":["string","null"]},"consecutive_failures":{"type":"integer"}}},"WebhookCreated":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"secret":{"type":"string","description":"whsec_… — shown only in this answer."}}}]},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"integer"},"event_id":{"type":"string"},"event_type":{"type":"string"},"status":{"type":"string","enum":["pending","delivered","failed"]},"attempts":{"type":"integer"},"last_status_code":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"created_at":{"type":"string"},"delivered_at":{"type":["string","null"]},"next_attempt_at":{"type":"string"}}},"WebhookEvent":{"type":"object","description":"The body of every webhook request. Verify the Microworker-Signature header before trusting it: t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your endpoint secret>; reject a t more than five minutes old. Microworker-Event-Id is the same on every retry of one event.","properties":{"id":{"type":"string","description":"evt_…"},"type":{"type":"string","enum":["submission.created","submission.approved","submission.rejected","task.completed","ping"]},"created":{"type":"integer","description":"Unix seconds."},"data":{"type":"object","properties":{"submission":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"integer"},"task_id":{"type":"integer"},"worker_id":{"type":"integer"},"worker_name":{"type":"string"},"status":{"type":"string"},"reward_minor":{"type":"integer"},"proof_text":{"type":["string","null"]},"proof_url":{"type":["string","null"]},"screenshots":{"type":"array","items":{"type":"string"},"description":"Fetch with your key."},"check_status":{"type":["string","null"]},"check_flags":{"type":"array","items":{"$ref":"#/components/schemas/CheckFlag"}},"verify_status":{"type":["string","null"]},"click":{"type":["string","null"],"description":"The worker's click code on a postback task."}}},"task":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string"},"status":{"type":"string"},"slots_total":{"type":"integer"},"slots_filled":{"type":"integer"}}}}}}},"ConversionRequest":{"type":"object","required":["click"],"properties":{"click":{"type":"string","description":"The mw_click code from the worker's personal link (mwc_…)."},"reference":{"type":"string","maxLength":100,"description":"Your own id for the conversion, kept with it for your records."}}},"ConversionResult":{"type":"object","properties":{"ok":{"type":"boolean"},"submission_id":{"type":"integer"},"status":{"type":"string"},"paid_minor":{"type":"integer"},"already":{"type":"boolean","description":"This code was already paid; nothing more was paid."},"task_completed":{"type":"boolean"}}},"PriceGuideRequest":{"type":"object","properties":{"title":{"type":"string","maxLength":140},"instructions":{"type":"string","maxLength":8000},"proofs":{"type":"array","maxItems":5,"items":{"type":"object","properties":{"type":{"type":"string","enum":["screenshot","text","url","file","code"]},"note":{"type":"string"}}}},"target_countries":{"type":"array","items":{"type":"string"}},"target_devices":{"type":"array","items":{"type":"string","enum":["android","ios","desktop","windows","mac"]},"description":"A device limit: iPhones only, computers only (Windows or Mac only too), or both, pay more."},"audience":{"type":"string","enum":["everyone","my_workers","project"],"description":"`my_workers` and `project` price a task for a small group you chose."},"target_profile":{"$ref":"#/components/schemas/TargetProfile"},"verify_method":{"type":"string","enum":["telegram_join"]},"verify_target":{"type":"string"}}},"PriceGuide":{"type":"object","properties":{"model":{"type":"string","description":"Which version of the guide answered."},"kind":{"type":"string","enum":["test","survey","signup","install","write","data","telegram","visit","video","social","clip","general"]},"kinds":{"type":"array","items":{"type":"string"}},"recommended_minor":{"type":"integer"},"low_minor":{"type":"integer"},"high_minor":{"type":"integer"},"minutes":{"type":"integer","description":"Estimated minutes of work."},"floor_minor":{"type":"integer"},"confidence":{"type":"string","enum":["high","low"],"description":"`low` when the posting did not say what kind of work it is."},"reasons":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"code":{"type":"string","enum":["kind","actions","more_kinds","steps","proofs","proof_media","location","devices","team","minutes","floor"]}}}}}}}}}