openapi: 3.1.0
info:
  title: Noptcha API
  version: 1.0.0
  summary: Captcha solving API with an anti-captcha compatible protocol and a native REST API (v1).
  description: |
    Noptcha routes each captcha task to the best available solver (our own models, third-party solving
    services or vision models) and charges only for solved tasks. Prices are in US dollars.

    There are two APIs on the same host. They share keys, prices, task state and limits.

    * **Compatibility API** (root paths such as `/createTask`): the anti-captcha compatible JSON protocol,
      so existing software works by changing the host and the key. Every response is HTTP 200; check
      `errorId` and `errorCode`. Task IDs are integers. The key goes in the `clientKey` body field.
    * **Native API** (`/v1/...`): REST + JSON with synchronous waiting (`Prefer: wait=N`), idempotent
      task creation (`Idempotency-Key`), errors as RFC 9457 problem details, usage reports. Task IDs are
      strings (the same number as in the compatibility API). The key goes in `Authorization: Bearer <key>`.

    ## Keys

    Keys look like `nop_live_…` (live) or `nop_test_…` (sandbox): the prefix, 36 random base62 characters
    and a 4-character checksum. A key has scopes: `tasks:write` (create, poll and report tasks),
    `balance:read`, `usage:read`. `GET /v1/me` and `GET /v1/types` work with any valid key; `GET /v1/catalog`
    needs no key at all. A key can be
    limited to some task types, to client IP ranges, and to a spending budget per period.

    After 50 requests with invalid keys from one IP address (one IPv6 /64; 500 per IPv6 /48) within
    10 minutes, that IP is blocked for the rest of the window for keys we do not already know
    (`rate_limited` / `ERROR_IP_BLOCKED`). Valid keys keep working from that IP.

    ## Sandbox (test keys)

    `nop_test_` keys never call a solver, never charge and never appear in usage. They return fixed answers
    so integrations, SDKs and documentation examples can run end to end:

    * token types return `NOPTCHA_TEST_TOKEN_<task id>` (with `user_agent: "Mozilla/5.0 (Noptcha test)"`
      where the type has one); `funcaptcha` returns only the token; `cloudflare.challenge` returns
      `{"cookies": {"cf_clearance": "NOPTCHA_TEST_TOKEN_<id>"}, "user_agent": …}`;
      `image.text` returns `{"text": "noptcha"}`; image grid and image FunCaptcha types return `{"objects": [0]}`.
    * Magic values in `website_key` (compatibility API: `websiteKey`), `website_public_key`
      (`websitePublicKey`) or `question` simulate outcomes:
      `test-unsolvable…` fails with `unsolvable` (`ERROR_CAPTCHA_UNSOLVABLE`),
      `test-timeout…` fails with `timeout` (`ERROR_CAPTCHA_UNSOLVABLE`),
      `test-proxy-error…` fails with `proxy_error` (`ERROR_PROXY_CONNECT_REFUSED`),
      `test-slow-<seconds>` stays `processing` for that many seconds (at most 90) and then succeeds.
    * Sandbox limits per organization: 10 tasks created per second (`rate_limited`,
      `ERROR_NO_SLOT_AVAILABLE`), 20 tasks still processing (`concurrency_limited`), 20 synchronous waits per
      API process (more waits return 202 at once). The per-key create limit applies too. Sandbox results are
      kept for 10 minutes. Test keys see only sandbox tasks, live keys only live tasks.
    * Sandbox tasks carry `"sandbox": true`; `GET /v1/usage` returns an empty report with `"sandbox": true`.

    ## Limits

    Starting values, subject to change (see `GET /v1/me` for the values that apply to a key):

    * Creating tasks: 200 per second per key, bursts of 200.
    * Polling one task: 5 per second, bursts of 10 (a long poll with `wait` counts once). The compatibility
      API answers `processing` instead of an error when a task is polled too often.
    * Concurrency: 50 tasks in flight per organization and captcha family.
    * Synchronous waiting: at most 90 seconds.
    * Request body: 1 MB for image types, 64 KB for other types.
    * Images: base64 in the standard or URL-safe alphabet (not mixed), with or without `=` padding, line breaks
      or spaces, optionally behind a `data:image/<type>;base64,` prefix; the image is passed on in standard base64.
      Only complete, well-formed PNG, JPEG, GIF, WebP and BMP files are accepted (no multi-picture JPEG / MPO, at most
      100 scans per JPEG), at most 8192 pixels per side and 8,388,608 (2^23) pixels in total, read from the file
      structure (for GIF and WebP the largest of the canvas and every frame). Animated images (GIF, APNG, WebP)
      count every frame as a full canvas: at most 1000 frames and 8,388,608 (2^23) pixels for frames x width x height; all images of one task together
      at most 33,554,432 (2^25) pixels counted the same way.
      Anything else is `422 invalid_params` with a pointer to the image (`/params/images/1`) and a
      `reason` (`image_too_large`, `image_format`, `image_base64`, `image_empty`); in the compatibility API
      `ERROR_TOO_BIG_CAPTCHA_FILESIZE`, `ERROR_IMAGE_TYPE_NOT_SUPPORTED` (format or base64) or
      `ERROR_ZERO_CAPTCHA_FILESIZE`.
    * Results are kept for 10 minutes after a task completes; feedback is accepted in the same window.

    `429` and `503` responses carry `Retry-After` (seconds); wait that long before retrying.

    ## Conventions

    * Every response carries `Noptcha-Request-Id`; include it when you contact support.
    * v1 and webhook times are RFC 3339 in UTC with exactly three fractional digits (milliseconds) and a `Z`
      suffix, for example `2026-11-05T08:00:01.120Z` (trailing zeros are kept). Compatibility API times are
      Unix seconds.
    * v1 money is a decimal string with 6 places plus `currency: "USD"`. The compatibility API returns
      `balance` as a number and `cost` as a string, like anti-captcha.
    * v1 only changes in backward-compatible ways: new fields, endpoints, enum values, error codes and task
      types can appear at any time. Clients must ignore unknown fields and accept unknown values.
    * An unknown path, or a known path with a method it does not support, is `404 not_found` (not 405).
    * Any request whose body is over 1 MB is `413 request_too_large`, whatever the method (GET included).

    ## Webhooks

    Noptcha can POST a task's final result to you instead of (or as well as) you polling for it:

    * **Per task**: `callback_url` (v1) or `callbackUrl` (compatibility API) when you create the task. Live and
      test keys both work; a sandbox task's callback is sent when its result is ready. The v1 body is the
      `task.solved` / `task.failed` event below; the compatibility body is the `getTaskResult` response plus
      `taskId` (see the `compatCallback` webhook).
    * **Organization endpoints**: configured in the console, each subscribed to some of these events and with
      its own signing secret:
      * `task.solved`, `task.failed`: a live task's final result (sandbox tasks are only sent to their own
        callback URL);
      * `balance.low`: the balance (cash + promotional credit) fell below the alert threshold you set in the
        console, at most once per 24 hours (also emailed to owners, admins and billing members);
      * `payment.succeeded`, `payment.refunded`, `payment.disputed`: a top-up was credited, a refund of a top-up
        succeeded (one message per refund), a top-up was disputed (chargeback);
      * `key.expiring`: an API key with an expiry date expires within 7 days (also emailed to owners and admins).

    Requests follow [Standard Webhooks](https://www.standardwebhooks.com/): `POST` with a JSON body and the
    headers `webhook-id` (the same for every retry and manual resend of a message: use it to deduplicate),
    `webhook-timestamp` (Unix seconds when this attempt was sent) and `webhook-signature`
    (`v1,<base64 HMAC-SHA256 of "<webhook-id>.<webhook-timestamp>.<body>">`; several space-separated
    signatures during a secret rotation, accept the request if any one matches). Secrets look like
    `whsec_<base64 of 32 bytes>`; HMAC with the decoded 32 bytes. Per-task callbacks are signed with your
    organization's default webhook secret (shown in the console); endpoints with their own secret. After a
    rotation the previous secret keeps signing for 24 hours. Reject requests whose timestamp is more than a
    few minutes old.

    * Any 2xx response acknowledges the message; anything else, a redirect, or no complete response within
      5 seconds is a failure. Redirects are not followed.
    * Task events are retried 5, 25, 60, 150 and 300 seconds after a failure (±10%), and not after 10 minutes
      (results are only kept for 10 minutes). Sandbox callbacks are tried at most twice. Other events are
      retried 8 times within 24 hours (5 s, 5 min, 30 min, 1 h, 2 h, 4 h, 8 h, 8 h). Messages can arrive
      more than once and out of order; `webhook-id` is fixed per event (for example one per refund), so
      deduplicate on it.
    * Callback URLs must be `http` or `https` on port 80, 443, 8080 or 8443, at most 2048 bytes, without
      user info or fragment, and must resolve to public addresses only. URLs that break these rules when the
      task is created are rejected (`422 invalid_params` with pointer `/callback_url`;
      `ERROR_INCORRECT_SESSION_DATA`); a host that resolves to a non-public address at delivery time is not
      contacted.
    * An endpoint that keeps failing for 24 hours is disabled (owners and admins are emailed); re-enable it
      in the console.
  contact:
    name: Noptcha support
    url: https://noptcha.com
servers:
  - url: https://api.noptcha.com
tags:
  - name: Compatibility
    description: |
      The anti-captcha compatible JSON protocol.

      * Requests are `POST` with a JSON body. `Content-Type` is not required; the body is always parsed as JSON.
      * The key goes in `clientKey`. The key is checked before anything else, so an invalid key is reported
        even when other fields are wrong.
      * Responses are always HTTP 200. `errorId` is 0 on success and 1 on error; then `errorCode` and
        `errorDescription` say what went wrong (see `CompatError` for every code).
      * Two responses come from the infrastructure, not from the protocol: `403` (problem details, code
        `forbidden`) when a request reaches the API without passing through our edge network, and `500`
        (code `internal`) on an unexpected server error. Treat both as transient errors.
      * An unknown path, or a known path with another method (for example `GET /createTask`), is `404`
        (problem details, code `not_found`).
      * Task type names are the common anti-captcha compatible names (case-insensitive); each maps to a
        canonical type and variant, see `CompatTaskType`.
      * `callbackUrl` (optional) receives the final result as a POST (see Webhooks and the `compatCallback`
        webhook). An invalid URL is `ERROR_INCORRECT_SESSION_DATA` and no task is created.
  - name: Tasks
    description: Create and poll tasks, report wrong answers (native API).
  - name: Account
    description: Balance, task types and prices, the key itself, usage, the public catalog (native API).
paths:
  /createTask:
    post:
      tags: [Compatibility]
      operationId: compatCreateTask
      summary: Create a task
      description: |
        Creates a task and returns its integer `taskId`; poll it with `/getTaskResult`.

        When the solver answers synchronously (image types on our own models), the response already has
        `status: "ready"`, `solution` and `cost`; polling it afterwards returns the same result. When such a
        synchronous attempt fails, the response is the task's error (for example `ERROR_CAPTCHA_UNSOLVABLE`)
        and no task ID is returned; nothing is charged.

        `softId` / `appId` (digits) are recorded with the task for the developer programme.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/CompatCreateTaskRequest'}
            example:
              clientKey: nop_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
              task:
                type: RecaptchaV2TaskProxyless
                websiteURL: https://example.com/login
                websiteKey: 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-
      responses:
        '200':
          description: Created (`errorId` 0) or rejected (`errorId` 1).
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/CompatCreateTaskResult'
                  - $ref: '#/components/schemas/CompatError'
        '403': {$ref: '#/components/responses/EdgeForbidden'}
        '500': {$ref: '#/components/responses/InternalError'}
  /getTaskResult:
    post:
      tags: [Compatibility]
      operationId: compatGetTaskResult
      summary: Get a task's result
      description: |
        Returns `status: "processing"` until the task completes, then `status: "ready"` with the solution, or
        the task's error (`errorId` 1). Results are kept for 10 minutes after completion; after that, and for
        tasks of other organizations, the answer is `ERROR_NO_SUCH_CAPCHA_ID`.

        Polling one task more than 5 times per second (bursts of 10) is answered with `processing` instead
        of an error, so clients keep polling. Likewise, when the API is momentarily at capacity, polling is
        answered with `processing` (the other compatibility endpoints answer `ERROR_NO_SLOT_AVAILABLE`; a
        rejected `/createTask` charges nothing and can be resent). Poll every 1–3 seconds.
      security: []
      requestBody: {$ref: '#/components/requestBodies/CompatTaskRequest'}
      responses:
        '200':
          description: Processing, ready, or an error.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/CompatTaskProcessing'
                  - $ref: '#/components/schemas/CompatTaskReady'
                  - $ref: '#/components/schemas/CompatError'
        '403': {$ref: '#/components/responses/EdgeForbidden'}
        '500': {$ref: '#/components/responses/InternalError'}
  /getBalance:
    post:
      tags: [Compatibility]
      operationId: compatGetBalance
      summary: Get the balance
      description: Available balance in US dollars (needs the `balance:read` scope).
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/CompatKeyRequest'}
      responses:
        '200':
          description: Balance or an error.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/CompatBalance'
                  - $ref: '#/components/schemas/CompatError'
        '403': {$ref: '#/components/responses/EdgeForbidden'}
        '500': {$ref: '#/components/responses/InternalError'}
  /reportIncorrectImageCaptcha:
    post:
      tags: [Compatibility]
      operationId: compatReportIncorrectImageCaptcha
      summary: Report a wrong image answer
      description: |
        Reports whether a solved task's answer worked. Accepted for solved tasks within 10 minutes after
        completion; only the first report per task counts, later ones still succeed. Reports are recorded as a quality signal;
        they do not refund the task. Unsolved or unknown tasks: `ERROR_NO_SUCH_CAPCHA_ID`.
      security: []
      requestBody: {$ref: '#/components/requestBodies/CompatTaskRequest'}
      responses:
        '200': {$ref: '#/components/responses/CompatReport'}
        '403': {$ref: '#/components/responses/EdgeForbidden'}
        '500': {$ref: '#/components/responses/InternalError'}
  /reportIncorrectRecaptcha:
    post:
      tags: [Compatibility]
      operationId: compatReportIncorrectRecaptcha
      summary: Report a wrong token
      description: |
        Reports whether a solved task's answer worked. Accepted for solved tasks within 10 minutes after
        completion; only the first report per task counts, later ones still succeed. Reports are recorded as a quality signal;
        they do not refund the task. Unsolved or unknown tasks: `ERROR_NO_SUCH_CAPCHA_ID`.
      security: []
      requestBody: {$ref: '#/components/requestBodies/CompatTaskRequest'}
      responses:
        '200': {$ref: '#/components/responses/CompatReport'}
        '403': {$ref: '#/components/responses/EdgeForbidden'}
        '500': {$ref: '#/components/responses/InternalError'}
  /reportCorrectRecaptcha:
    post:
      tags: [Compatibility]
      operationId: compatReportCorrectRecaptcha
      summary: Report a correct token
      description: |
        Reports whether a solved task's answer worked. Accepted for solved tasks within 10 minutes after
        completion; only the first report per task counts, later ones still succeed. Reports are recorded as a quality signal;
        they do not refund the task. Unsolved or unknown tasks: `ERROR_NO_SUCH_CAPCHA_ID`.
      security: []
      requestBody: {$ref: '#/components/requestBodies/CompatTaskRequest'}
      responses:
        '200': {$ref: '#/components/responses/CompatReport'}
        '403': {$ref: '#/components/responses/EdgeForbidden'}
        '500': {$ref: '#/components/responses/InternalError'}
  /v1/tasks:
    post:
      tags: [Tasks]
      operationId: createTask
      summary: Create a task
      description: |
        Creates a task. A task that is already final (solved or failed) is returned with **200**; a task that
        is still processing with **202**. Both carry `Location: /v1/tasks/{id}`.

        **Synchronous waiting.** With `Prefer: wait=N` (or `?wait=N`), the request waits up to N seconds
        (at most 90) for the result: 200 if the task completes in time, otherwise 202 and you poll
        `GET /v1/tasks/{id}`. `Preference-Applied: wait=N` confirms the wait (N capped at 90). Types solved
        synchronously return the result at once anyway. When an organization already has too many waiting
        requests, the request returns 202 without waiting and without `Preference-Applied`.

        **Idempotency.** With `Idempotency-Key` (1–64 visible ASCII characters, kept for 24 hours per organization),
        retrying the same request returns the same task instead of creating and charging a new one; replayed
        responses carry `Idempotent-Replayed: true`. The same key with a body that is not byte-for-byte identical (serialize once, resend the same bytes) is `409
        idempotency_conflict`; so is a retry while the first request with that key is still being admitted
        (retry shortly). When the first request did not create a task (insufficient balance, no capacity,
        invalid parameters…) the key is released and can be reused. After the task's result has expired
        (10 minutes after completion) a replay is `404 not_found`: the task is not created again, use a new key.

        **Failures.** A task that cannot be solved is a successful request: 200 with `status: "failed"`,
        `error.code` and zero cost. `503 service_unavailable` means no solver could take the task right now;
        nothing was created or charged.

        **Callbacks.** With `callback_url`, the final result is also POSTed there as a `task.solved` or
        `task.failed` event (see Webhooks). An invalid URL is `422 invalid_params` with pointer `/callback_url`
        and no task is created; a `callback_url` that is not a string is `400 invalid_request`. `null` and `""`
        mean no callback.

        Every response passes through the per-key create limit and carries `RateLimit-Policy` and
        `RateLimit` (when rate limiting is enabled), including errors after authentication.
      parameters:
        - $ref: '#/components/parameters/Prefer'
        - $ref: '#/components/parameters/Wait'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/CreateTaskRequest'}
            example:
              type: recaptcha.v2
              params:
                website_url: https://example.com/login
                website_key: 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-
              metadata: {order: A-17}
      responses:
        '200':
          description: The task is final (solved or failed).
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
            Location: {$ref: '#/components/headers/Location'}
            Preference-Applied: {$ref: '#/components/headers/Preference-Applied'}
            Idempotent-Replayed: {$ref: '#/components/headers/Idempotent-Replayed'}
            RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
            RateLimit: {$ref: '#/components/headers/RateLimit'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Task'}
        '202':
          description: The task is still processing; poll `Location`.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
            Location: {$ref: '#/components/headers/Location'}
            Preference-Applied: {$ref: '#/components/headers/Preference-Applied'}
            Idempotent-Replayed: {$ref: '#/components/headers/Idempotent-Replayed'}
            RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
            RateLimit: {$ref: '#/components/headers/RateLimit'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Task'}
        '400': {$ref: '#/components/responses/BadRequest'}
        '401': {$ref: '#/components/responses/Unauthorized'}
        '402': {$ref: '#/components/responses/PaymentRequired'}
        '403': {$ref: '#/components/responses/Forbidden'}
        '404': {$ref: '#/components/responses/NotFound'}
        '409': {$ref: '#/components/responses/Conflict'}
        '413': {$ref: '#/components/responses/ContentTooLarge'}
        '422': {$ref: '#/components/responses/UnprocessableContent'}
        '429': {$ref: '#/components/responses/TooManyRequests'}
        '500': {$ref: '#/components/responses/InternalError'}
        '503': {$ref: '#/components/responses/ServiceUnavailable'}
  /v1/tasks/{id}:
    get:
      tags: [Tasks]
      operationId: getTask
      summary: Get a task
      description: |
        Returns the task: 200 when final, 202 while processing. With `?wait=N` (or `Prefer: wait=N`) this
        is a long poll of up to N seconds (at most 90) that returns as soon as the task completes. Unknown and
        expired tasks, tasks of other organizations, and sandbox tasks asked with a live key (or the reverse)
        are `404`.

        One task may be polled 5 times per second (bursts of 10); a long poll counts once. Over the limit:
        `429 rate_limited` with `Retry-After` and `RateLimit-Policy: "poll";q=10;w=2`. When the server already
        has too many waiting requests, a long poll returns `processing` at once (without
        `Preference-Applied`); wait a second before asking again.
      parameters:
        - $ref: '#/components/parameters/TaskId'
        - $ref: '#/components/parameters/Wait'
        - $ref: '#/components/parameters/Prefer'
      responses:
        '200':
          description: The task is final.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
            Location: {$ref: '#/components/headers/Location'}
            Preference-Applied: {$ref: '#/components/headers/Preference-Applied'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Task'}
        '202':
          description: The task is still processing.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
            Location: {$ref: '#/components/headers/Location'}
            Preference-Applied: {$ref: '#/components/headers/Preference-Applied'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Task'}
        '401': {$ref: '#/components/responses/Unauthorized'}
        '403': {$ref: '#/components/responses/Forbidden'}
        '404': {$ref: '#/components/responses/NotFound'}
        '413': {$ref: '#/components/responses/ContentTooLarge'}
        '429': {$ref: '#/components/responses/TooManyRequests'}
        '500': {$ref: '#/components/responses/InternalError'}
        '503': {$ref: '#/components/responses/ServiceUnavailable'}
  /v1/tasks/{id}/feedback:
    post:
      tags: [Tasks]
      operationId: sendTaskFeedback
      summary: Report whether an answer worked
      description: |
        Tells us whether a solved task's answer was accepted by the target site. Accepted for solved tasks
        within 10 minutes after completion; only the first report per task counts, later ones still return
        200. Feedback is recorded as a quality signal; it does not refund the task. A task that is not solved (processing or
        failed) is `409 task_not_solved`. `reason` is optional and is not stored.
      parameters:
        - $ref: '#/components/parameters/TaskId'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/FeedbackRequest'}
      responses:
        '200':
          description: Recorded.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/FeedbackResult'}
        '400': {$ref: '#/components/responses/BadRequest'}
        '401': {$ref: '#/components/responses/Unauthorized'}
        '403': {$ref: '#/components/responses/Forbidden'}
        '404': {$ref: '#/components/responses/NotFound'}
        '409': {$ref: '#/components/responses/Conflict'}
        '413': {$ref: '#/components/responses/ContentTooLarge'}
        '429': {$ref: '#/components/responses/TooManyRequests'}
        '500': {$ref: '#/components/responses/InternalError'}
        '503': {$ref: '#/components/responses/ServiceUnavailable'}
  /v1/balance:
    get:
      tags: [Account]
      operationId: getBalance
      summary: Get the balance
      description: The organization's balance (needs `balance:read`).
      responses:
        '200':
          description: Balance.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Balance'}
        '401': {$ref: '#/components/responses/Unauthorized'}
        '403': {$ref: '#/components/responses/Forbidden'}
        '413': {$ref: '#/components/responses/ContentTooLarge'}
        '429': {$ref: '#/components/responses/TooManyRequests'}
        '500': {$ref: '#/components/responses/InternalError'}
        '503': {$ref: '#/components/responses/ServiceUnavailable'}
  /v1/types:
    get:
      tags: [Account]
      operationId: listTypes
      summary: List task types and prices
      description: |
        The public task types this key may use, with the organization's actual price per variant. Types
        without a price are left out; `data` is an empty array when none is left. Any valid key may call this,
        whatever its scopes.
      responses:
        '200':
          description: Types and prices.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/TypeList'}
        '401': {$ref: '#/components/responses/Unauthorized'}
        '403': {$ref: '#/components/responses/Forbidden'}
        '413': {$ref: '#/components/responses/ContentTooLarge'}
        '429': {$ref: '#/components/responses/TooManyRequests'}
        '500': {$ref: '#/components/responses/InternalError'}
        '503': {$ref: '#/components/responses/ServiceUnavailable'}
  /v1/me:
    get:
      tags: [Account]
      operationId: getMe
      summary: Describe the current key
      description: |
        The key's organization, settings, budget (with what is used in the current period) and the limits that
        apply to it. Any valid key may call this. The response never contains the key itself.
      responses:
        '200':
          description: The key.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Me'}
        '401': {$ref: '#/components/responses/Unauthorized'}
        '403': {$ref: '#/components/responses/Forbidden'}
        '413': {$ref: '#/components/responses/ContentTooLarge'}
        '429': {$ref: '#/components/responses/TooManyRequests'}
        '500': {$ref: '#/components/responses/InternalError'}
        '503': {$ref: '#/components/responses/ServiceUnavailable'}
  /v1/usage:
    get:
      tags: [Account]
      operationId: getUsage
      summary: Get usage
      description: |
        Task counts and amounts per hour or day, by task type or by key (needs `usage:read`). Amounts are what
        was actually taken from the balance, after corrections. Only periods with data are returned (no zero
        rows), ordered by time and then type or key; the rows always add up to `totals`.

        `from` is rounded down and `to` up to period boundaries (`to` is exclusive). Without `to`: the end of the
        current period; without `from`: 24 hours (hourly) or 30 days (daily) before `to`. Ranges: hourly at most
        7 days, daily at most 92 days; `from` not before 2026-01-01 and not in the future. `group_by=key` covers
        the last 90 days only: an earlier `from` is moved to the first available day (the response shows the
        effective `from`), a range entirely before that is `400`. Results over 10,000 rows (or over the query's
        resource limits) are `400` instead of being truncated; narrow the range or use `granularity=day`.
        Each parameter may be given once.

        Rate limit per organization: 20 per minute, bursts of 5 (test keys counted separately), with
        `RateLimit-Policy: "usage";q=20;w=60`. `503` when usage is temporarily unavailable (or only the per-key
        breakdown is, then with `Retry-After: 30`; `group_by=type` still works) and when too many usage queries
        are running (`Retry-After: 5`).

        Test keys get an empty report with `"sandbox": true` (parameters are still checked and rate limited).
      parameters:
        - name: from
          in: query
          description: Start, RFC 3339 or `YYYY-MM-DD` (UTC).
          schema: {type: string, examples: ['2026-11-01', '2026-11-05T08:00:00Z']}
        - name: to
          in: query
          description: End (exclusive), RFC 3339 or `YYYY-MM-DD` (UTC).
          schema: {type: string}
        - name: granularity
          in: query
          schema: {type: string, enum: [hour, day], default: day}
        - name: group_by
          in: query
          schema: {type: string, enum: [type, key], default: type}
      responses:
        '200':
          description: Usage.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
            RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
            RateLimit: {$ref: '#/components/headers/RateLimit'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Usage'}
        '400': {$ref: '#/components/responses/BadRequest'}
        '401': {$ref: '#/components/responses/Unauthorized'}
        '403': {$ref: '#/components/responses/Forbidden'}
        '413': {$ref: '#/components/responses/ContentTooLarge'}
        '429': {$ref: '#/components/responses/TooManyRequests'}
        '500': {$ref: '#/components/responses/InternalError'}
        '503': {$ref: '#/components/responses/ServiceUnavailable'}
  /v1/catalog:
    get:
      tags: [Account]
      operationId: getCatalog
      summary: Get the public catalog and list prices
      description: |
        The public task types with their list prices, for price tables and integrations that have no key yet.
        No authentication: the `Authorization` header is ignored (a valid, invalid or missing key gets the same
        response, and is not checked or counted against the IP). The items have the same shape as `GET /v1/types`,
        but list every public type at the list price, whatever an organization's actual prices or a key's
        allowed types; use `GET /v1/types` for the prices that apply to you.

        `placeholder` is `true` when the prices are not real prices (test deployments only); do not show them as
        prices then.

        Cacheable: `Cache-Control: public, max-age=300`, a strong `ETag`; send it back in `If-None-Match` to get
        `304 Not Modified`. `HEAD` works as well.

        Cross-origin reads are allowed from any origin: every response on this path, errors included, has
        `Access-Control-Allow-Origin: *` and exposes `ETag`, `Retry-After` and `Noptcha-Request-Id`. There is no
        CORS preflight (`OPTIONS` is not served), so from a browser send a simple request: a plain `fetch(url)`, or
        only CORS-safelisted headers such as `Accept`; a request with `Authorization` or another custom header is
        blocked by the browser. The SDKs' `get_catalog` / `getCatalog` send only `Accept` (no key, and the
        TypeScript SDK no `User-Agent`).

        Rate limit per client IP: 60 per minute, bursts of 30; for IPv6 per /64, and also 600 per minute, bursts of
        300, per /48 (starting values). Over that `429 rate_limited` with `Retry-After`.
      security: []
      parameters:
        - name: If-None-Match
          in: header
          description: An `ETag` from an earlier response (or a list of them, or `*`); `304` when it still matches.
          schema: {type: string, examples: ['"5d41402abc4b2a76b9719d911017c592"']}
      responses:
        '200':
          description: The catalog.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
            Cache-Control: {$ref: '#/components/headers/CatalogCacheControl'}
            ETag: {$ref: '#/components/headers/CatalogETag'}
            Access-Control-Allow-Origin: {$ref: '#/components/headers/CatalogAllowOrigin'}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Catalog'}
        '304':
          description: Not modified (`If-None-Match` matched the current `ETag`); no body.
          headers:
            Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
            Cache-Control: {$ref: '#/components/headers/CatalogCacheControl'}
            ETag: {$ref: '#/components/headers/CatalogETag'}
            Access-Control-Allow-Origin: {$ref: '#/components/headers/CatalogAllowOrigin'}
        '403': {$ref: '#/components/responses/EdgeForbidden'}
        '413': {$ref: '#/components/responses/ContentTooLarge'}
        '429': {$ref: '#/components/responses/TooManyRequests'}
        '500': {$ref: '#/components/responses/InternalError'}
        '503': {$ref: '#/components/responses/ServiceUnavailable'}
security:
  - bearerAuth: []
webhooks:
  task.solved:
    post:
      summary: A task was solved
      description: |
        Sent to a v1 task's `callback_url` and to organization endpoints subscribed to `task.solved`. `data` is the
        task object exactly as `GET /v1/tasks/{id}` returns it. `webhook-id` is `msg_t<task id>` for endpoints,
        `msg_t<task id>_cb` for a live task's callback and `msg_s<task id>_cb` for a sandbox task's callback.
        The compatibility API's `callbackUrl` gets a different body, see `compatCallback`.
      parameters:
        - $ref: '#/components/parameters/WebhookId'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/WebhookTaskEvent'}
            example:
              type: task.solved
              timestamp: '2026-11-05T08:00:07.456Z'
              data:
                id: '7259140378796032'
                type: recaptcha.v2
                status: solved
                params: {website_url: 'https://example.com/login', website_key: 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-}
                solution: {token: 03AFcWeA…, user_agent: Mozilla/5.0 …}
                error: null
                cost: {amount: '0.001000', currency: USD}
                created_at: '2026-11-05T08:00:01.120Z'
                completed_at: '2026-11-05T08:00:07.456Z'
      responses:
        '200':
          description: Any 2xx status acknowledges the message; the response body is ignored.
  task.failed:
    post:
      summary: A task failed
      description: |
        Like `task.solved`, for a task that ended without a solution (`data.status` is `failed`, `data.error` says why).
      parameters:
        - $ref: '#/components/parameters/WebhookId'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/WebhookTaskEvent'}
      responses:
        '200':
          description: Any 2xx status acknowledges the message; the response body is ignored.
  compatCallback:
    post:
      summary: Compatibility API callback
      description: |
        Sent to the `callbackUrl` given to `/createTask`: the `getTaskResult` response for the final result plus
        `taskId`, signed like the other webhooks (verify it if your client can; if not, treat it as a signal and confirm with `getTaskResult`). `webhook-id` is `msg_t<task id>_cb`
        (`msg_s<task id>_cb` for sandbox tasks, whose body also has `"sandbox": true`).
      parameters:
        - $ref: '#/components/parameters/WebhookId'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/CompatCallback'}
      responses:
        '200':
          description: Any 2xx status acknowledges the message; the response body is ignored.
  balance.low:
    post:
      summary: The balance is low
      description: |
        Sent to endpoints subscribed to `balance.low` when a task's charge leaves the balance (cash +
        promotional credit) below the alert threshold set in the console (0 turns it off). The current balance
        is read again just before the message is created: if it is back at or above the threshold (for example
        you just topped up), nothing is sent. At most once per 24 hours per organization: while the balance
        stays low you get one message a day; a top-up does not reset the 24 hours, raising the threshold does.
        Test keys never trigger it. `webhook-id` is `msg_bl_<organization id>_<unix seconds>`.
      parameters:
        - $ref: '#/components/parameters/WebhookId'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/WebhookBalanceLowEvent'}
            example:
              type: balance.low
              timestamp: '2026-11-05T08:00:07.456Z'
              data: {balance: '4.500000', threshold: '5.000000', currency: USD}
      responses:
        '200':
          description: Any 2xx status acknowledges the message; the response body is ignored.
  payment.succeeded:
    post:
      summary: A top-up was credited
      description: |
        A top-up was paid and its amount added to the balance. One message per top-up; `webhook-id` is
        `msg_ps_<payment id>`.
      parameters:
        - $ref: '#/components/parameters/WebhookId'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/WebhookPaymentSucceededEvent'}
            example:
              type: payment.succeeded
              timestamp: '2026-11-05T08:00:07.456Z'
              data: {id: tp_3k9x0q2m8w1v7c5b4n6z, amount: '20.000000', currency: USD, created_at: '2026-11-05T07:58:40.120Z', credited_at: '2026-11-05T08:00:07.456Z'}
      responses:
        '200':
          description: Any 2xx status acknowledges the message; the response body is ignored.
  payment.refunded:
    post:
      summary: A refund succeeded
      description: |
        A refund of a top-up succeeded (refunds requested from support and refunds made by the payment
        provider alike). One message per refund, so a top-up refunded in two parts sends two; `webhook-id`
        is `msg_pr_<refund id>`.
      parameters:
        - $ref: '#/components/parameters/WebhookId'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/WebhookPaymentRefundedEvent'}
            example:
              type: payment.refunded
              timestamp: '2026-11-06T10:12:00.000Z'
              data: {id: '41', payment_id: tp_3k9x0q2m8w1v7c5b4n6z, amount: '5.000000', currency: USD, refunded_at: '2026-11-06T10:12:00.000Z'}
      responses:
        '200':
          description: Any 2xx status acknowledges the message; the response body is ignored.
  payment.disputed:
    post:
      summary: A top-up was disputed
      description: |
        The card issuer opened a dispute (chargeback) on a top-up. One message per dispute, sent when it is
        opened (not when it is resolved); `webhook-id` is `msg_pd_<dispute id>`.
      parameters:
        - $ref: '#/components/parameters/WebhookId'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/WebhookPaymentDisputedEvent'}
            example:
              type: payment.disputed
              timestamp: '2026-11-07T03:00:00.000Z'
              data: {id: '7', payment_id: tp_3k9x0q2m8w1v7c5b4n6z, amount: '20.000000', currency: USD, opened_at: '2026-11-07T03:00:00.000Z'}
      responses:
        '200':
          description: Any 2xx status acknowledges the message; the response body is ignored.
  key.expiring:
    post:
      summary: An API key expires soon
      description: |
        An active API key with an expiry date expires within 7 days (keys still working during a rotation's
        grace period are not included). One message per key and expiry date: changing the expiry date sends
        a new one. `webhook-id` is `msg_ke_<key id>_<expiry as unix seconds>`; `timestamp` is when the
        expiry was noticed.
      parameters:
        - $ref: '#/components/parameters/WebhookId'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/WebhookKeyExpiringEvent'}
            example:
              type: key.expiring
              timestamp: '2026-11-05T08:05:00.000Z'
              data: {id: '12', name: ci, prefix: nop_live_ab12, last4: wxyz, mode: live, expires_at: '2026-11-10T00:00:00.000Z'}
      responses:
        '200':
          description: Any 2xx status acknowledges the message; the response body is ignored.
  webhook.test:
    post:
      summary: Test event
      description: Sent to one endpoint when you press "Send test event" in the console. Never retried.
      parameters:
        - $ref: '#/components/parameters/WebhookId'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/WebhookTestEvent'}
      responses:
        '200':
          description: Any 2xx status acknowledges the message.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        `Authorization: Bearer nop_live_…` (or `nop_test_…` for the sandbox). The compatibility API takes the
        same key in the `clientKey` body field instead.
  parameters:
    TaskId:
      name: id
      in: path
      required: true
      description: Task ID (decimal digits; the same number as the compatibility API's `taskId`).
      schema: {type: string, pattern: '^[0-9]{1,16}$', examples: ['7259140378796032']}
    Wait:
      name: wait
      in: query
      description: |
        Seconds to wait for the result (capped at 90). Takes precedence over `Prefer: wait`. Values that are
        not a non-negative integer are ignored.
      schema: {type: integer, minimum: 0}
    Prefer:
      name: Prefer
      in: header
      description: '`wait=N`: wait up to N seconds (capped at 90) for the result, RFC 7240.'
      schema: {type: string, examples: ['wait=60']}
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      description: |
        1–64 visible ASCII characters (`!` to `~`, no spaces), unique per logical request; kept for 24 hours.
        Anything else is `400 invalid_request`.
      schema: {type: string, pattern: '^[!-~]{1,64}$', examples: [3f6c2b9e-8d41-4a7f-b0c5-1e2d3a4b5c6d]}
    WebhookId:
      name: webhook-id
      in: header
      required: true
      description: Message ID, the same for every retry and manual resend of the message. Use it to deduplicate.
      schema: {type: string, examples: [msg_t7259140378796032]}
    WebhookTimestamp:
      name: webhook-timestamp
      in: header
      required: true
      description: Unix seconds when this attempt was sent (part of the signed content).
      schema: {type: string, pattern: '^[0-9]+$'}
    WebhookSignature:
      name: webhook-signature
      in: header
      required: true
      description: |
        `v1,<base64 HMAC-SHA256 of "<webhook-id>.<webhook-timestamp>.<raw body>">` with the secret's decoded 32 bytes.
        Several space-separated signatures during a rotation (the newest first); accept the request if any matches.
      schema: {type: string, examples: ['v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=']}
  headers:
    Noptcha-Request-Id:
      description: ID of this request (`req_…`); include it when contacting support.
      required: true
      schema: {type: string, pattern: '^req_[a-z2-7]+$'}
    Location:
      description: '`/v1/tasks/{id}`.'
      schema: {type: string, pattern: '^/v1/tasks/[0-9]+$'}
    Preference-Applied:
      description: |
        `wait=N` when `Prefer: wait` or `?wait` was honoured; N is the effective wait in seconds. Absent when
        the wait was asked for but not done because too many requests were already waiting (the response is
        then `202` at once).
      schema: {type: string, pattern: '^wait=[0-9]+$'}
    Idempotent-Replayed:
      description: '`true` when this response replays an earlier request with the same `Idempotency-Key`.'
      schema: {type: string, const: 'true'}
    Retry-After:
      description: Seconds to wait before retrying.
      required: true
      schema: {type: integer, minimum: 1}
    RateLimit-Policy:
      description: |
        The rate limit policy, in the structured form of draft-ietf-httpapi-ratelimit-headers (not final yet):
        `"<name>";q=<quota>;w=<window seconds>`, for example `"create";q=200;w=1`. Policies: `create`
        (`POST /v1/tasks`, every response), `poll` (`GET /v1/tasks/{id}`, only when over the limit), `usage`
        (`GET /v1/usage`), `catalog` (`GET /v1/catalog`, only when over the limit; `q=600` when an IPv6 /48 is over
        its limit).
      schema: {type: string, pattern: '^"[a-z_]+";q=[0-9]+;w=[0-9]+$'}
    RateLimit:
      description: '`"<name>";r=<remaining>;t=<seconds until full>`, for example `"create";r=153;t=1`.'
      schema: {type: string, pattern: '^"[a-z_]+";r=[0-9]+;t=[0-9]+$'}
    CatalogCacheControl:
      description: '`public, max-age=300`: the catalog may be cached for 5 minutes.'
      required: true
      schema: {type: string, const: 'public, max-age=300'}
    CatalogETag:
      description: Strong entity tag of the catalog; changes whenever the content changes.
      required: true
      schema: {type: string, pattern: '^"[0-9a-f]{32}"$'}
    CatalogAllowOrigin:
      description: '`*`: any website may read the catalog.'
      required: true
      schema: {type: string, const: '*'}
  requestBodies:
    CompatTaskRequest:
      required: true
      content:
        application/json:
          schema: {$ref: '#/components/schemas/CompatTaskRequest'}
  responses:
    CompatReport:
      description: Recorded (`errorId` 0) or an error.
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/CompatReportResult'
              - $ref: '#/components/schemas/CompatError'
    BadRequest:
      description: |
        `invalid_request`: the body is not valid JSON or misses a field, a parameter is wrong, `callback_url`
        is not a string, the result would be too large (usage). `unsupported_type`: the task type
        does not exist or is not available.
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    Unauthorized:
      description: '`unauthorized`: the key is malformed, unknown, revoked or expired.'
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    PaymentRequired:
      description: '`insufficient_balance` or `budget_exceeded` (the key''s spending budget for the period is used up).'
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    Forbidden:
      description: |
        `forbidden`: the key lacks the scope this endpoint needs, the client IP is not in the key's allowlist,
        the key may not use this task type, or the organization is suspended (`detail` says which).
        Also returned, without `detail`, when the request did not pass through our edge network.
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    NotFound:
      description: |
        `not_found`: no such task, it expired (results are kept 10 minutes after completion) or it belongs to
        another organization; or the task created with this `Idempotency-Key` has expired.
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    Conflict:
      description: |
        `idempotency_conflict`: the `Idempotency-Key` was used with a different body, or the first request with
        it is still being processed (retry shortly). `task_not_solved`: feedback for a task that is not solved
        (still processing, or failed).
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    ContentTooLarge:
      description: |
        `request_too_large`: the body exceeds the limit (1 MB for image types, 64 KB otherwise). Any request
        with a body over 1 MB gets this, GET requests included.
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    UnprocessableContent:
      description: '`invalid_params`: `params` or `metadata` do not match the schema; `errors` lists each problem.'
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/ValidationProblem'}
    TooManyRequests:
      description: |
        `rate_limited`: too many tasks created per second by this key, the same task polled too often, the
        sandbox's per-organization rate, usage queried too often, the catalog fetched too often from this IP, or
        this IP sent too many invalid keys.
        `concurrency_limited`: the organization has too many tasks in flight for this captcha family (or, with a
        test key, too many sandbox tasks still processing). Wait `Retry-After` seconds.
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        Retry-After: {$ref: '#/components/headers/Retry-After'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    EdgeForbidden:
      description: |
        `forbidden` (problem details): the request reached the API without passing through our edge network.
        Infrastructure, not part of the compatibility protocol.
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    InternalError:
      description: '`internal`: an error on our side.'
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
    ServiceUnavailable:
      description: |
        `service_unavailable`: no capacity right now (all solvers busy or failing, the gateway is overloaded,
        a dependency is unavailable). For `POST /v1/tasks` nothing was created or charged. Wait `Retry-After`.
      headers:
        Noptcha-Request-Id: {$ref: '#/components/headers/Noptcha-Request-Id'}
        Retry-After: {$ref: '#/components/headers/Retry-After'}
        RateLimit-Policy: {$ref: '#/components/headers/RateLimit-Policy'}
        RateLimit: {$ref: '#/components/headers/RateLimit'}
      content:
        application/problem+json:
          schema: {$ref: '#/components/schemas/Problem'}
  schemas:
    Problem:
      type: object
      description: |
        An error, as RFC 9457 problem details (`application/problem+json`). Use `code` in programs; `type` is
        `https://noptcha.com/docs/errors/<code>`. New codes may be added.

        | code | HTTP | when |
        | --- | --- | --- |
        | `invalid_request` | 400 | malformed body, missing field, bad parameter, `callback_url` not a string |
        | `unsupported_type` | 400 | unknown or unavailable task type |
        | `unauthorized` | 401 | invalid, revoked or expired key |
        | `insufficient_balance` | 402 | balance too low for the task's price |
        | `budget_exceeded` | 402 | the key's budget for the period is used up |
        | `forbidden` | 403 | missing scope, IP not allowed, type not allowed, organization suspended |
        | `not_found` | 404 | unknown endpoint or method; unknown, expired or foreign task; expired idempotent task |
        | `idempotency_conflict` | 409 | same `Idempotency-Key` with another body, or still in progress |
        | `task_not_solved` | 409 | feedback for a task that is still processing or failed |
        | `request_too_large` | 413 | body over the limit |
        | `invalid_params` | 422 | `params` or `metadata` do not match the schema, or `callback_url` is not allowed |
        | `rate_limited` | 429 | over a rate limit, or IP blocked after invalid keys |
        | `concurrency_limited` | 429 | too many tasks in flight |
        | `internal` | 500 | our error |
        | `service_unavailable` | 503 | no capacity or a dependency is down |
      required: [type, title, status, code, request_id]
      properties:
        type: {type: string, format: uri, examples: ['https://noptcha.com/docs/errors/insufficient_balance']}
        title: {type: string}
        status: {type: integer, description: The HTTP status code.}
        code: {type: string, description: Machine-readable error code (see the table).}
        detail: {type: string, description: Human-readable explanation of this occurrence.}
        request_id: {type: string, description: Same as the `Noptcha-Request-Id` header.}
      x-noptcha-codes:
        - {code: invalid_request, status: [400]}
        - {code: unsupported_type, status: [400]}
        - {code: unauthorized, status: [401]}
        - {code: insufficient_balance, status: [402]}
        - {code: budget_exceeded, status: [402]}
        - {code: forbidden, status: [403]}
        - {code: not_found, status: [404]}
        - {code: idempotency_conflict, status: [409]}
        - {code: task_not_solved, status: [409]}
        - {code: request_too_large, status: [413]}
        - {code: invalid_params, status: [422]}
        - {code: rate_limited, status: [429]}
        - {code: concurrency_limited, status: [429]}
        - {code: internal, status: [500]}
        - {code: service_unavailable, status: [503]}
    ValidationProblem:
      description: '`invalid_params`, with one entry per problem (at most 50, sorted by pointer).'
      allOf:
        - $ref: '#/components/schemas/Problem'
        - type: object
          required: [errors]
          properties:
            errors:
              type: array
              maxItems: 50
              description: |
                One entry per problem, sorted by `pointer`. At most 50 entries; when there are more, `detail` ends with
                "and N more errors".
              items: {$ref: '#/components/schemas/FieldError'}
    FieldError:
      type: object
      required: [pointer, message]
      properties:
        pointer:
          type: string
          description: |
            JSON Pointer (RFC 6901) to the offending value, from the root of the request body:
            `/params/website_key`, `/params/proxy/port`, `/params` for the parameters object itself,
            `/metadata/order`, `/metadata` for the metadata object itself.
          examples: [/params/website_url, /metadata/order]
        message: {type: string, examples: [is required]}
        reason:
          type: string
          enum: [image_too_large, image_empty, image_base64, image_format]
          description: |
            Machine-readable kind, present for image problems: `image_too_large` (over 1,000,000 characters, the
            pixel or frame limits, or the images of the task together over the per-task limit; the pointer is the
            image that went over), `image_empty`, `image_base64` (not valid base64), `image_format` (not a PNG,
            JPEG, GIF, WebP or BMP image, a multi-picture JPEG, or malformed or truncated). New values may be added.
    Money:
      type: object
      required: [amount, currency]
      properties:
        amount:
          type: string
          pattern: '^-?[0-9]+\.[0-9]{6}$'
          description: Decimal US dollars with 6 places.
          examples: ['0.000900']
        currency: {type: string, const: USD}
    Amount:
      type: string
      pattern: '^-?[0-9]+\.[0-9]{6}$'
      description: Decimal US dollars with 6 places.
      examples: ['1.230000']
    Metadata:
      type: object
      description: |
        Your own key-value pairs, returned with the task. At most 16 keys; keys 1–64 characters, values
        strings of at most 256 characters (Unicode code points, as JSON Schema counts them). A value that is
        not a string is `422 invalid_params` with pointer `/metadata/<key>`.
      maxProperties: 16
      propertyNames: {minLength: 1, maxLength: 64}
      additionalProperties: {type: string, maxLength: 256}
    CallbackUrl:
      type: [string, 'null']
      maxLength: 2048
      description: |
        Where to POST the task's final result (a `task.solved` or `task.failed` event, see Webhooks). `http` or
        `https`, port 80, 443, 8080 or 8443, no user info or fragment, public hosts only. `null` or `""`: no callback.
      example: https://example.com/hooks/noptcha?token=abc
    Task:
      type: object
      required: [id, type, status, params, solution, error, cost, created_at]
      properties:
        id: {type: string, pattern: '^[0-9]+$', description: Task ID (a 53-bit integer as a string)., examples: ['7259140378796032']}
        type: {type: string, description: 'Canonical task type, e.g. `recaptcha.v2` (see `CreateTaskRequest`).'}
        status:
          type: string
          enum: [processing, solved, failed]
        params:
          type: [object, 'null']
          additionalProperties: true
          description: |
            The task's parameters with secrets removed (proxy login and password, cookies where the type marks
            them sensitive) and strings longer than 256 bytes (images) replaced by `"[N bytes]"`. `null` when
            not available.
        solution:
          description: The answer when `status` is `solved`, else `null`. Its shape depends on `type`.
          oneOf:
            - $ref: '#/components/schemas/Solution'
            - type: 'null'
        error:
          description: Why the task failed when `status` is `failed`, else `null`.
          oneOf:
            - $ref: '#/components/schemas/TaskError'
            - type: 'null'
        cost:
          $ref: '#/components/schemas/Money'
          description: What the task cost (zero unless solved and charged).
        created_at: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$'}
        completed_at: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: When the task became final; absent while processing.}
        metadata: {$ref: '#/components/schemas/Metadata'}
        sandbox: {type: boolean, const: true, description: Present (true) on tasks created with a test key.}
    TaskError:
      type: object
      description: |
        | code | meaning |
        | --- | --- |
        | `unsolvable` | the captcha could not be solved (also when no solver accepts these parameters) |
        | `timeout` | not solved before the type's deadline |
        | `proxy_error` | the proxy could not be used |
        | `invalid_site` | the site key or domain is invalid |
        | `invalid_image` | the image is empty, too big or unreadable |
        | `cancelled` | the task was cancelled |

        New codes may be added.
      required: [code, message]
      properties:
        code: {type: string, examples: [unsolvable]}
        message: {type: string}
      x-noptcha-codes: [unsolvable, timeout, proxy_error, invalid_site, invalid_image, cancelled]
    FeedbackRequest:
      type: object
      required: [correct]
      properties:
        correct: {type: boolean, description: Whether the answer was accepted by the target site.}
        reason: {type: string, description: Optional free text; not stored.}
    FeedbackResult:
      type: object
      required: [id, accepted]
      properties:
        id: {type: string, pattern: '^[0-9]+$'}
        accepted: {type: boolean, const: true}
    Balance:
      type: object
      required: [available, cash, promo, held, credit_limit, currency]
      properties:
        available: {$ref: '#/components/schemas/Amount', description: 'What new tasks can use: cash + promo + credit limit − held.'}
        cash: {$ref: '#/components/schemas/Amount'}
        promo: {$ref: '#/components/schemas/Amount', description: Promotional credit.}
        held: {$ref: '#/components/schemas/Amount', description: Reserved for tasks in flight.}
        credit_limit: {$ref: '#/components/schemas/Amount'}
        currency: {type: string, const: USD}
    TypeList:
      type: object
      required: [data, price_version]
      properties:
        data:
          type: array
          items: {$ref: '#/components/schemas/TypeInfo'}
        price_version: {type: integer, description: Version of the price list the prices come from.}
    TypeInfo:
      type: object
      required: [type, family, name, status, timeout_seconds, variants]
      properties:
        type: {type: string, examples: [recaptcha.v2]}
        family:
          type: string
          description: Captcha family (concurrency is limited per family), e.g. `recaptcha`, `hcaptcha`, `cloudflare`, `arkose`, `image`.
        name: {type: string, examples: [reCAPTCHA v2]}
        status: {type: string, description: '`active` or `beta`.', examples: [active]}
        timeout_seconds: {type: integer, description: Deadline after which an unsolved task fails with `timeout`.}
        variants:
          type: array
          items:
            type: object
            required: [variant, price]
            properties:
              variant: {type: string, description: '`proxyless` or `proxy` (the task has `params.proxy`).'}
              price: {$ref: '#/components/schemas/Money'}
    Catalog:
      type: object
      required: [data, price_version, currency, placeholder]
      properties:
        data:
          type: array
          description: Every public task type with a list price, ordered by `type`.
          items: {$ref: '#/components/schemas/TypeInfo'}
        price_version: {type: integer, description: Version of the price list the prices come from.}
        currency: {type: string, const: USD}
        placeholder:
          type: boolean
          description: '`true` when the prices are placeholders, not real prices (test deployments only).'
    Me:
      type: object
      required: [organization, key, budget, limits]
      properties:
        organization:
          type: object
          required: [id, name, status]
          properties:
            id: {type: string}
            name: {type: string}
            status: {type: string, examples: [active]}
        key:
          type: object
          required: [id, name, prefix, last4, mode, scopes, allowed_types, allow_cidrs, expires_at, revoke_at]
          properties:
            id: {type: string}
            name: {type: string}
            prefix: {type: string, examples: [nop_live_AbCd]}
            last4: {type: string}
            mode: {type: string, enum: [live, test]}
            scopes:
              type: array
              items: {type: string, examples: ['tasks:write', 'balance:read', 'usage:read']}
            allowed_types:
              type: [array, 'null']
              items: {type: string}
              description: Task types this key may use; `null` = all.
            allow_cidrs:
              type: [array, 'null']
              items: {type: string, examples: [203.0.113.0/24]}
              description: Client IP ranges this key may be used from; `null` = any.
            expires_at: {type: [string, 'null'], format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$'}
            revoke_at: {type: [string, 'null'], format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: When a rotated key stops working.}
        budget:
          description: The key's spending budget; `null` when unlimited.
          oneOf:
            - type: 'null'
            - type: object
              required: [period, limit, used, remaining]
              properties:
                period: {type: string, enum: [day, week, month, total], description: 'Calendar day / week from Monday / month (UTC), or a lifetime total.'}
                limit: {$ref: '#/components/schemas/Money'}
                used:
                  description: Spent or reserved in the current period; `null` when it cannot be read right now. Always zero for test keys.
                  oneOf: [{$ref: '#/components/schemas/Money'}, {type: 'null'}]
                remaining:
                  oneOf: [{$ref: '#/components/schemas/Money'}, {type: 'null'}]
        limits:
          type: object
          required: [create_tasks, task_polls, concurrency_per_family, concurrency_by_family, wait_max_seconds]
          properties:
            create_tasks:
              description: Tasks this key may create per window; `null` when disabled.
              oneOf: [{$ref: '#/components/schemas/RatePolicy'}, {type: 'null'}]
            task_polls:
              description: Polls per task per window (including bursts); `null` when disabled.
              oneOf: [{$ref: '#/components/schemas/RatePolicy'}, {type: 'null'}]
            concurrency_per_family:
              type: integer
              description: >-
                Default number of tasks in flight per captcha family. Your organization may have a different limit for some families; `concurrency_by_family` has the limit that actually applies.
            concurrency_by_family:
              type: object
              description: >-
                Tasks in flight allowed for this organization, per captcha family (`recaptcha`, `hcaptcha`, …). Every public family is listed. Creating a task beyond the limit returns 429 `concurrency_limited`. Contact support to raise it.
              additionalProperties: {type: integer}
            wait_max_seconds: {type: integer, description: Longest synchronous wait.}
            sandbox:
              type: object
              description: Present for test keys.
              required: [create_tasks_per_org, in_flight_per_org, waits_per_org_per_process]
              properties:
                create_tasks_per_org:
                  oneOf: [{$ref: '#/components/schemas/RatePolicy'}, {type: 'null'}]
                in_flight_per_org: {type: integer}
                waits_per_org_per_process: {type: integer}
    RatePolicy:
      type: object
      required: [limit, window_seconds]
      properties:
        limit: {type: integer}
        window_seconds: {type: integer}
    Usage:
      type: object
      required: [object, from, to, granularity, group_by, currency, sandbox, data, totals]
      properties:
        object: {type: string, const: usage}
        from: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: Effective start (after rounding and the 90-day limit of `group_by=key`).}
        to: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: Effective end (exclusive).}
        granularity: {type: string, enum: [hour, day]}
        group_by: {type: string, enum: [type, key]}
        currency: {type: string, const: USD}
        sandbox: {type: boolean, description: True for test keys (always an empty report).}
        data:
          type: array
          items: {$ref: '#/components/schemas/UsageRow'}
        totals: {$ref: '#/components/schemas/UsageCounts'}
    UsageCounts:
      type: object
      required: [billed, unbilled, amount, cash, promo]
      properties:
        billed: {type: integer, description: Tasks charged.}
        unbilled: {type: integer, description: 'Tasks not charged (unsolvable, timed out, cancelled).'}
        amount: {$ref: '#/components/schemas/Amount', description: cash + promo.}
        cash: {$ref: '#/components/schemas/Amount'}
        promo: {$ref: '#/components/schemas/Amount'}
    UsageRow:
      description: One period and type (`group_by=type`) or key (`group_by=key`).
      allOf:
        - $ref: '#/components/schemas/UsageCounts'
        - type: object
          required: [start, end]
          properties:
            start: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$'}
            end: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$'}
            type: {type: string, description: 'With `group_by=type`.'}
            variant: {type: string, description: 'With `group_by=type`.'}
            key:
              description: |
                With `group_by=key`. `null` is the unattributed remainder (the difference between the ledger and the
                per-key task log; usually zero, briefly non-zero while events are being processed).
              oneOf:
                - type: 'null'
                - type: object
                  required: [id]
                  properties:
                    id: {type: string}
                    name: {type: string}
                    mode: {type: string, enum: [live, test]}
                    hint: {type: string, description: 'Prefix and last four characters, e.g. `nop_live_ab12…wxyz`.'}
    CompatKeyRequest:
      type: object
      required: [clientKey]
      properties:
        clientKey: {type: string, description: 'Your key (`nop_live_…` or `nop_test_…`).'}
    CompatTaskRequest:
      allOf:
        - $ref: '#/components/schemas/CompatKeyRequest'
        - type: object
          required: [taskId]
          properties:
            taskId:
              description: The task ID, as a number or a string of digits.
              oneOf:
                - {type: integer, format: int64}
                - {type: string, pattern: '^\s*[0-9]+\s*$'}
    CompatCreateTaskRequest:
      allOf:
        - $ref: '#/components/schemas/CompatKeyRequest'
        - type: object
          required: [task]
          properties:
            task: {$ref: '#/components/schemas/CompatTask'}
            callbackUrl:
              type: string
              maxLength: 2048
              description: |
                Where to POST the final result (the `getTaskResult` response plus `taskId`, see the `compatCallback`
                webhook). Same rules as v1 `callback_url`; an invalid URL is `ERROR_INCORRECT_SESSION_DATA`. Empty: no callback.
            softId: {type: [integer, string], description: 'Developer programme ID (digits); recorded with the task.'}
            appId: {type: [integer, string], description: Same as `softId`.}
    CompatTask:
      type: object
      required: [type]
      description: |
        The task in anti-captcha's format: `type` (see `CompatTaskType`) plus that type's fields
        (`websiteURL`, `websiteKey`, `body`, …; the field map per name is in `CompatTaskType`). Unknown fields are
        ignored. Parameters are validated against the canonical type's schema (see `CreateTaskRequest`).

        Proxy names (`…Task`, without `Proxyless`) need a proxy: `proxyType` (`http`, `https`, `socks4`,
        `socks5`), `proxyAddress`, `proxyPort`, optional `proxyLogin` and `proxyPassword`; or `proxy` as a URL
        string `http://user:pass@host:port`. Proxyless names ignore proxy fields.
      properties:
        type: {$ref: '#/components/schemas/CompatTaskType'}
        proxyType: {type: string, enum: [http, https, socks4, socks5]}
        proxyAddress: {type: string}
        proxyPort: {type: [integer, string]}
        proxyLogin: {type: string}
        proxyPassword: {type: string}
      additionalProperties: true
    CompatError:
      type: object
      description: |
        A request or task error. `errorCode` values (anti-captcha's codes; new ones may be added):

        | errorCode | when |
        | --- | --- |
        | `ERROR_KEY_DOES_NOT_EXIST` | key malformed, unknown, revoked or expired; or the key lacks the scope this endpoint needs |
        | `ERROR_ZERO_BALANCE` | balance too low, or the key's budget is used up |
        | `ERROR_IP_NOT_ALLOWED` | client IP not in the key's allowlist |
        | `ERROR_IP_BLOCKED` | too many invalid keys from this IP |
        | `ERROR_ACCOUNT_SUSPENDED` | the organization is suspended |
        | `ERROR_NO_SLOT_AVAILABLE` | no capacity, concurrency limit, create rate limit (also sandbox limits), temporary or internal error; retry later |
        | `ERROR_TASK_NOT_SUPPORTED` | unknown task type name, or type not allowed for the key |
        | `ERROR_TASK_ABSENT` | `task` missing or not an object (or the body could not be read) |
        | `ERROR_NO_SUCH_CAPCHA_ID` | `taskId` missing, unknown, expired or of another organization; report for an unsolved task |
        | `ERROR_INCORRECT_SESSION_DATA` | a parameter does not match the schema (`errorDescription` names it), or `callbackUrl` is not allowed |
        | `ERROR_RECAPTCHA_INVALID_SITEKEY` | the site key is invalid (parameter check or solver) |
        | `ERROR_RECAPTCHA_INVALID_DOMAIN` | the website URL, API domain or subdomain is invalid or not allowed |
        | `ERROR_ZERO_CAPTCHA_FILESIZE` | the image is missing or empty |
        | `ERROR_TOO_BIG_CAPTCHA_FILESIZE` | the image (characters or pixels) or the request body is too large |
        | `ERROR_IMAGE_TYPE_NOT_SUPPORTED` | the image is not valid base64, not a PNG, JPEG, GIF, WebP or BMP image, or malformed or truncated (checked when the task is created); or the solver could not read it (task failed) |
        | `ERROR_CAPTCHA_UNSOLVABLE` | the task failed: unsolvable, timed out, or no solver accepts these parameters |
        | `ERROR_PROXY_CONNECT_REFUSED`, `ERROR_PROXY_CONNECT_TIMEOUT`, `ERROR_PROXY_READ_TIMEOUT`, `ERROR_PROXY_BANNED`, `ERROR_PROXY_TRANSPARENT`, `ERROR_PROXY_HAS_NO_IMAGE_SUPPORT`, `ERROR_PROXY_INCOMPATIBLE_HTTP_VERSION` | the proxy could not be used (task failed) |
        | `ERROR_TOKEN_EXPIRED` | the solver reported the token expired (task failed) |
      required: [errorId, errorCode, errorDescription]
      properties:
        errorId: {type: integer, const: 1}
        errorCode: {type: string, examples: [ERROR_ZERO_BALANCE]}
        errorDescription: {type: string}
      x-noptcha-codes:
        - ERROR_KEY_DOES_NOT_EXIST
        - ERROR_ZERO_BALANCE
        - ERROR_IP_NOT_ALLOWED
        - ERROR_IP_BLOCKED
        - ERROR_ACCOUNT_SUSPENDED
        - ERROR_NO_SLOT_AVAILABLE
        - ERROR_TASK_NOT_SUPPORTED
        - ERROR_TASK_ABSENT
        - ERROR_NO_SUCH_CAPCHA_ID
        - ERROR_INCORRECT_SESSION_DATA
        - ERROR_RECAPTCHA_INVALID_SITEKEY
        - ERROR_RECAPTCHA_INVALID_DOMAIN
        - ERROR_ZERO_CAPTCHA_FILESIZE
        - ERROR_TOO_BIG_CAPTCHA_FILESIZE
        - ERROR_IMAGE_TYPE_NOT_SUPPORTED
        - ERROR_CAPTCHA_UNSOLVABLE
        - ERROR_PROXY_CONNECT_REFUSED
        - ERROR_PROXY_CONNECT_TIMEOUT
        - ERROR_PROXY_READ_TIMEOUT
        - ERROR_PROXY_BANNED
        - ERROR_PROXY_TRANSPARENT
        - ERROR_PROXY_HAS_NO_IMAGE_SUPPORT
        - ERROR_PROXY_INCOMPATIBLE_HTTP_VERSION
        - ERROR_TOKEN_EXPIRED
    CompatCreateTaskResult:
      type: object
      required: [errorId, taskId]
      properties:
        errorId: {type: integer, const: 0}
        taskId: {type: integer, format: int64, description: 'Task ID (53-bit integer, safe in JavaScript).'}
        status: {type: string, const: ready, description: Only when solved synchronously.}
        solution: {$ref: '#/components/schemas/CompatSolution'}
        cost: {type: string, pattern: '^[0-9]+\.[0-9]{6}$', description: Only when solved synchronously.}
      dependentRequired:
        status: [solution, cost]
    CompatTaskProcessing:
      type: object
      required: [errorId, status]
      properties:
        errorId: {type: integer, const: 0}
        status: {type: string, const: processing}
    CompatTaskReady:
      type: object
      required: [errorId, status, solution, cost, ip, createTime, endTime, solveCount]
      properties:
        errorId: {type: integer, const: 0}
        status: {type: string, const: ready}
        solution: {$ref: '#/components/schemas/CompatSolution'}
        cost: {type: string, pattern: '^[0-9]+\.[0-9]{6}$', description: 'US dollars, e.g. `"0.001000"`.'}
        ip: {type: string, description: 'Client IP that created the task (empty for sandbox tasks).'}
        createTime: {type: integer, description: Unix seconds.}
        endTime: {type: integer, description: Unix seconds.}
        solveCount: {type: integer, minimum: 1, description: Solver attempts.}
    CompatSolution:
      type: object
      description: |
        The answer in anti-captcha's field names for the task type name used to create the task (for example
        `gRecaptchaResponse` and `userAgent` for reCAPTCHA, `token` for Turnstile, `text` for images). The
        mapping from the canonical solution is listed per name in `CompatTaskType` (`solution`). Tasks created
        through v1 use the first compatible name of their type and variant.
      additionalProperties: true
    WebhookTaskEvent:
      type: object
      required: [type, timestamp, data]
      description: A task event (`task.solved` or `task.failed`), Standard Webhooks' recommended envelope.
      properties:
        type: {type: string, enum: [task.solved, task.failed]}
        timestamp: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: When the task became final.}
        data: {$ref: '#/components/schemas/Task'}
    WebhookBalanceLowEvent:
      type: object
      required: [type, timestamp, data]
      properties:
        type: {type: string, const: balance.low}
        timestamp: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: When the balance was checked (just after a charge).}
        data:
          type: object
          required: [balance, threshold, currency]
          properties:
            balance: {$ref: '#/components/schemas/Amount', description: 'Cash + promotional credit when the balance was checked.'}
            threshold: {$ref: '#/components/schemas/Amount', description: The alert threshold set in the console.}
            currency: {type: string, const: USD}
    WebhookPaymentSucceededEvent:
      type: object
      required: [type, timestamp, data]
      properties:
        type: {type: string, const: payment.succeeded}
        timestamp: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: When the top-up was credited.}
        data:
          type: object
          required: [id, amount, currency, created_at, credited_at]
          properties:
            id: {type: string, description: 'The payment (top-up) ID shown in the console, `tp_…`.', examples: [tp_3k9x0q2m8w1v7c5b4n6z]}
            amount: {$ref: '#/components/schemas/Amount', description: Amount added to the balance.}
            currency: {type: string, const: USD}
            created_at: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$'}
            credited_at: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$'}
    WebhookPaymentRefundedEvent:
      type: object
      required: [type, timestamp, data]
      properties:
        type: {type: string, const: payment.refunded}
        timestamp: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: When the refund succeeded.}
        data:
          type: object
          required: [id, payment_id, amount, currency, refunded_at]
          properties:
            id: {type: string, pattern: '^[0-9]+$', description: Refund ID.}
            payment_id: {type: string, description: 'The refunded payment (top-up), `tp_…`.'}
            amount: {$ref: '#/components/schemas/Amount', description: Amount refunded.}
            currency: {type: string, const: USD}
            refunded_at: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: When the refund first succeeded (does not change afterwards).}
    WebhookPaymentDisputedEvent:
      type: object
      required: [type, timestamp, data]
      properties:
        type: {type: string, const: payment.disputed}
        timestamp: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: When the dispute was opened.}
        data:
          type: object
          required: [id, payment_id, amount, currency, opened_at]
          properties:
            id: {type: string, pattern: '^[0-9]+$', description: Dispute ID.}
            payment_id: {type: string, description: 'The disputed payment (top-up), `tp_…`.'}
            amount: {$ref: '#/components/schemas/Amount', description: Amount in dispute.}
            currency: {type: string, const: USD}
            opened_at: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$'}
    WebhookKeyExpiringEvent:
      type: object
      required: [type, timestamp, data]
      properties:
        type: {type: string, const: key.expiring}
        timestamp: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$', description: When the upcoming expiry was noticed.}
        data:
          type: object
          required: [id, name, prefix, last4, mode, expires_at]
          properties:
            id: {type: string, pattern: '^[0-9]+$', description: API key ID (as in the console).}
            name: {type: string}
            prefix: {type: string, description: 'The first characters of the key, as shown in the console.', examples: [nop_live_ab12]}
            last4: {type: string}
            mode: {type: string, enum: [live, test]}
            expires_at: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$'}
    WebhookTestEvent:
      type: object
      required: [type, timestamp, data]
      properties:
        type: {type: string, const: webhook.test}
        timestamp: {type: string, format: date-time, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$'}
        data:
          type: object
          required: [endpoint_id, message]
          properties:
            endpoint_id: {type: string}
            message: {type: string}
    CompatCallback:
      description: |
        The compatibility API's callback body: the `getTaskResult` response for the final result, plus `taskId`.
        Callbacks for sandbox tasks (created with a test key) also carry `"sandbox": true`; treat such a
        callback as a test, never as a real result.
      oneOf:
        - type: object
          required: [errorId, status, solution, cost, ip, createTime, endTime, solveCount, taskId]
          properties:
            errorId: {type: integer, const: 0}
            status: {type: string, const: ready}
            solution: {$ref: '#/components/schemas/CompatSolution'}
            cost: {type: string, pattern: '^[0-9]+\.[0-9]{6}$'}
            ip: {type: string}
            createTime: {type: integer}
            endTime: {type: integer}
            solveCount: {type: integer, minimum: 1}
            taskId: {type: integer, format: int64}
            sandbox: {type: boolean, const: true, description: Present (true) only for sandbox tasks.}
        - type: object
          required: [errorId, errorCode, errorDescription, taskId]
          properties:
            errorId: {type: integer, const: 1}
            errorCode: {type: string, examples: [ERROR_CAPTCHA_UNSOLVABLE]}
            errorDescription: {type: string}
            taskId: {type: integer, format: int64}
            sandbox: {type: boolean, const: true, description: Present (true) only for sandbox tasks.}
    CompatBalance:
      type: object
      required: [errorId, balance]
      properties:
        errorId: {type: integer, const: 0}
        balance: {type: number, description: Available balance in US dollars.}
    CompatReportResult:
      type: object
      required: [errorId, status]
      properties:
        errorId: {type: integer, const: 0}
        status: {type: string, const: success}
    WebsiteUrl:
      type: string
      format: uri
      pattern: ^[Hh][Tt][Tt][Pp][Ss]?://
      minLength: 8
      maxLength: 4096
      description: Full URL of the page that shows the captcha (http or https).
    WebsiteKey:
      type: string
      minLength: 1
      maxLength: 512
      description: The site key of the captcha widget.
    UserAgent:
      type: string
      minLength: 1
      maxLength: 1024
      description: Browser User-Agent.
    Hostname:
      type: string
      format: hostname
      minLength: 1
      maxLength: 253
    Image:
      type: string
      minLength: 1
      maxLength: 1000000
      description: Base64-encoded PNG, JPEG, GIF, WebP or BMP image (standard or URL-safe alphabet; a data:image/...;base64, prefix, line breaks and missing padding are accepted). At most 1,000,000 characters, 8192 pixels per side and 8,388,608 pixels in total; animated images at most 1000 frames and 8,388,608 pixels for frames x width x height; multi-picture JPEG (MPO), JPEG with more than 100 scans, malformed or truncated images are rejected.
    Question:
      type: string
      minLength: 1
      maxLength: 1024
    Proxy:
      type: object
      description: Proxy the solver must use. login and password are used in memory only and are never stored or logged.
      required:
        - type
        - host
        - port
      additionalProperties: false
      properties:
        type:
          enum:
            - http
            - https
            - socks4
            - socks5
        host:
          type: string
          minLength: 1
          maxLength: 253
          description: IP address or host name. Private and loopback addresses are rejected.
        port:
          type: integer
          minimum: 1
          maximum: 65535
        login:
          type: string
          maxLength: 256
        password:
          type: string
          maxLength: 256
    EnterprisePayload:
      type: object
      maxProperties: 32
      additionalProperties: true
      description: 'Extra enterprise parameters passed to the widget as is, e.g. {"s": "..."} for reCAPTCHA Enterprise or {"rqdata": "..."} for hCaptcha.'
    Token:
      type: string
      minLength: 1
      description: Token to submit to the target site. Single use.
    Indexes:
      type: array
      items:
        type: integer
        minimum: 0
        maximum: 63
      maxItems: 64
      uniqueItems: true
      description: Zero-based indexes of the images or tiles to select, left to right, top to bottom.
    Confidences:
      type: array
      items:
        type: number
        minimum: 0
        maximum: 1
      maxItems: 64
    CloudflareChallengeParams:
      title: Cloudflare challenge page parameters
      x-schema-version: 1
      type: object
      required:
        - website_url
        - proxy
      additionalProperties: false
      properties:
        website_url:
          $ref: '#/components/schemas/WebsiteUrl'
        proxy:
          $ref: '#/components/schemas/Proxy'
        user_agent:
          $ref: '#/components/schemas/UserAgent'
          description: Usually omit it. The User-Agent actually used is returned in the solution.
        wait_load:
          type: boolean
          description: Wait for the page's network activity to settle before returning.
        required_cookies:
          type: array
          items:
            type: string
            minLength: 1
            maxLength: 256
          maxItems: 32
          uniqueItems: true
          description: Cookie names that must all be obtained for the task to succeed, e.g. ["cf_clearance"].
        block_image:
          type: boolean
          description: Do not load images of the target page (saves proxy traffic).
        post_data:
          type: object
          maxProperties: 64
          additionalProperties:
            type:
              - string
              - number
              - boolean
          description: If set, submitted to website_url as an HTML form POST.
    CloudflareChallengeSolution:
      title: Cloudflare challenge page solution
      x-schema-version: 1
      type: object
      required:
        - cookies
        - user_agent
      additionalProperties: false
      properties:
        cookies:
          type: object
          additionalProperties:
            type: string
          description: Cookies after passing the challenge, name to value; usually includes cf_clearance.
        user_agent:
          $ref: '#/components/schemas/UserAgent'
          description: User-Agent the cookies are bound to; use it for every follow-up request.
        headers:
          type: object
          additionalProperties:
            type: string
          description: Response headers of the target page.
        request_headers:
          type: object
          additionalProperties:
            type: string
          description: Request headers of the browser's last request to the target page.
        content:
          type: string
          description: HTML of the target page after passing the challenge.
    CreateCloudflareChallengeTaskRequest:
      type: object
      title: Cloudflare challenge page task
      description: 'Cloudflare challenge page (`cloudflare.challenge`, family `cloudflare`, active). Variants: `proxy` (`params.proxy` is required). Tasks not solved within 120 seconds fail with `timeout`.'
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: cloudflare.challenge
        params:
          $ref: '#/components/schemas/CloudflareChallengeParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    FuncaptchaParams:
      title: FunCaptcha (Arkose Labs) parameters
      x-schema-version: 1
      type: object
      required:
        - website_url
        - website_public_key
      additionalProperties: false
      properties:
        website_url:
          $ref: '#/components/schemas/WebsiteUrl'
        website_public_key:
          type: string
          minLength: 1
          maxLength: 128
          description: The public key (data-pkey).
        api_subdomain:
          $ref: '#/components/schemas/Hostname'
          description: Subdomain that serves the widget, e.g. client-api.arkoselabs.com.
        data:
          type: string
          minLength: 1
          maxLength: 16384
          description: Extra blob data as a JSON string, e.g. {"blob":"..."}.
        user_agent:
          $ref: '#/components/schemas/UserAgent'
        proxy:
          $ref: '#/components/schemas/Proxy'
    FuncaptchaSolution:
      title: FunCaptcha (Arkose Labs) solution
      x-schema-version: 1
      type: object
      required:
        - token
      additionalProperties: false
      properties:
        token:
          $ref: '#/components/schemas/Token'
    CreateFuncaptchaTaskRequest:
      type: object
      title: FunCaptcha (Arkose Labs) task
      description: |-
        FunCaptcha (Arkose Labs) (`funcaptcha`, family `arkose`, beta). Variants: `proxyless`, `proxy` (the `proxy` variant when `params.proxy` is given). Tasks not solved within 180 seconds fail with `timeout`.

        Compatibility API names: `FunCaptchaTask` (proxy), `FunCaptchaTaskProxyless` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: funcaptcha
        params:
          $ref: '#/components/schemas/FuncaptchaParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    HcaptchaParams:
      title: hCaptcha parameters
      x-schema-version: 1
      type: object
      required:
        - website_url
        - website_key
      additionalProperties: false
      properties:
        website_url:
          $ref: '#/components/schemas/WebsiteUrl'
        website_key:
          $ref: '#/components/schemas/WebsiteKey'
        is_invisible:
          type: boolean
          description: true for the invisible hCaptcha.
        enterprise_payload:
          $ref: '#/components/schemas/EnterprisePayload'
        user_agent:
          $ref: '#/components/schemas/UserAgent'
        proxy:
          $ref: '#/components/schemas/Proxy'
    HcaptchaSolution:
      title: hCaptcha solution
      x-schema-version: 1
      type: object
      required:
        - token
      additionalProperties: false
      properties:
        token:
          $ref: '#/components/schemas/Token'
        user_agent:
          $ref: '#/components/schemas/UserAgent'
          description: User-Agent the token was obtained with; use it when submitting the token.
        resp_key:
          type: string
          description: Value some sites require together with the token (respKey).
    CreateHcaptchaTaskRequest:
      type: object
      title: hCaptcha task
      description: |-
        hCaptcha (`hcaptcha`, family `hcaptcha`, active). Variants: `proxyless`, `proxy` (the `proxy` variant when `params.proxy` is given). Tasks not solved within 120 seconds fail with `timeout`.

        Compatibility API names: `HCaptchaTask` (proxy), `HCaptchaTaskProxyless` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: hcaptcha
        params:
          $ref: '#/components/schemas/HcaptchaParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    ImageFuncaptchaParams:
      title: FunCaptcha image parameters
      x-schema-version: 1
      type: object
      required:
        - images
        - question
      additionalProperties: false
      properties:
        images:
          type: array
          items:
            $ref: '#/components/schemas/Image'
          minItems: 1
          maxItems: 20
          description: Base64 of each challenge image (at most 20).
        question:
          $ref: '#/components/schemas/Question'
          description: The question in English, e.g. Pick the image that is the correct way up.
    ImageFuncaptchaSolution:
      title: FunCaptcha image solution
      x-schema-version: 1
      type: object
      required:
        - objects
      additionalProperties: false
      properties:
        objects:
          $ref: '#/components/schemas/Indexes'
    CreateImageFuncaptchaTaskRequest:
      type: object
      title: FunCaptcha image task
      description: |-
        FunCaptcha image (`image.funcaptcha`, family `image`, beta). Variants: `proxyless`. Tasks not solved within 60 seconds fail with `timeout`.

        Compatibility API names: `FunCaptchaClassification` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: image.funcaptcha
        params:
          $ref: '#/components/schemas/ImageFuncaptchaParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    ImageGridAwsParams:
      title: AWS WAF image grid parameters
      x-schema-version: 1
      type: object
      required:
        - image
      additionalProperties: false
      properties:
        image:
          $ref: '#/components/schemas/Image'
          description: Base64 of the whole 3x3 grid image.
        question:
          $ref: '#/components/schemas/Question'
          description: The full question as shown on the page, e.g. Select all hats.
    ImageGridAwsSolution:
      title: AWS WAF image grid solution
      x-schema-version: 1
      type: object
      required:
        - objects
      additionalProperties: false
      properties:
        objects:
          $ref: '#/components/schemas/Indexes'
        confidences:
          $ref: '#/components/schemas/Confidences'
    CreateImageGridAwsTaskRequest:
      type: object
      title: AWS WAF image grid task
      description: 'AWS WAF image grid (`image.grid.aws`, family `image`, beta). Variants: `proxyless`. Tasks not solved within 60 seconds fail with `timeout`.'
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: image.grid.aws
        params:
          $ref: '#/components/schemas/ImageGridAwsParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    ImageGridHcaptchaParams:
      title: hCaptcha image grid parameters
      x-schema-version: 1
      type: object
      required:
        - images
      additionalProperties: false
      properties:
        images:
          type: array
          items:
            $ref: '#/components/schemas/Image'
          minItems: 1
          maxItems: 20
          description: Base64 of each challenge image in page order.
        question:
          $ref: '#/components/schemas/Question'
          description: The full question as shown on the page.
        anchors:
          type: array
          items:
            $ref: '#/components/schemas/Image'
          minItems: 1
          maxItems: 10
          description: Base64 of the example images shown next to the question.
      allOf:
        - anyOf:
            - required:
                - question
              properties:
                question: {}
            - required:
                - anchors
              properties:
                anchors: {}
    ImageGridHcaptchaSolution:
      title: hCaptcha image grid solution
      x-schema-version: 1
      type: object
      required:
        - objects
      additionalProperties: false
      properties:
        objects:
          $ref: '#/components/schemas/Indexes'
        confidences:
          $ref: '#/components/schemas/Confidences'
    CreateImageGridHcaptchaTaskRequest:
      type: object
      title: hCaptcha image grid task
      description: |-
        hCaptcha image grid (`image.grid.hcaptcha`, family `image`, active). Variants: `proxyless`. Tasks not solved within 60 seconds fail with `timeout`.

        Compatibility API names: `HCaptchaClassification` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: image.grid.hcaptcha
        params:
          $ref: '#/components/schemas/ImageGridHcaptchaParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    ImageGridRecaptchaParams:
      title: reCAPTCHA image grid parameters
      x-schema-version: 1
      type: object
      required:
        - image
        - question
      additionalProperties: false
      properties:
        image:
          $ref: '#/components/schemas/Image'
          description: Base64 of the whole challenge image (3x3 or 4x4 grid) or of a single 1x1 tile.
        question:
          $ref: '#/components/schemas/Question'
          description: Question ID such as /m/0k4j.
        grid:
          enum:
            - 1x1
            - 3x3
            - 4x4
          description: Grid shape. Inferred from the image size when omitted.
        confidence:
          type: number
          minimum: 0
          maximum: 1
          description: '3x3 only: return every tile scoring above this threshold instead of the top 3.'
    ImageGridRecaptchaSolution:
      title: reCAPTCHA image grid solution
      x-schema-version: 1
      type: object
      required: []
      additionalProperties: false
      properties:
        objects:
          $ref: '#/components/schemas/Indexes'
        has_object:
          type: boolean
          description: '1x1 tile: whether the tile contains the target.'
        confidences:
          $ref: '#/components/schemas/Confidences'
      allOf:
        - anyOf:
            - required:
                - objects
              properties:
                objects: {}
            - required:
                - has_object
              properties:
                has_object: {}
    CreateImageGridRecaptchaTaskRequest:
      type: object
      title: reCAPTCHA image grid task
      description: |-
        reCAPTCHA image grid (`image.grid.recaptcha`, family `image`, active). Variants: `proxyless`. Tasks not solved within 60 seconds fail with `timeout`.

        Compatibility API names: `ReCaptchaV2Classification` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: image.grid.recaptcha
        params:
          $ref: '#/components/schemas/ImageGridRecaptchaParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    ImageTextParams:
      title: Image to text parameters
      x-schema-version: 1
      type: object
      required:
        - image
      additionalProperties: false
      properties:
        image:
          $ref: '#/components/schemas/Image'
        case_sensitive:
          type: boolean
          description: The answer is case-sensitive.
        numeric:
          enum:
            - 0
            - 1
            - 2
          description: 0 no requirement; 1 digits only; 2 no digits.
        phrase:
          type: boolean
          description: The answer contains at least one space.
        math:
          type: boolean
          description: The image is an arithmetic expression; the answer is its result.
        min_length:
          type: integer
          minimum: 0
          maximum: 100
          description: Minimum answer length; 0 means no limit.
        max_length:
          type: integer
          minimum: 0
          maximum: 100
          description: Maximum answer length; 0 means no limit.
    ImageTextSolution:
      title: Image to text solution
      x-schema-version: 1
      type: object
      required:
        - text
      additionalProperties: false
      properties:
        text:
          type: string
          minLength: 1
          maxLength: 1024
          description: Recognized text.
    CreateImageTextTaskRequest:
      type: object
      title: Image to text task
      description: |-
        Image to text (`image.text`, family `image`, active). Variants: `proxyless`. Tasks not solved within 60 seconds fail with `timeout`.

        Compatibility API names: `ImageToTextTask` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: image.text
        params:
          $ref: '#/components/schemas/ImageTextParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    RecaptchaV2Params:
      title: reCAPTCHA v2 parameters
      x-schema-version: 1
      type: object
      required:
        - website_url
        - website_key
      additionalProperties: false
      properties:
        website_url:
          $ref: '#/components/schemas/WebsiteUrl'
        website_key:
          $ref: '#/components/schemas/WebsiteKey'
        is_invisible:
          type: boolean
          description: true for the invisible reCAPTCHA.
        api_domain:
          $ref: '#/components/schemas/Hostname'
          description: Domain the widget is loaded from, e.g. www.recaptcha.net. Omit for the default.
        user_agent:
          $ref: '#/components/schemas/UserAgent'
        cookies:
          type: string
          maxLength: 8192
          description: 'Cookies for the target site, as in a Cookie header: name1=value1; name2=value2.'
        proxy:
          $ref: '#/components/schemas/Proxy'
    RecaptchaV2Solution:
      title: reCAPTCHA v2 solution
      x-schema-version: 1
      type: object
      required:
        - token
      additionalProperties: false
      properties:
        token:
          $ref: '#/components/schemas/Token'
        user_agent:
          $ref: '#/components/schemas/UserAgent'
          description: User-Agent the token was obtained with; use it when submitting the token.
    CreateRecaptchaV2TaskRequest:
      type: object
      title: reCAPTCHA v2 task
      description: |-
        reCAPTCHA v2 (`recaptcha.v2`, family `recaptcha`, active). Variants: `proxyless`, `proxy` (the `proxy` variant when `params.proxy` is given). Tasks not solved within 120 seconds fail with `timeout`.

        Compatibility API names: `NoCaptchaTask` (proxy), `NoCaptchaTaskProxyless` (proxyless), `RecaptchaV2Task` (proxy), `RecaptchaV2TaskProxyless` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: recaptcha.v2
        params:
          $ref: '#/components/schemas/RecaptchaV2Params'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    RecaptchaV2EnterpriseParams:
      title: reCAPTCHA v2 Enterprise parameters
      x-schema-version: 1
      type: object
      required:
        - website_url
        - website_key
      additionalProperties: false
      properties:
        website_url:
          $ref: '#/components/schemas/WebsiteUrl'
        website_key:
          $ref: '#/components/schemas/WebsiteKey'
        is_invisible:
          type: boolean
          description: true for the invisible reCAPTCHA.
        enterprise_payload:
          $ref: '#/components/schemas/EnterprisePayload'
        api_domain:
          $ref: '#/components/schemas/Hostname'
          description: Domain the widget is loaded from, e.g. www.recaptcha.net. Omit for the default.
        user_agent:
          $ref: '#/components/schemas/UserAgent'
        cookies:
          type: string
          maxLength: 8192
          description: 'Cookies for the target site, as in a Cookie header: name1=value1; name2=value2.'
        proxy:
          $ref: '#/components/schemas/Proxy'
    RecaptchaV2EnterpriseSolution:
      title: reCAPTCHA v2 Enterprise solution
      x-schema-version: 1
      type: object
      required:
        - token
      additionalProperties: false
      properties:
        token:
          $ref: '#/components/schemas/Token'
        user_agent:
          $ref: '#/components/schemas/UserAgent'
          description: User-Agent the token was obtained with; use it when submitting the token.
    CreateRecaptchaV2EnterpriseTaskRequest:
      type: object
      title: reCAPTCHA v2 Enterprise task
      description: |-
        reCAPTCHA v2 Enterprise (`recaptcha.v2.enterprise`, family `recaptcha`, active). Variants: `proxyless`, `proxy` (the `proxy` variant when `params.proxy` is given). Tasks not solved within 120 seconds fail with `timeout`.

        Compatibility API names: `RecaptchaV2EnterpriseTask` (proxy), `RecaptchaV2EnterpriseTaskProxyless` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: recaptcha.v2.enterprise
        params:
          $ref: '#/components/schemas/RecaptchaV2EnterpriseParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    RecaptchaV3Params:
      title: reCAPTCHA v3 parameters
      x-schema-version: 1
      type: object
      required:
        - website_url
        - website_key
      additionalProperties: false
      properties:
        website_url:
          $ref: '#/components/schemas/WebsiteUrl'
        website_key:
          $ref: '#/components/schemas/WebsiteKey'
        page_action:
          type: string
          minLength: 1
          maxLength: 256
          description: The action passed to grecaptcha.execute.
        min_score:
          type: number
          minimum: 0.1
          maximum: 0.9
          description: Minimum score the token should reach (0.1 to 0.9).
        api_domain:
          $ref: '#/components/schemas/Hostname'
          description: Domain the widget is loaded from, e.g. www.recaptcha.net. Omit for the default.
        user_agent:
          $ref: '#/components/schemas/UserAgent'
        cookies:
          type: string
          maxLength: 8192
          description: 'Cookies for the target site, as in a Cookie header: name1=value1; name2=value2.'
    RecaptchaV3Solution:
      title: reCAPTCHA v3 solution
      x-schema-version: 1
      type: object
      required:
        - token
      additionalProperties: false
      properties:
        token:
          $ref: '#/components/schemas/Token'
        user_agent:
          $ref: '#/components/schemas/UserAgent'
          description: User-Agent the token was obtained with; use it when submitting the token.
    CreateRecaptchaV3TaskRequest:
      type: object
      title: reCAPTCHA v3 task
      description: |-
        reCAPTCHA v3 (`recaptcha.v3`, family `recaptcha`, active). Variants: `proxyless`. Tasks not solved within 120 seconds fail with `timeout`.

        Compatibility API names: `RecaptchaV3TaskProxyless` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: recaptcha.v3
        params:
          $ref: '#/components/schemas/RecaptchaV3Params'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    RecaptchaV3EnterpriseParams:
      title: reCAPTCHA v3 Enterprise parameters
      x-schema-version: 1
      type: object
      required:
        - website_url
        - website_key
      additionalProperties: false
      properties:
        website_url:
          $ref: '#/components/schemas/WebsiteUrl'
        website_key:
          $ref: '#/components/schemas/WebsiteKey'
        page_action:
          type: string
          minLength: 1
          maxLength: 256
          description: The action passed to grecaptcha.execute.
        min_score:
          type: number
          minimum: 0.1
          maximum: 0.9
          description: Minimum score the token should reach (0.1 to 0.9).
        enterprise_payload:
          $ref: '#/components/schemas/EnterprisePayload'
        api_domain:
          $ref: '#/components/schemas/Hostname'
          description: Domain the widget is loaded from, e.g. www.recaptcha.net. Omit for the default.
        user_agent:
          $ref: '#/components/schemas/UserAgent'
        cookies:
          type: string
          maxLength: 8192
          description: 'Cookies for the target site, as in a Cookie header: name1=value1; name2=value2.'
    RecaptchaV3EnterpriseSolution:
      title: reCAPTCHA v3 Enterprise solution
      x-schema-version: 1
      type: object
      required:
        - token
      additionalProperties: false
      properties:
        token:
          $ref: '#/components/schemas/Token'
        user_agent:
          $ref: '#/components/schemas/UserAgent'
          description: User-Agent the token was obtained with; use it when submitting the token.
    CreateRecaptchaV3EnterpriseTaskRequest:
      type: object
      title: reCAPTCHA v3 Enterprise task
      description: |-
        reCAPTCHA v3 Enterprise (`recaptcha.v3.enterprise`, family `recaptcha`, active). Variants: `proxyless`. Tasks not solved within 120 seconds fail with `timeout`.

        Compatibility API names: `RecaptchaV3EnterpriseTask` (proxyless), `RecaptchaV3TaskProxyless` (proxyless, with `isEnterprise: true`).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: recaptcha.v3.enterprise
        params:
          $ref: '#/components/schemas/RecaptchaV3EnterpriseParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    TurnstileParams:
      title: Cloudflare Turnstile parameters
      x-schema-version: 1
      type: object
      required:
        - website_url
        - website_key
      additionalProperties: false
      properties:
        website_url:
          $ref: '#/components/schemas/WebsiteUrl'
        website_key:
          $ref: '#/components/schemas/WebsiteKey'
        action:
          type: string
          minLength: 1
          maxLength: 256
          description: The action option of the widget (data-action).
        cdata:
          type: string
          minLength: 1
          maxLength: 1024
          description: The cData option of the widget (data-cdata).
        page_data:
          type: string
          minLength: 1
          maxLength: 8192
          description: The chlPageData value of a challenge page widget.
        user_agent:
          $ref: '#/components/schemas/UserAgent'
        proxy:
          $ref: '#/components/schemas/Proxy'
    TurnstileSolution:
      title: Cloudflare Turnstile solution
      x-schema-version: 1
      type: object
      required:
        - token
      additionalProperties: false
      properties:
        token:
          $ref: '#/components/schemas/Token'
        user_agent:
          $ref: '#/components/schemas/UserAgent'
          description: User-Agent the token was obtained with; use it when submitting the token.
    CreateTurnstileTaskRequest:
      type: object
      title: Cloudflare Turnstile task
      description: |-
        Cloudflare Turnstile (`turnstile`, family `cloudflare`, active). Variants: `proxyless`, `proxy` (the `proxy` variant when `params.proxy` is given). Tasks not solved within 120 seconds fail with `timeout`.

        Compatibility API names: `TurnstileTask` (proxy), `TurnstileTaskProxyless` (proxyless).
      required:
        - type
        - params
      properties:
        type:
          type: string
          const: turnstile
        params:
          $ref: '#/components/schemas/TurnstileParams'
        metadata:
          $ref: '#/components/schemas/Metadata'
        callback_url:
          $ref: '#/components/schemas/CallbackUrl'
    CreateTaskRequest:
      description: |
        A task: `type` selects the parameters schema. New types may be added.

        | `type` | Name | Family | Variants | Timeout | Status |
        | --- | --- | --- | --- | --- | --- |
        | `cloudflare.challenge` | Cloudflare challenge page | `cloudflare` | `proxy` | 120 s | active |
        | `funcaptcha` | FunCaptcha (Arkose Labs) | `arkose` | `proxyless`, `proxy` | 180 s | beta |
        | `hcaptcha` | hCaptcha | `hcaptcha` | `proxyless`, `proxy` | 120 s | active |
        | `image.funcaptcha` | FunCaptcha image | `image` | `proxyless` | 60 s | beta |
        | `image.grid.aws` | AWS WAF image grid | `image` | `proxyless` | 60 s | beta |
        | `image.grid.hcaptcha` | hCaptcha image grid | `image` | `proxyless` | 60 s | active |
        | `image.grid.recaptcha` | reCAPTCHA image grid | `image` | `proxyless` | 60 s | active |
        | `image.text` | Image to text | `image` | `proxyless` | 60 s | active |
        | `recaptcha.v2` | reCAPTCHA v2 | `recaptcha` | `proxyless`, `proxy` | 120 s | active |
        | `recaptcha.v2.enterprise` | reCAPTCHA v2 Enterprise | `recaptcha` | `proxyless`, `proxy` | 120 s | active |
        | `recaptcha.v3` | reCAPTCHA v3 | `recaptcha` | `proxyless` | 120 s | active |
        | `recaptcha.v3.enterprise` | reCAPTCHA v3 Enterprise | `recaptcha` | `proxyless` | 120 s | active |
        | `turnstile` | Cloudflare Turnstile | `cloudflare` | `proxyless`, `proxy` | 120 s | active |
      oneOf:
        - $ref: '#/components/schemas/CreateCloudflareChallengeTaskRequest'
        - $ref: '#/components/schemas/CreateFuncaptchaTaskRequest'
        - $ref: '#/components/schemas/CreateHcaptchaTaskRequest'
        - $ref: '#/components/schemas/CreateImageFuncaptchaTaskRequest'
        - $ref: '#/components/schemas/CreateImageGridAwsTaskRequest'
        - $ref: '#/components/schemas/CreateImageGridHcaptchaTaskRequest'
        - $ref: '#/components/schemas/CreateImageGridRecaptchaTaskRequest'
        - $ref: '#/components/schemas/CreateImageTextTaskRequest'
        - $ref: '#/components/schemas/CreateRecaptchaV2TaskRequest'
        - $ref: '#/components/schemas/CreateRecaptchaV2EnterpriseTaskRequest'
        - $ref: '#/components/schemas/CreateRecaptchaV3TaskRequest'
        - $ref: '#/components/schemas/CreateRecaptchaV3EnterpriseTaskRequest'
        - $ref: '#/components/schemas/CreateTurnstileTaskRequest'
      discriminator:
        propertyName: type
        mapping:
          cloudflare.challenge: '#/components/schemas/CreateCloudflareChallengeTaskRequest'
          funcaptcha: '#/components/schemas/CreateFuncaptchaTaskRequest'
          hcaptcha: '#/components/schemas/CreateHcaptchaTaskRequest'
          image.funcaptcha: '#/components/schemas/CreateImageFuncaptchaTaskRequest'
          image.grid.aws: '#/components/schemas/CreateImageGridAwsTaskRequest'
          image.grid.hcaptcha: '#/components/schemas/CreateImageGridHcaptchaTaskRequest'
          image.grid.recaptcha: '#/components/schemas/CreateImageGridRecaptchaTaskRequest'
          image.text: '#/components/schemas/CreateImageTextTaskRequest'
          recaptcha.v2: '#/components/schemas/CreateRecaptchaV2TaskRequest'
          recaptcha.v2.enterprise: '#/components/schemas/CreateRecaptchaV2EnterpriseTaskRequest'
          recaptcha.v3: '#/components/schemas/CreateRecaptchaV3TaskRequest'
          recaptcha.v3.enterprise: '#/components/schemas/CreateRecaptchaV3EnterpriseTaskRequest'
          turnstile: '#/components/schemas/CreateTurnstileTaskRequest'
    Solution:
      type: object
      description: A task's answer. Its shape is the solution schema of the task's type (`<Type>Solution`, e.g. `RecaptchaV2Solution` for `recaptcha.v2`).
      anyOf:
        - $ref: '#/components/schemas/CloudflareChallengeSolution'
        - $ref: '#/components/schemas/FuncaptchaSolution'
        - $ref: '#/components/schemas/HcaptchaSolution'
        - $ref: '#/components/schemas/ImageFuncaptchaSolution'
        - $ref: '#/components/schemas/ImageGridAwsSolution'
        - $ref: '#/components/schemas/ImageGridHcaptchaSolution'
        - $ref: '#/components/schemas/ImageGridRecaptchaSolution'
        - $ref: '#/components/schemas/ImageTextSolution'
        - $ref: '#/components/schemas/RecaptchaV2Solution'
        - $ref: '#/components/schemas/RecaptchaV2EnterpriseSolution'
        - $ref: '#/components/schemas/RecaptchaV3Solution'
        - $ref: '#/components/schemas/RecaptchaV3EnterpriseSolution'
        - $ref: '#/components/schemas/TurnstileSolution'
    CompatTaskType:
      type: string
      description: |
        Task type name in the compatibility API (case-insensitive). Each name maps to a canonical type and variant (see `CreateTaskRequest` for the parameters). Names ending in `Proxyless` ignore proxy fields; the others require a proxy. Unknown names: `ERROR_TASK_NOT_SUPPORTED`.

        `x-noptcha-aliases` lists, per name, `fields` (compatibility field → canonical parameter; `null`: accepted but not passed on; `to_array`: a single value is wrapped in an array) and `solution` (canonical solution field → compatibility field; `index_bools`: selected indexes become one boolean per image or tile).

        | Name | Canonical type | Variant | Notes |
        | --- | --- | --- | --- |
        | `FunCaptchaClassification` | `image.funcaptcha` | proxyless |  |
        | `FunCaptchaTask` | `funcaptcha` | proxy |  |
        | `FunCaptchaTaskProxyless` | `funcaptcha` | proxyless |  |
        | `HCaptchaClassification` | `image.grid.hcaptcha` | proxyless |  |
        | `HCaptchaTask` | `hcaptcha` | proxy |  |
        | `HCaptchaTaskProxyless` | `hcaptcha` | proxyless |  |
        | `ImageToTextTask` | `image.text` | proxyless |  |
        | `NoCaptchaTask` | `recaptcha.v2` | proxy |  |
        | `NoCaptchaTaskProxyless` | `recaptcha.v2` | proxyless |  |
        | `ReCaptchaV2Classification` | `image.grid.recaptcha` | proxyless |  |
        | `RecaptchaV2EnterpriseTask` | `recaptcha.v2.enterprise` | proxy |  |
        | `RecaptchaV2EnterpriseTaskProxyless` | `recaptcha.v2.enterprise` | proxyless |  |
        | `RecaptchaV2Task` | `recaptcha.v2` | proxy |  |
        | `RecaptchaV2TaskProxyless` | `recaptcha.v2` | proxyless |  |
        | `RecaptchaV3EnterpriseTask` | `recaptcha.v3.enterprise` | proxyless |  |
        | `RecaptchaV3TaskProxyless` | `recaptcha.v3` | proxyless | `isEnterprise: true` → `recaptcha.v3.enterprise` |
        | `TurnstileTask` | `turnstile` | proxy |  |
        | `TurnstileTaskProxyless` | `turnstile` | proxyless |  |
      examples:
        - FunCaptchaClassification
        - FunCaptchaTask
        - FunCaptchaTaskProxyless
      x-noptcha-aliases:
        - name: FunCaptchaClassification
          type: image.funcaptcha
          variant: proxyless
          fields:
            image:
              to: images
              conv: to_array
            question: question
          solution:
            - from: objects
              to: objects
        - name: FunCaptchaTask
          type: funcaptcha
          variant: proxy
          fields:
            data: data
            funcaptchaApiJSSubdomain: api_subdomain
            userAgent: user_agent
            websitePublicKey: website_public_key
            websiteURL: website_url
          solution:
            - from: token
              to: token
        - name: FunCaptchaTaskProxyless
          type: funcaptcha
          variant: proxyless
          fields:
            data: data
            funcaptchaApiJSSubdomain: api_subdomain
            userAgent: user_agent
            websitePublicKey: website_public_key
            websiteURL: website_url
          solution:
            - from: token
              to: token
        - name: HCaptchaClassification
          type: image.grid.hcaptcha
          variant: proxyless
          fields:
            anchors:
              to: anchors
              conv: to_array
            queries: images
            question: question
          solution:
            - from: objects
              to: objects
              conv: index_bools
              len_param: images
            - from: objects
              to: top_k
            - from: confidences
              to: confidences
        - name: HCaptchaTask
          type: hcaptcha
          variant: proxy
          fields:
            enterprisePayload: enterprise_payload
            isInvisible: is_invisible
            rqdata: enterprise_payload.rqdata
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
            - from: resp_key
              to: respKey
        - name: HCaptchaTaskProxyless
          type: hcaptcha
          variant: proxyless
          fields:
            enterprisePayload: enterprise_payload
            isInvisible: is_invisible
            rqdata: enterprise_payload.rqdata
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
            - from: resp_key
              to: respKey
        - name: ImageToTextTask
          type: image.text
          variant: proxyless
          fields:
            body: image
            case: case_sensitive
            image: image
            math: math
            maxLength: max_length
            minLength: min_length
            numeric: numeric
            phrase: phrase
          solution:
            - from: text
              to: text
        - name: NoCaptchaTask
          type: recaptcha.v2
          variant: proxy
          fields:
            apiDomain: api_domain
            cookies: cookies
            enterprisePayload: enterprise_payload
            isEnterprise: null
            isInvisible: is_invisible
            minScore: min_score
            pageAction: page_action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
        - name: NoCaptchaTaskProxyless
          type: recaptcha.v2
          variant: proxyless
          fields:
            apiDomain: api_domain
            cookies: cookies
            enterprisePayload: enterprise_payload
            isEnterprise: null
            isInvisible: is_invisible
            minScore: min_score
            pageAction: page_action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
        - name: ReCaptchaV2Classification
          type: image.grid.recaptcha
          variant: proxyless
          fields:
            body: image
            confidence: confidence
            image: image
            question: question
          solution:
            - from: objects
              to: objects
            - from: has_object
              to: hasObject
            - from: confidences
              to: confidences
        - name: RecaptchaV2EnterpriseTask
          type: recaptcha.v2.enterprise
          variant: proxy
          fields:
            apiDomain: api_domain
            cookies: cookies
            enterprisePayload: enterprise_payload
            isEnterprise: null
            isInvisible: is_invisible
            minScore: min_score
            pageAction: page_action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
        - name: RecaptchaV2EnterpriseTaskProxyless
          type: recaptcha.v2.enterprise
          variant: proxyless
          fields:
            apiDomain: api_domain
            cookies: cookies
            enterprisePayload: enterprise_payload
            isEnterprise: null
            isInvisible: is_invisible
            minScore: min_score
            pageAction: page_action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
        - name: RecaptchaV2Task
          type: recaptcha.v2
          variant: proxy
          fields:
            apiDomain: api_domain
            cookies: cookies
            enterprisePayload: enterprise_payload
            isEnterprise: null
            isInvisible: is_invisible
            minScore: min_score
            pageAction: page_action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
        - name: RecaptchaV2TaskProxyless
          type: recaptcha.v2
          variant: proxyless
          fields:
            apiDomain: api_domain
            cookies: cookies
            enterprisePayload: enterprise_payload
            isEnterprise: null
            isInvisible: is_invisible
            minScore: min_score
            pageAction: page_action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
        - name: RecaptchaV3EnterpriseTask
          type: recaptcha.v3.enterprise
          variant: proxyless
          fields:
            apiDomain: api_domain
            cookies: cookies
            enterprisePayload: enterprise_payload
            isEnterprise: null
            isInvisible: is_invisible
            minScore: min_score
            pageAction: page_action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
        - name: RecaptchaV3TaskProxyless
          type: recaptcha.v3
          variant: proxyless
          fields:
            apiDomain: api_domain
            cookies: cookies
            enterprisePayload: enterprise_payload
            isEnterprise: null
            isInvisible: is_invisible
            minScore: min_score
            pageAction: page_action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          type_if:
            - field: isEnterprise
              equals: true
              type: recaptcha.v3.enterprise
          solution:
            - from: token
              to: gRecaptchaResponse
            - from: user_agent
              to: userAgent
        - name: TurnstileTask
          type: turnstile
          variant: proxy
          fields:
            data: cdata
            pageAction: action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: token
            - from: user_agent
              to: userAgent
        - name: TurnstileTaskProxyless
          type: turnstile
          variant: proxyless
          fields:
            data: cdata
            pageAction: action
            userAgent: user_agent
            websiteKey: website_key
            websiteURL: website_url
          solution:
            - from: token
              to: token
            - from: user_agent
              to: userAgent
