---
title: "Run a read-only tool"
method: POST
path: "/v1/runs/read-only"
tags: ["runs"]
---

# Run a read-only tool

`POST /v1/runs/read-only`

## Request

- Base URL: `https://api.pomerado.ai`
- URL: `https://api.pomerado.ai/v1/runs/read-only`
- Auth: HTTP bearer

Run a read-only website tool once and return its job.

It takes the same body as POST /v1/runs and changes nothing on the website, so you can send several at once. A tool that changes the website is refused with tool_not_read_only before any job starts; run it with POST /v1/runs. Follow the job with GET /v1/jobs/{id} or job webhooks. A result over 256 KiB comes as result_file, a single-use result.json download, with result_preview showing its start.

## Headers

- `Idempotency-Key` string: 1 to 200 letters, digits, _ or -, such as a UUID

## Request body

- object
  - `tool_id` string, required: The ID of the tool to run, from a tool search
  - `input` union, required: The tool's input, as its input_schema describes; {} when it takes none
    - object
    - string
    - number
    - boolean
  - `login_id` string: a saved login ID: login_ and 32 lowercase hex digits
  - `login` object: A login for this call, used only when the site has no saved login: a username (or email, phone or account number) and password, or mode "code" without a password when the site sends a code. It passes through your client and model provider; without it, the run asks the person on its answer page.
    - `label` string: a string at most 200 character(s) long
    - `username` string, required: a string at most 1024 character(s) long
    - `password` string: a string at most 16384 character(s) long
    - `mode` 'code'
    - `email` string: a string at most 320 character(s) long
    - `phone` string: a string at most 64 character(s) long
    - `account_number` string: a string at most 128 character(s) long
    - `date_of_birth` string: a string matching the pattern ^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$
    - `zip` string: a string at most 10 character(s) long
  - `save_login` boolean: Save login for later runs (requires login)
  - `two_factor_method` 'sms' | 'call' | 'email' | 'authenticator' | 'push': The two-factor method to use when the site offers several; one the tool supports
  - `correct_login` true: Fix the rejected saved login named by login_id during this run
  - `redact_at_rest` boolean: Staging and preview only: true seals what this job stores and redacts it once it settles, false keeps it readable, overriding the environment's setting. Production refuses a request that names it with invalid_request.
  - `location` 'US' | 'GB' | 'FR' | 'DE' | 'SG': The country websites see this run coming from: US, GB, FR, DE or SG. Without it, the run uses the tool's location, which this doesn't change. Private tools on Business accounts only.
  - `proxy` string: Your own proxy for this call, as http://username:password@host:port, in place of Pomerado's. Leave out location when you pass it. Pomerado keeps it only until this call and any repair it starts have ended. Private tools on Business accounts only.

## Response `200`

The job this Idempotency-Key already started, with Idempotent-Replayed: true

