> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pomerado.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Limits and errors

Use the existing job and retry key to recover from a lost response. Check website effects before creating another execution.

## Personal plan allowances

| Allowance | Personal Free | Personal Premium | Reset |
| - | - | - | - |
| Successful tool runs | 100 per day | 1,000 per day | Midnight UTC |
| First-time website creations | 10 per month | 100 per month | Start of the calendar month in UTC |
| Saved logins per site | One | One | No reset after verified sign-in |

These are the current Personal plan rules. Business accounts do not use these Personal quota limits.

* Count a tool run only when it ends with a confirmed result, on the day it was accepted. A run that fails, is cancelled, is lost, gets no answer to its question, or may have completed without confirmation is not counted. If Pomerado later confirms such a run and returns its result, it counts once.
* Count a new website when its first publication succeeds for the account. A build that ends without publishing is not counted.
* Count further operations for an already-created website without another first-website charge.
* Keep the website's creation history after deletion.

Polling a job, answering pending input and resuming that job do not create another accepted run. Rejoining the same accepted submission with its retry key does not consume another run.

Website creation is tracked by HTTPS origin, including a non-default port. Paths on the same origin do not create separate website allowances.

Work in progress holds a reservation that reduces the remaining allowance until it ends. A successful run or publication turns its reservation into usage; any other ending returns it.

Personal builds require a website origin. An offline build without `site_origin` is not supported for a Personal account.

## Recover from quota denial

* Check the account's plan against the allowances and UTC reset periods above.
* Wait for reset or resolve the plan limit before requesting new work.
* Keep an accepted job's original key when recovering its response.
* Use a new key after a definite quota denial when intentionally submitting a fresh request.

A denied request can retain its denial for the same retry key. Waiting for reset does not turn that old key into a new request. Before switching keys after an ambiguous failure, confirm whether a job was accepted.

When quota or account checks are unavailable, work can be refused until those checks recover. An unavailable check is not permission to bypass a limit.

## Understand MCP failures

Definitive build and run quota denials set `isError` to `true` with a JSON object in the text content. A same-website build denial has this content.

```json theme={null}
{
  "error": "website_pending",
  "job_accepted": false,
  "retry_key_status": "denied",
  "message": "Another build for this website is pending. Check that build, then use a new retry key if another build is still needed."
}
```

* Check the pending build before starting another for that website.
* Wait for reset or resolve the plan limit when `error` is `quota_exceeded`.
* Use a new retry key for a fresh request after either definite denial.
* Keep the original key when recovering an accepted request or an ambiguous failure.

These two denial results confirm that the request did not create a job. They do not change an existing job or turn a denied key into a retryable request.

A website build without `effect` also sets `isError` to `true` and creates no job.

```json theme={null}
{
  "error": "effect_required",
  "job_accepted": false,
  "message": "A website build requires effect. Use read when the tool only looks things up. Use write when it changes anything on the site: filling in or advancing a form that saves data (an application, profile or checkout), submitting, booking, drafts, holds, uploads and account updates. A search, filter or query form is a read. Use ask when unsure: the build first asks read or write, as a pending input request answered with provide_input."
}
```

Resubmit with `read`, `write` or `ask`. With `ask`, the build asks read or write before it touches the site, as a pending input request (see `get_job` and `provide_input`). REST `POST /v1/builds` returns HTTP `400` with the same `effect_required` code.

Pomerado does not build tools for ticket sites, banks and credit unions, or government sites. A build for one of these sites, or any of its subdomains, sets `isError` to `true` and creates no job. `site` names the site and `message` gives the reason.

```json theme={null}
{
  "error": "site_not_supported",
  "job_accepted": false,
  "retryable": false,
  "site": "chase.com",
  "category": "bank",
  "message": "Pomerado can't build for chase.com. Banks don't allow automated access to accounts."
}
```

`category` is `ticket_site`, `bank` or `government`. Calling again does not help. REST `POST /v1/builds` returns HTTP `403` with the same `site_not_supported` code, `site`, `category` and `message`. Only new builds are refused. Tools you already have for these sites keep running.

A run whose `input` does not match the structure of the tool's input schema also sets `isError` to `true` and creates no job. Structure means types, required fields, allowed values and bounds. A field the schema does not list is a mismatch too, since the tool would ignore it and answer a different question. `issues` names each failing field's path and what the schema expects, never the value sent. A `pattern` in the schema is not checked here. The tool checks it when the run starts.

```json theme={null}
{
  "error": "invalid_input",
  "job_accepted": false,
  "retryable": false,
  "message": "The input does not match this tool's input schema, so no job was started. issues names each field's path and what it expects. Fix the input and call again.",
  "issues": [
    { "path": "/seat", "message": "must NOT have additional properties" },
    { "path": "/departure_date", "message": "must be string" }
  ]
}
```

Fix the input and call again. REST `POST /v1/runs` returns HTTP `400` with the same `invalid_input` code and `issues`.

