# Sekreto API reference

Sekreto is anti-detect browser automation for AI agents, served over MCP (streamable HTTP).
One endpoint (`https://api.sekreto.ai/mcp`), one key, 22 tools: navigate, read, click, type,
extract clean content from any URL, and search the web — through browser sessions that
websites cannot distinguish from real people.

This file is self-contained. An LLM given only this file can produce working client code.

Generated from the deployed server's live `tools/list` (2026-10-03). The generator lives at
`site/generate_api_md.py` in the wolverine repo; re-run it after any tool-surface change so
this file cannot drift from the implementation.

## Quickstart

One call, no assembly. Replace `wv_live_…` with your key (create an account and top up at
[sekreto.ai/account.html](https://sekreto.ai/account.html)):

```bash
# 1. Search the web
curl -s -X POST https://api.sekreto.ai/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "x-api-key: wv_live_YOUR_KEY" \
  -H "Mcp-Method: tools/call" -H "Mcp-Name: search" \
  --data '{"jsonrpc":"2.0","id":1,"method":"tools/call",
           "params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28",
                              "io.modelcontextprotocol/clientCapabilities":{}},
                     "name":"search",
                     "arguments":{"query":"stealth browser automation","num_results":3,"timeout_ms":30000}}}'

# 2. Extract a page clean
#    (same shape, Mcp-Name: extract, arguments: {"url":"https://example.com","format":"text","main_content_only":true,"timeout_ms":30000})

# 3. Check your balance
curl -s "https://api.sekreto.ai/check?acct=YOUR_16_DIGIT_ACCOUNT_NUMBER"
```

## Authentication

Every request to `/mcp` carries your secret API key:

- Header `x-api-key: wv_live_…` — the RAW key, no `Bearer` prefix. (Adding `Bearer ` inside
  this header value is the most common 401.)
- Or `Authorization: Bearer wv_live_…` — the only header that takes the prefix.

Rotating: POST `/rotate` from the account dashboard (an SCA check confirms it is you).
The old key stops validating on its next request.

## Wire protocol (MCP 2026-07-28, stateless, POST-only)

- Endpoint: `POST https://api.sekreto.ai/mcp`
- `Content-Type: application/json`
- `Accept: application/json, text/event-stream` (required verbatim, both types)
- Header `Mcp-Method: <rpc method>` — must equal the body's JSON-RPC method
  (`tools/list`, `tools/call`, `ping`)
- For `tools/call` additionally header `Mcp-Name: <tool name>` — must equal `params.name`
- Every request's `params._meta` object must include:

```json
{
  "_meta": {
    "io.modelcontextprotocol/protocolVersion": "2026-07-28",
    "io.modelcontextprotocol/clientCapabilities": {}
  }
}
```

Responses come back as SSE data frames: `data: {jsonrpc result}`
(`content-type: text/event-stream`). Parse the `data:` lines and JSON-decode the payload.

Discovery: `tools/list` (headers `Mcp-Method: tools/list`) returns all 22 tools with the
schemas below.

## Client configs (one key, no assembly)

Claude Code (run once, then pick Sekreto in the session):

```bash
claude mcp add --transport http sekreto https://api.sekreto.ai/mcp
```

Claude Desktop: Settings → Connectors → Add custom connector → URL `https://api.sekreto.ai/mcp`.
On first connect a consent page opens; approve, paste your `wv_live_…` key, tokens refresh
automatically after that.

Goose (`~/.config/goose/config.yaml` — raw-key header; Goose's OAuth is broken upstream):

```yaml
extensions:
  sekreto:
    name: sekreto
    type: streamable_http
    url: https://api.sekreto.ai/mcp
    headers:
      x-api-key: wv_live_YOUR_KEY_HERE
    enabled: true
    timeout: 300
```

Any MCP client reading JSON config (Hermes and similar):

```json
{
  "mcp_servers": {
    "sekreto": {
      "url": "https://api.sekreto.ai/mcp",
      "headers": { "x-api-key": "wv_live_YOUR_KEY_HERE" }
    }
  }
}
```

Raw JSON-RPC (python, stdlib only — works with nothing installed):

```python
import json, urllib.request

def call(method, tool=None, arguments=None):
    params = {"_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientCapabilities": {},
    }}
    if tool:
        params["name"] = tool
    if arguments is not None:
        params["arguments"] = arguments
    req = urllib.request.Request("https://api.sekreto.ai/mcp",
        data=json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode(),
        headers={
            "Content-Type": "application/json",
            "Accept": "application/json, text/event-stream",
            "x-api-key": "wv_live_YOUR_KEY_HERE",
            "Mcp-Method": method if tool is None else "tools/call",
            "Mcp-Name": tool or "",
        })
    body = urllib.request.urlopen(req, timeout=120).read().decode()
    for line in body.splitlines():          # SSE frames: "data: {…}"
        if line.startswith("data:"):
            return json.loads(line[5:])
    return json.loads(body)

print(call("tools/list"))                     # discovery
print(call("tools/call", "search",
           {"query": "rust mcp sdk", "num_results": 5, "timeout_ms": 30000}))
print(call("tools/call", "extract",
           {"url": "https://example.com", "format": "markdown",
            "main_content_only": True, "timeout_ms": 30000}))
```

## Pricing and limits

- €0.01 per successful call (2xx). Failed calls (4xx/5xx) are never billed.
- Prepaid packs: €5 = 500 calls, €10 = 1,000, €20 = 2,000 (through Polar, our merchant of
  record; card details never touch Sekreto). First top-up pays +500 bonus calls.
- Calls never expire. No subscription. Pay per call.
- With credit: 60 requests/minute, 5 concurrent requests per key (per HTTP request).
- With zero balance the key is inert until the next top-up (HTTP 429).
- The `usage` tool and REST `GET /usage` are free (not metered) — check balance any time.
- Account balance is publicly checkable without any key: `GET /check?acct=<16-digit account number>`.

## Billing surface (account endpoints, on the gateway but not metered)

| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | `/signup` | none | Create a Mullvad-style account: response carries account_key, api_key (shown once), limits, ready-to-paste MCP configs. 3 per IP per day. |
| GET | `/check?acct=` | none | Usage summary for an account number (Luhn-checked 16 digits). |
| GET | `/usage` | API key | Same usage summary for the presented key (free). |
| PUT / GET | `/atopup`, `/atopup/card`, `/atopup/session` | SCA session | Auto top-up settings + Polar card portal hand-off. |
| POST | `/rotate` | SCA session | Rotate the API key (new plaintext key shown once). |
| POST | `/webhook/polar` | HMAC | Polar order.paid ingestion — credits the account. |

## MCP tools reference

Argument names marked `*` are required. `null`-able types accept JSON null. Tab-taking
tools default to the most recently created tab for the calling session when `tab_id` is
omitted. Session note: pages created during one tools/call belong to the caller's sticky
session (per-tenant fingerprint + residential egress); `usage` is answered by the gateway
and never metered.


### Browser session

#### `browser_navigate`

Navigate to a URL in the browser. Creates a new tab, loads the page, and returns a snapshot of interactive elements with ref IDs.

```json
{
  "type": "object",
  "properties": {
    "timeout_secs": {
      "type": "integer",
      "description": "Navigation timeout in seconds (default: 30)."
    },
    "url": {
      "type": "string",
      "description": "URL to navigate to."
    }
  },
  "required": [
    "url",
    "timeout_secs"
  ]
}
```

#### `browser_snapshot`

Get a text-based snapshot of the current page's accessibility tree. Returns interactive elements with ref IDs (e.g. [e1], [e2]) for use with browser_click and browser_type.

```json
{
  "type": "object",
  "properties": {
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID to snapshot. If not provided, uses the most recently created tab."
    }
  }
}
```

#### `browser_click`

Click an element on the page by its ref ID (e.g. 'e1'). Call browser_snapshot first to get ref IDs.

```json
{
  "type": "object",
  "properties": {
    "ref": {
      "type": "string",
      "description": "Element ref ID (e.g. 'e1'). Get refs from browser_snapshot."
    },
    "return_snapshot": {
      "type": "boolean",
      "description": "Return a page snapshot after clicking. Default: true.\nSet to false when you know the page didn't change (e.g. clicking a checkbox)."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    }
  },
  "required": [
    "ref",
    "return_snapshot"
  ]
}
```

#### `browser_type`

Type text into an input element by its ref ID. Call browser_snapshot first to get ref IDs.

```json
{
  "type": "object",
  "properties": {
    "ref": {
      "type": "string",
      "description": "Element ref ID (e.g. 'e3'). Get refs from browser_snapshot."
    },
    "return_snapshot": {
      "type": "boolean",
      "description": "Return a page snapshot after typing. Default: true.\nSet to false when typing into a form field and the page hasn't changed."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    },
    "text": {
      "type": "string",
      "description": "Text to type into the element."
    }
  },
  "required": [
    "ref",
    "text",
    "return_snapshot"
  ]
}
```

#### `browser_press`

Press a keyboard key (e.g. 'Enter', 'Tab', 'Escape', 'ArrowDown').

```json
{
  "type": "object",
  "properties": {
    "key": {
      "type": "string",
      "description": "Key to press (Enter, Tab, Escape, ArrowDown, ArrowUp, ArrowLeft, ArrowRight, Backspace, Delete, Space)."
    },
    "return_snapshot": {
      "type": "boolean",
      "description": "Return a page snapshot after pressing. Default: true.\nSet to false when pressing a key that doesn't change the page (e.g. ArrowDown in a text field)."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    }
  },
  "required": [
    "key",
    "return_snapshot"
  ]
}
```

#### `browser_scroll`

Scroll the page in a direction. Useful for screenshot-based workflows — scroll down to capture content below the fold via `browser_screenshot`. Does not affect `browser_snapshot` output (accessibility tree is DOM-complete, not viewport-scoped).

```json
{
  "type": "object",
  "properties": {
    "direction": {
      "type": "string",
      "description": "Direction to scroll: up, down, left, or right."
    },
    "return_snapshot": {
      "type": "boolean",
      "description": "Return a page snapshot after scrolling. Default: true.\nShows new content that scrolled into view (lazy-loaded elements, etc.)."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    }
  },
  "required": [
    "direction",
    "return_snapshot"
  ]
}
```

#### `browser_evaluate`

Evaluate a JavaScript expression on the current page and return the result.

```json
{
  "type": "object",
  "properties": {
    "expression": {
      "type": "string",
      "description": "JavaScript expression to evaluate."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    },
    "timeout_secs": {
      "type": "integer",
      "description": "Evaluation timeout in seconds (default: 15)."
    }
  },
  "required": [
    "expression",
    "timeout_secs"
  ]
}
```

#### `browser_back`

Navigate back to the previous page.

```json
{
  "type": "object",
  "properties": {
    "return_snapshot": {
      "type": "boolean",
      "description": "Return a page snapshot after navigating back. Default: true."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    }
  },
  "required": [
    "return_snapshot"
  ]
}
```

#### `browser_close`

Close the current browser tab and free its context slot.

```json
{
  "type": "object",
  "properties": {
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID to close. If not provided, closes the most recently created tab."
    }
  }
}
```

#### `browser_screenshot`

Capture a screenshot of the current page. Returns a base64-encoded PNG screenshot. Typical payload: 100-300KB. Prefer `browser_snapshot` or `extract` first — use `browser_screenshot` only when visual inspection is needed (CAPTCHAs, canvas-based content, layout verification), or as a fallback when the accessibility tree doesn't capture meaningful content (WebGL, images, complex visual layouts that are invisible to the DOM).

```json
{
  "type": "object",
  "properties": {
    "format": {
      "type": "string",
      "description": "Image format: 'png' (default) or 'jpeg'."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    }
  },
  "required": [
    "format"
  ]
}
```

### Content primitives

#### `extract`

Extract content from a URL. Creates a tab, navigates to the URL, optionally waits for a CSS selector, extracts the page content, and returns the title, content, url, and content_type. The tab is closed automatically.

```json
{
  "type": "object",
  "properties": {
    "format": {
      "type": "string",
      "description": "Extraction format: \"text\", \"markdown\", or \"html\". Default: \"text\"."
    },
    "main_content_only": {
      "type": "boolean",
      "description": "Filter to main page content only: extract from the content root\n(`main`/`article`/`[role=main]`, else drop nav/footer boilerplate).\nDefault: true."
    },
    "timeout_ms": {
      "type": "integer",
      "description": "Total timeout in milliseconds for create + navigate + wait + extract. Default: 30000."
    },
    "url": {
      "type": "string",
      "description": "URL to extract content from."
    },
    "wait_for": {
      "type": [
        "string",
        "null"
      ],
      "description": "CSS selector(s) to wait for before extracting. Comma-separated. Optional."
    }
  },
  "required": [
    "url",
    "format",
    "main_content_only",
    "timeout_ms"
  ]
}
```

#### `search`

Search the web via the Brave Search API. Returns results with URLs, titles, and snippets. Requests pass through a global QPS limiter and circuit breaker; on throttling, a rate_limited error with retry_after_ms is returned.

```json
{
  "type": "object",
  "properties": {
    "num_results": {
      "type": "integer",
      "description": "Max results. Default: 10."
    },
    "query": {
      "type": "string",
      "description": "Search query string."
    },
    "timeout_ms": {
      "type": "integer",
      "description": "Total timeout in milliseconds. Default: 30000."
    }
  },
  "required": [
    "query",
    "num_results",
    "timeout_ms"
  ]
}
```

#### `browser_script`

Execute multiple browser actions in a single call. Composes navigate, click, type, wait, and read into one round-trip — dramatically reduces token usage and network latency compared to individual browser_navigate + browser_click + browser_type calls. Returns the results of each step.

```json
{
  "type": "object",
  "properties": {
    "steps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "action": {
            "type": "string",
            "description": "Action to perform: \"click\", \"type\", \"wait\", \"read\"."
          },
          "selector": {
            "type": [
              "string",
              "null"
            ],
            "description": "CSS selector for the target element."
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "description": "Text to type (for \"type\" action)."
          },
          "timeout_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Timeout in milliseconds for this step (for \"wait\" action). Default: 5000."
          }
        },
        "required": [
          "action"
        ]
      },
      "description": "Ordered list of steps to execute after navigation.\nEach step has: action (navigate|click|type|wait|read), selector (CSS), text (for type), timeout_ms."
    },
    "timeout_ms": {
      "type": "integer",
      "description": "Total timeout in milliseconds. Default: 30000."
    },
    "url": {
      "type": "string",
      "description": "URL to navigate to. The tool creates a fresh tab, navigates, executes steps, and closes the tab."
    }
  },
  "required": [
    "url",
    "steps",
    "timeout_ms"
  ]
}
```

### Account & instances

#### `usage`

Return the current usage summary for the authenticated key: requests used this month, monthly limit, per-tier breakdown, and proxy bandwidth. Mirrors REST GET /usage. In gateway (multitenant) deployments, tenant usage is answered by the gateway, not this tool.

```json
{
  "type": "object",
  "properties": {}
}
```

#### `list_instances`

List all browser instances: the default instance plus any extra instances launched with custom fingerprints (via create_instance). Returns instance_id, pid, version, fingerprint_active, context_count, tab_count, is_default for each.

```json
{
  "type": "object",
  "properties": {}
}
```

#### `create_instance`

Launch a new browser instance with an optional custom fingerprint (locale, timezone, screen, os, webrtc, geolocation — Camoufox config JSON), and register it for use. Extra instances are isolated from the default; target them by passing their instance_id where tools accept it, or by first creating a tab in them via REST create_tab. Returns the new instance_id.

```json
{
  "type": "object",
  "properties": {
    "contexts": {
      "type": "integer",
      "description": "Number of browser contexts to create in this instance (default 1)."
    },
    "fingerprint_config": {
      "type": [
        "string",
        "null"
      ],
      "description": "Camoufox fingerprint config JSON string (same as REST POST /instances).\nOptional \u2014 omit for a plain unfingerprinted extra instance."
    },
    "headed": {
      "type": [
        "boolean",
        "null"
      ],
      "description": "Run this instance in headed mode (for debugging)."
    }
  },
  "required": [
    "contexts"
  ]
}
```

#### `delete_instance`

Stop and remove an extra browser instance (created via create_instance). The default instance cannot be deleted. Tabs and contexts in the instance are destroyed.

```json
{
  "type": "object",
  "properties": {
    "instance_id": {
      "type": "string",
      "description": "Instance ID to delete (from create_instance or list_instances)."
    }
  },
  "required": [
    "instance_id"
  ]
}
```

#### `get_cookies`

Read all cookies for the browser context of a tab. Useful for exporting login state so it can be re-imported later (import_cookies) for session reuse. Cookie values are sensitive — treat the result as a secret.

```json
{
  "type": "object",
  "properties": {
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    }
  }
}
```

#### `import_cookies`

Import cookies into a tab's browser context (session reuse: restore login state captured earlier via get_cookies, or from another browser). Each cookie needs name and value, plus either url or domain. Cookies live in memory for the context lifetime — nothing is persisted to disk.

```json
{
  "type": "object",
  "properties": {
    "cookies": {
      "type": "array",
      "items": {},
      "description": "Cookies to import (name + value + url or domain each)."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    }
  },
  "required": [
    "cookies"
  ]
}
```

#### `handle_dialog`

Resolve a pending JavaScript dialog (alert/confirm/prompt) in a tab: accept it, or dismiss it. For prompt dialogs, optionally supply the text to submit when accepting. Fails if no dialog is pending.

```json
{
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "description": "\"accept\" or \"dismiss\"."
    },
    "prompt_text": {
      "type": [
        "string",
        "null"
      ],
      "description": "For prompt dialogs: the text to submit (accepted only)."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    }
  },
  "required": [
    "action"
  ]
}
```

#### `get_links`

Extract all http(s) links from a page (url + text, capped at 200 chars per text). Returns total and a paged list — pass limit (default 50, max 500) and offset for large pages.

```json
{
  "type": "object",
  "properties": {
    "limit": {
      "type": "integer",
      "description": "Max links to return (default 50, max 500)."
    },
    "offset": {
      "type": "integer",
      "description": "Offset into the full link list for paging."
    },
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    }
  },
  "required": [
    "limit",
    "offset"
  ]
}
```

#### `solve_captcha`

Attempt to auto-solve a detected captcha/interstitial challenge on the page (same solver as the extract pipeline). Use after browser_navigate when snapshot content looks like a challenge page. Returns the captcha type and whether it was solved.

```json
{
  "type": "object",
  "properties": {
    "tab_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Tab ID. If not provided, uses the most recently created tab."
    },
    "timeout_secs": {
      "type": "integer",
      "description": "Timeout in seconds (default 60)."
    }
  },
  "required": [
    "timeout_secs"
  ]
}
```

## Example calls and real responses

### search

Request: Mcp-Method `tools/call`, Mcp-Name `search`:

```json
{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28",
                    "io.modelcontextprotocol/clientCapabilities":{}},
           "name":"search",
           "arguments":{"query":"esperanto word for stealth","num_results":3,"timeout_ms":30000}}}
```

Response (trimmed mid-list; two of three hits shown):

```json
{
  "ok": true,
  "query": "esperanto word for stealth",
  "results": [
    {
      "url": "https://www.definitions.net/translate/stealth/eo",
      "title": "How to say stealth in Esperanto? - Definitions.net",
      "snippet": "How to say stealth in Esperanto? What's the Esperanto translation of stealth? See comprehensive translation options on Definitions.net!",
      "engine": "ddg_lite",
      "position": 1
    },
    {
      "url": "https://www.indifferentlanguages.com/words/stealth/esperanto",
      "title": "How to Say Stealth in Esperanto - Indifferent Languages",
      "snippet": "stealth in Esperanto. Learn how to say it and discover more Esperanto translations on indifferentlanguages.com.",
      "engine": "ddg_lite",
      "position": 2
    },
    {
      "url": "https://www.i
… 
```

### extract

```json
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}},"name":"extract","arguments":{"url":"https://example.com","format":"text","main_content_only":true,"timeout_ms":30000}}}
```

Response (the full result text of a live call; the `content` field carries the page text):

```json
{
  "title": "Example Domain",
  "content": "This domain is for use in documentation examples without needing permission. This is not a service; avoid relying on it for testing and monitoring purposes.هذا النطاق مُخصص للاستخدام في أمثلة التوثيق دون الحاجة إلى إذن. هذه ليست خدمة، يُرجى تجنب الاعتماد عليها لأغراض الاختبار والمراقبة.该域名仅用于文档示例，无需获得许可。这并非一项服务，请勿将其用于测试和监控目的。L’usage de ce domaine est réservé à des exemples de documentation, sans autorisation préalable. Il ne s’agit pas d’un service ; son utilisation à des fins de test ou de surveillance est à éviter.Данный домен предназначен для использования в примерах документации без необходимости получения предварительного разрешения. Это не сервис; не рекомендуется его использование для тестирования и мониторинга.Este dominio está destinado al uso en ejemplos de documentación sin necesidad de permiso. Esto no es un servicio; evitar utilizarlo para realizar pruebas o monitoreos.Learn more",
  "url": "https://example.com",
  "content_type": "text"
}

…
```

### usage

Response shape (live values from a tenant key):

```json
{
  "used_this_month": 647,
  "monthly_limit": 200500,
  "concurrent_sessions": 1,
  "concurrent_limit": 10,
  "rate_limit_rpm": 120,
  "period_start": 1789962389,
  "period_end": 1792554389
}
```

## Errors

| Shape | HTTP | Meaning |
|---|---|---|
| `{"error":"rate_limit_exceeded","reason":"Rate limit exceeded at gateway"}` | 429 | Per-key requests/minute exceeded. `Retry-After: 5`. |
| `{"error":"concurrent_limit_exceeded","reason":"Too many in-flight requests for this key's plan","scope":"per_http_request"}` | 429 | The plan's concurrent-in-flight ceiling. `Retry-After: 5`. |
| `{"error":"rest_not_available","reason":"REST API is not available to clients. Use the MCP endpoint (/mcp)."}` | 403 | Tenant keys get MCP only. |
| `Invalid or missing API key.` / `API key has been revoked.` | 401 | Header missing, mangled (`Bearer ` inside `x-api-key`), whitespace, or revoked/rotated key. |
| `{"error":"quota: …(used=…, limit=…)…"}` | 429 | Balance exhausted — top up. |
| `worker at tool capacity (5 in-flight) — retry` | 200 (isError) | Shared per-worker burst ceiling; retry with backoff. Capacity errors are never billed. |
| `-32602` Invalid params: missing `_meta` keys | 400 | Include the two `io.modelcontextprotocol/*` `_meta` keys. |
| `-32020` Missing/mismatched `Mcp-Method`/`Mcp-Name` header | 400 | Header must exactly equal the body's method / `params.name`. |
| `-32022` Unsupported protocol version | 400 | Use `2026-07-28`. |

Failed calls (4xx/5xx) are never billed.

## Data handling

- The account is a random 16-digit number. No email, name, or password exists; nothing else
  is collected.
- No record of which sites your agents visit: a call counter ticks down; that is the only
  trace a call leaves.
- Browser state lives in isolated RAM and is destroyed after your session — never on disk,
  never shared.
- Everything runs in the EU (Frankfurt, Germany).
- Support: support@sekreto.ai · Terms: https://sekreto.ai/terms.html