- Job
  - `id` string, required: a job ID: job_ and 32 lowercase hex digits
  - `type` 'run' | 'build', required
  - `status` 'queued' | 'running' | 'needs_input' | 'paused' | 'succeeded' | 'failed' | 'cancelled', required
  - `tool_id` string, nullable, required
  - `input_request` InputRequest, required
    - `id` string, required: An input request ID: req_ and 32 lowercase hex digits
    - `expires_at` string, required
    - `answer_url` string, required
    - `message` string, required
    - `questions` Question[], required
      - union
        - object
          - `id` string, required
          - `prompt` string, required
          - `type` 'choice', required
          - `options` object[], required
            - `id` string, required
            - `label` string, required
          - `allow_other` boolean, required
          - `allow_note` boolean, required
        - object
          - `id` string, required
          - `prompt` string, required
          - `type` 'multi_choice', required
          - `options` object[], required
            - `id` string, required
            - `label` string, required
          - `min_selections` integer, required: an integer
          - `max_selections` integer, required: an integer
          - `allow_other` boolean, required
          - `allow_note` boolean, required
        - object
          - `id` string, required
          - `prompt` string, required
          - `type` 'text', required
          - `max_length` integer, required: an integer
        - object
          - `id` string, required
          - `prompt` string, required
          - `type` 'confirm', required
          - `follow_up` object, nullable, required
            - `prompt` string, required
        - object
          - `id` string, required
          - `prompt` string, required
          - `type` 'secret', required
          - `kind` 'one_time_code' | 'authenticator_code' | 'private_text', required
        - object
          - `id` string, required
          - `prompt` string, required
          - `type` 'login', required
          - `site_url` string, required
          - `fields` string[], required
          - `reason` 'missing' | 'rejected' | 'expired', required
          - `username_masked` string, nullable, required
          - `can_save` boolean, required
  - `pause` JobPause
    - `reason` 'question_expired' | 'sign_in_expired' | 'owner' | 'credentials_rejected', required
    - `paused_at` string, required: a string to be decoded into a Date
    - `expires_at` string, required: a string to be decoded into a Date
    - `resume_by` 'answer' | 'resume' | 'sign_in', required
  - `result` unknown, required
  - `result_file` ResultFile
    - `id` string, required
    - `name` string, required
    - `media_type` string, required
    - `size` number, required
    - `sha256` string, required
    - `download_url` string, required
    - `expires_at` string, required
  - `result_preview` unknown
  - `result_status` 'available' | 'delivered' | 'expired', nullable, required
  - `result_expires_at` string, required: a string to be decoded into a Date
  - `write_status` 'not_attempted' | 'may_have_applied' | 'applied' | 'not_applied', nullable, required
  - `login_save` object, nullable, required
    - `status` 'saved' | 'not_saved' | 'failed', required
    - `login_id` string, nullable, required
    - `reason` string, nullable, required
  - `error` Error, required
    - `error` object, required
      - `code` string, required
      - `message` string, required
      - `retryable` boolean, required
      - `docs_url` string, required
      - `details` object
  - `message` string, nullable, required
  - `maintenance` object, nullable, required
    - `status` 'repairing' | 'repaired' | 'incomplete', required
    - `result_recovered` boolean, required
    - `message` string, required
    - `deadline_at` string, nullable, required
    - `now` JobMaintenanceNow: What the repair is doing now while the run waits for it. Absent otherwise, and from an older backend
      - `label` string, required: One line for the user saying what the repair is doing now: repairing, testing the fix or publishing it
      - `started_at` string, nullable, required: When the repair's current step started (ISO 8601 UTC); null when unknown
    - `waiting_for_repair` true: Present, as true, while the run waits behind another repair of its tool, which runs first. Each time a repair publishes a fixed tool, Pomerado runs the task again on it, and repairs the tool for this run if it still fails. Absent otherwise, and from an older backend
    - `change_in_progress` true: Present, as true, with waiting_for_repair while the repair the run waits behind is a change to the tool that someone requested. When the change publishes, Pomerado runs the task again on the changed tool; deadline_at leaves the change its time to finish. Absent otherwise, and from an older backend
    - `input_refusal` JobMaintenanceInputRefusal: Present while the repair checks a value in the input that the tool refused. It finds whether the input or the tool is wrong. Absent otherwise, and from an older backend
      - `reason` string, nullable, required: The tool's own reason for refusing the value. Null from a status-only read, or when Pomerado can't read it
  - `build` JobBuild, required
    - `integration_id` string, required: an integration ID: int_ and 32 lowercase hex digits
    - `stage` 'queued' | 'checking_login' | 'exploring' | 'building' | 'publishing', required
    - `stages` object[], required
      - `key` 'queued' | 'checking_login' | 'exploring' | 'building' | 'publishing', required
      - `label` string, required
      - `status` 'done' | 'active' | 'pending', required
      - `started_at` string, nullable, required
      - `completed_at` string, nullable, required
    - `now` JobBuildNow
      - `label` string, required: One line for the user saying what the build is doing now, such as its step on the site, a test round's progress or that it waits for an answer. It changes as the build moves on
      - `started_at` string, nullable, required: When this line started (ISO 8601 UTC); null when unknown
    - `eta` object, nullable, required
      - `min_seconds` number, required
      - `max_seconds` number, required
    - `longer_than_usual` boolean: True when this build is queued or running and has run longer than usual. eta is null then
    - `example_result` unknown, required
    - `existing_tool_id` string, nullable, required
    - `change` JobChange, required
      - `revision_id` string, nullable, required
      - `previous_revision_id` string, nullable, required
      - `write_started` boolean, required
    - `recorded` boolean, required: Whether the build's browser was recorded. A Business account watches the recording on the build page, or reads there why it was removed
  - `site_url` string, nullable: The website's https origin the job was asked for: a build's site_url, or the site of the tool a run runs
  - `site_name` string, nullable: The site's name: its tool's integration's, else the integration of its website; null until one names it
  - `task` string, nullable: A build's task, or a requested change, as it was sent; null for a run and a job accepted before Pomerado kept tasks
  - `tool_name` string, nullable: The name of the tool tool_id names, as GET /v1/tools shows it
  - `waiting_since` string, nullable: When the question input_request asks was asked; null when the job asks none
  - `answered_questions` JobAnsweredQuestion[]: The questions the job asked and its member answered, oldest first; a secret, login or code shows only that it was answered
    - `request_id` string, required
    - `question_id` string, required
    - `type` string, required
    - `prompt` string, required
    - `options` string[], nullable, required: The offered options' labels, for a choice; null otherwise
    - `selected` string[], required: The labels picked
    - `other` string, nullable, required: The member's own answer instead of, or beside, the offered options
    - `note` string, nullable, required
    - `text` string, nullable, required: A text answer, or the text a confirmation sent
    - `confirmed` boolean, nullable, required
    - `answered` boolean, required: False for an optional question left unanswered
    - `value_hidden` boolean, required: True for a secret, a login, a code or a saved login's choice: it was answered, and its value is never shown
    - `asked_at` string, nullable, required
    - `answered_at` string, nullable, required
  - `warnings` JobWarning[]: What to tell the person about a build or requested change, as is, or, on the answer that accepted a run, about its call. Absent when there is none
    - `code` 'non_us_site' | 'login_not_needed', required
    - `message` string, required
  - `watch_url` string, required
  - `tip` string
  - `created_at` string, required: a string to be decoded into a Date
  - `updated_at` string, required: a string to be decoded into a Date
  - `completed_at` string, required: a string to be decoded into a Date