A run whose value the tool refuses when the run starts, by a `pattern` or because the website refused it, was already accepted as a job. Its result has `kind` set to `error` and `error` set to `invalid_input`, with its `job_id`, a `message` giving the tool's reason when it has one, and no `issues`. REST `POST /v1/runs` returns HTTP `409`. Nothing changed on the website, unless the message says a write's step may have changed it, in which case check the website first. Send the corrected input with a new `retry_key`, since the same key with a changed input returns `retry_conflict`.

Other MCP tool failures return the same error code the REST API gives for that failure, with whether repeating the call can help, the underlying message and the failure's detail.

```json theme={null}
{
  "isError": true,
  "content": [
    {
      "type": "text",
      "text": "{\"error\":\"storage_unavailable\",\"retryable\":true,\"retry_after_seconds\":5,\"message\":\"Connection terminated unexpectedly\",\"failure_detail\":{\"subCause\":\"job_storage_failed\",\"operation\":\"jobs.transaction\"}}"
    }
  ]
}
```

* `error` is the REST code, such as `storage_unavailable`, `quota_exceeded` or `retry_conflict`.
* `retryable` is `true` when the same call can succeed later. Wait `retry_after_seconds`, then repeat it with the same `retry_key` if it has one. When it is `false`, change the request first. A write sent without a `retry_key` that fails with `write_possibly_accepted: true` may already have happened: check the job (`get_job`) or read the site's state back before calling it again, because a repeat without the key is a new website action.
* `message` is the underlying reason, and `failure_detail` names the step that failed. Credentials outside URLs are screened; recorded URLs remain exact and may contain credential-like values.

Invalid tool arguments name each failing field's path and what it expected, never the value sent. An elicitation the client declines or cancels returns `input_declined` or `input_cancelled`, and the job's request stays pending. An answer that does not fit the request returns `invalid_request` with the question and reason.

A site with more than 1,000 operations lists the first 1,000 tools. The `tools/list` result then carries `_meta["io.pomerado/truncated"]`; use the account MCP's `find_tool` and `run_tool` to reach the rest.

| Response | Meaning | Next step |
| - | - | - |
| HTTP `401` | Authentication was rejected | Reconnect through the client's OAuth flow |
| HTTP `403` | Account access or required authority was denied | Check account state, resource and permissions |
| HTTP `503` with `account_unavailable` | Account authority could not be checked | Wait `Retry-After` and retry the same request |
| HTTP `503` with `temporary_error` | The run's worker was lost before it finished, and nothing reached the website or the run only reads | Retry the same request with the same `retry_key` |
| MCP tool result with `isError` | The tool could not complete the request | Read `error` and `retryable`, then fix or retry |
| JSON-RPC error | The protocol request could not be handled | Check the advertised tool name and input schema |

A revoked grant, changed permissions or inactive account can prevent polling and answering input as well as new work. Signing into a browser does not restore a denied MCP grant.

## Handle a detailed error when one is exposed

MCP tool results and REST responses carry the same codes. Use the returned code, message and account state.

| Condition | Next step |
| - | - |
| Quota exceeded | Wait for reset or resolve the plan limit |
| Quota unavailable | Wait for quota checks to recover |
| Expired reservation | Confirm no job was accepted before starting a new request |
| Retry conflict | Restore the original arguments for that key |
| Login selection required | Choose an authorized saved connection |
| Credentials required or rejected | Answer the job's login request on its protected page |
| Pending input unavailable | Fetch the latest job and request version |
| Pending input expired | Check job and website state before starting again |
| Result expired or unavailable | Check website state and retained output before starting again |
| Execution budget exhausted | Inspect the job outcome before another execution |
| Access forbidden | Resolve account access or permissions |
| Storage temporarily unavailable | Retry a read or the same keyed submission |
| Temporary error (`worker_lost`) | With `retryable`, retry the same keyed submission; with `possible_commit`, check the website first |

## Keep retry behavior safe

1. Keep the original request arguments and `retry_key`.
2. Wait on the job with `get_job` when you have its ID.
3. Retry the same submission if its response was lost.
4. Check the website if the job reports an uncertain effect.
5. Start a new execution only when you intend another website action.

For temporary failures, use a delay between retries and increase it after repeated failures. Follow any retry timing supplied by the client or service.

Do not turn a generic MCP failure into an automatic new write. A failed response can follow an accepted job or a completed website action.

## Keep identifiers and input valid

* Keep each MCP HTTP request body within 1,000,000 bytes.
* Use job and connection IDs exactly as returned.
* Use retry keys of 1 to 200 letters, digits, underscores or hyphens.
* Pass generic tool `input` values as JSON-encoded strings.
* Match generated tools to their advertised schemas.
* Answer with the current pending request ID and version.

See [tool reference](tool-reference.md) for arguments, [jobs and results](jobs.md) for outcome fields and [saved logins](connections.md) for protected input.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.