{"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 — it pays at once; check it later and ask for a fix (POST /submissions/{id}/revise) or take the pay back (POST /submissions/{id}/reverse).","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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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. With `x_check: true` each worker's X work is checked automatically and `fee_minor` includes the charge for it (`x_check_fee_minor`): 503 `x_check_unavailable` when X checks are off (GET /version `verification.x_check`), 422 `x_check_no_target` when the task names nothing on X a check can see, 422 `x_check_not_for_postback` on a conversion task.","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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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"}}}},"503":{"description":"Switched off for maintenance, e.g. `live_jobs_unavailable` for the live-task calls. Try again later.","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. It goes through the funding checks again first: 422 `task_link_broken` (with `url`, `kind` and `detail`) when a link no longer works, 422 `task_other_platform` (with `names`) or `task_material_missing` (with `material`) when a worker here could not do it — 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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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`, `task_other_platform` with `names`, `task_material_missing` with `material` — resuming a task that still does not work. 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`. When our AI reviewer refuses a posting a person may still approve, an account without a plan gets a 403 `verified_required` with `review: true` and the reviewer's `reason`; with a plan it is funded and held for a person (`under_review: true`, `review: \"person\"`) until our team approves it or declines it (the escrow comes back). A task with `x_check` also escrows the X-check charge for every place, at today's price (frozen on the task; never waived by a plan): 503 `x_check_unavailable` when X checks are off, 422 `x_check_no_target` when the draft no longer names anything on X. 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. `earnings_cannot_fund` (with `added_minor` — what of your balance can pay for jobs: money you added and credit — and `needed_minor`): money you EARNED doing tasks cannot pay for jobs; add funds. 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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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":[]}]}},"/submissions/{id}/revise":{"post":{"tags":["Review"],"operationId":"askForFix","summary":"Ask the worker to fix conversion work","description":"On your own conversion (postback) task: send the work back with a note the worker reads as written, and a time to fix it (`hours`, 1–24, the default; more is cut to it). Their personal link keeps working. On work not paid yet (claimed, or \"I've finished\") nothing is paid until you confirm it with POST /conversions. On PAID work (up to 30 days after it was paid) the pay stays — confirm it again with the same click before `fix.due_at` and it is kept, or it is taken back automatically as not done, with your note as the reason. At most 2 fix requests per submission; a new one replaces an open one. Proof tasks are checked by the platform, which asks for fixes itself.","parameters":[{"name":"id","in":"path","required":true,"description":"Submission id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["note"],"properties":{"note":{"type":"string","minLength":5,"maxLength":500},"hours":{"type":"integer","minimum":1}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"submission_id":{"type":"integer"},"status":{"type":"string"},"fix":{"$ref":"#/components/schemas/ConversionFix"}}}}}},"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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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":"`submission_not_revisable` (not a conversion task, or work that can no longer be fixed), `fix_limit_reached`, `reverse_window_closed` (paid too long ago) or `task_not_open`.","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"}}}},"503":{"description":"`conversions_unavailable` — switched off for maintenance. Nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"review","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/submissions/{id}/reverse":{"post":{"tags":["Review"],"operationId":"reverseConversion","summary":"Take back the pay for conversion work that was not done","description":"On your own conversion (postback) task, for work you confirmed and we paid, up to 30 days after it was paid: your later check found it not done (the follow is not in the follower list). The pay comes back — to the task, with the place reopened, while it is live; else to your balance — and the submission becomes rejected with your `reason`, which the worker reads. There is no fine. If the worker's earned balance cannot cover it, the rest is kept from their next pay. The worker can dispute it, and a person decides; if they side with the worker the pay is restored. Once per submission.","parameters":[{"name":"id","in":"path","required":true,"description":"Submission id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","minLength":5,"maxLength":500}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"submission_id":{"type":"integer"},"status":{"type":"string","enum":["rejected"]},"took_minor":{"type":"integer","description":"What the work had paid, decided to come back."},"taken_minor":{"type":"integer","description":"What came back at once; the rest (`debt_minor`) comes from the worker's next pay."},"debt_minor":{"type":"integer"},"back_to":{"type":["string","null"],"enum":["escrow","balance",null],"description":"`escrow`: back in the task, its place open again; `balance`: back in your balance."},"reopened":{"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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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":"`submission_not_reversible` (not paid conversion work, already taken back once, or a dispute was decided for the worker) or `reverse_window_closed`.","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"}}}},"503":{"description":"`conversions_unavailable` — switched off for maintenance. Nothing changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-scope":"review","security":[{"apiKey":[]},{"apiKeyHeader":[]}]}},"/submissions/{id}/report":{"post":{"tags":["Review"],"operationId":"reportPaidWork","summary":"Report paid proof work to our staff","description":"On your own PROOF task (conversion tasks use POST /submissions/{id}/reverse), for work our review paid, up to 30 days after it was paid: your later check found it not done — the follow is gone, the screenshot was of another account. Our staff look at it and decide: take the pay back as not done (no fine for the worker; it comes back to the task, with the place reopened, while the task is live, else to your balance), confirm it a fake, or keep it paid. You are told the outcome. The `reason` (10–1000 characters) is for our staff. Once per submission: a second report answers the first, with `already: true`.","parameters":[{"name":"id","in":"path","required":true,"description":"Submission id","schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","minLength":10,"maxLength":1000}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"submission_id":{"type":"integer"},"already":{"type":"boolean","description":"This submission was reported before; `report` is that report."},"report":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["open","taken_back","fake","kept","closed"],"description":"`open`: waiting for our staff. `taken_back`: the pay came back. `fake`: confirmed a fake (taken back). `kept`: the work stands. `closed`: decided another way."},"reason":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"decided_at":{"type":["string","null"],"format":"date-time"},"note":{"type":["string","null"],"description":"What our staff wrote back, once decided."}}}}}}}},"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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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":"`submission_not_reportable` (not paid proof work, a conversion task, or already taken back) or `report_window_closed`.","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"}}}},"503":{"description":"`work_reports_unavailable` — switched off for maintenance. Nothing changed.","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, `earnings_cannot_fund` when only money you earned is: a raise is paid with money you added). 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. `earnings_cannot_fund` (with `added_minor` — what of your balance can pay for jobs: money you added and credit — and `needed_minor`): money you EARNED doing tasks cannot pay for jobs; add funds. 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`, `submission.closed` (a worker's \"I've finished\" on a conversion task you neither confirmed nor rejected in time, closed without pay), `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","submission.closed","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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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 — after our account checks (one phone is one person): `status: \"held\"` means they held it for a person to decide, `checking: true` that they are still running and the payment follows them (webhook `submission.approved`). Safe to retry: a code that was already paid answers `already: true`. Paid work can still be checked later: ask for a fix (POST /submissions/{id}/revise) — calling this again with the same click within its time confirms the fix (`fix: \"fixed\"`) — or take the pay back as not done (POST /submissions/{id}/reverse), up to 30 days after it was paid. A worker's \"I've finished\" you neither confirm nor reject within 3 days is closed without pay. 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 — and, with `review: true` and the reviewer's `reason`, for a posting our AI reviewer refused that a person may still approve once you have the plan.","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":"`claim_expired`, `submission_not_pending` (not accepted, taken back or closed), `task_not_open`, or `worker_inactive` — the worker's account is suspended, banned or closed, so it is not paid.","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."}}}},"submission.closed":{"post":{"summary":"submission.closed","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."},"x_check":{"type":"boolean","default":false,"description":"Check each worker's X work automatically: that the X account in their proof follows the account your steps link to, and reposted, replied to or quoted the post they link to (likes cannot be checked — X keeps them private). A task asking only for follows and reposts is paid on X's answer; otherwise the answer is given to our proof reviewer, and work X shows not done is sent back for a fix. Costs `x_check.price_per_worker_micro` (GET /version, micro-dollars) per worker place, rounded up to the cent, added to the fee — never waived by a plan. Not on conversion (postback) tasks."},"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)."},"x_check":{"type":"boolean","description":"Turn X checks on or off on a draft; `fee_minor` follows. 503 `x_check_unavailable` / 422 `x_check_no_target` as on create."},"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"},"x_check":{"type":"boolean","description":"Present (true) only when the task has X checks."},"x_check_fee_minor":{"type":"integer","description":"What the X checks add to `fee_minor` (present with `x_check`)."}}},"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"},"x_check":{"type":"boolean","description":"Each worker's X work is checked automatically."},"x_check_fee_minor":{"type":"integer","description":"What the X checks add to `fee_minor` for the task's places (0 without them)."},"x_check_price_micro":{"type":["integer","null"],"description":"The X-check price per worker place in micro-dollars, frozen when the task was funded; null on a draft or without X checks."},"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."},"reports":{"type":"array","description":"GET /tasks/{id}: what workers reported about it that is still open, newest first (at most 10) — the reason and their words (links, addresses and names taken out, at most 300 characters), never who sent it.","items":{"type":"object","properties":{"reason":{"type":"string","enum":["not_working","scam_or_fraud","pay_to_get_paid","credential_or_account_sale","illegal_or_harmful","spam_or_fake_engagement","other"]},"details":{"type":["string","null"]},"at":{"type":"string","format":"date-time"}}}}}},"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."},"funds_minor":{"type":"integer","description":"What of your available balance can pay for jobs: money you added and credit, never money you earned."},"enough":{"type":"boolean","description":"false when `extra_minor` is more than `funds_minor`: the real call would answer 402 (`insufficient_balance`, or `earnings_cannot_fund`)."}}},"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`); on geo-tunnel tasks also tunnel_unused (the tunnel carried no real traffic for this claim), tunnel_wrong_country (`t` is the task's country, `seen` where the exit actually was) and tunnel_geo_split (`seen` lists the countries the geo vendors disagreed between)."},"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."},"fix":{"$ref":"#/components/schemas/ConversionFix","description":"A conversion task only: the fix you asked for, while it is open (else absent or null)."},"reversed_at":{"type":["string","null"],"description":"A conversion task only: when you took the pay back as not done."},"click":{"type":["string","null"],"description":"A conversion task only: the worker's click code (mwc_…), so your server can find the submission of work it has not confirmed yet."}}},"ConversionFix":{"type":["object","null"],"properties":{"note":{"type":"string"},"due_at":{"type":"string","description":"ISO UTC: the end of the time to fix it."},"kind":{"type":"string","enum":["paid","unpaid"],"description":"`paid`: confirm it again before `due_at` or the pay is taken back; `unpaid`: nothing is paid until you confirm it."}}},"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","submission.closed","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","enum":["auto_approved","approved","held","confirmed"],"description":"`auto_approved`: paid. `held`: our account checks held it — a person decides, nothing was paid. `confirmed`: recorded, paid when the checks finish (`checking`)."},"paid_minor":{"type":"integer"},"already":{"type":"boolean","description":"This code was already paid; nothing more was paid."},"task_completed":{"type":"boolean"},"held":{"type":"boolean"},"checking":{"type":"boolean"},"fix":{"type":"string","enum":["fixed"],"description":"This call confirmed a fix you had asked for on paid work: the pay stays."}}},"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"},"raise_minor":{"type":"integer","description":"A higher price to offer: half again over the recommendation (at least a cent more), never past high_minor."},"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","quantity","steps","proofs","proof_media","proof_included","location","devices","team","profile","minutes","floor","screenshot_floor"]}}}}}}}}}