## Other responses

- `202`: The new run's job
- `400`: invalid_input, unsupported_login_field, tool_not_read_only, invalid_request
- `401`: unauthorized, reauthentication_required
- `403`: save_forbidden, service_account_not_allowed, site_not_supported, business_account_required, egress_options_not_allowed, forbidden
- `404`: not_found
- `409`: idempotency_conflict, quota_expired, account_selection_required, saved_login_conflict, login_identity_conflict, legacy_duplicate_saved_logins, saved_login_unsettled, file_unavailable
- `413`: file_too_large, file_limit_exceeded, request_too_large
- `415`: file_type_refused
- `422`: file_fetch_failed
- `429`: quota_exceeded, service_account_cap_reached
- `500`: internal_error
- `501`: not_configured
- `503`: acceptance_unknown, temporarily_unavailable

## Changes

- **2026-10-20** `c07cea59691c`: 2 warning, 2 info
  - added the new `paused` enum value to the `status` response property for the response status `200`
  - added the new `paused` enum value to the `status` response property for the response status `202`
  - added the optional property `pause` to the response with the `200` status
  - added the optional property `pause` to the response with the `202` status

[Operation history](https://skmtc.dev/pomerado/apis/pomerado-api/changes/v1/runs/read-only/post.md)

---

[API](https://skmtc.dev/pomerado/apis/pomerado-api.md) · [All operations](https://skmtc.dev/pomerado/apis/pomerado-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pomerado/apis/pomerado-api/revisions/c07cea59691c?raw)
