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

# Limits and timeouts

> Every plan quota, rate limit, ceiling and deadline in one place, and whether waiting clears it.

Find which limit you hit, and whether waiting clears it.

## Plan limits

| | Free | Plus | Enterprise |
| - | - | - | - |
| Payments per rolling 30 days | 5 | 100 | Unlimited |
| Agent cards | 1 | 5 | Unlimited |
| Credentials | Unlimited | Unlimited | Unlimited |
| Daily spending limits | No | Yes | Yes |
| Merchant allowlists | No | No | Yes |
| Webhook notifications | No | Yes | Yes |
| Token catalog requests per key, per minute | 60 | 300 | 600 |
| Token catalog requests per account, per minute | 120 | 600 | 1,200 |

| Quota | Window | Clears by waiting? |
| - | - | - |
| Payments | Rolling 30 days | **Yes**, as the oldest payment ages out |
| Agent cards | A standing count of agent cards linked to a funding card you still have. One showing **No funding card** does not count | **No.** The plan's slots go to the oldest of those first, so a newer card beyond the limit stops working |

* Free and Plus have a payment limit. Enterprise has none.
* Upgrading raises the limit but does not reset the count, which carries across
  plan changes.
* A refusal for a plan quota is `plan_quota_reached`. It is an account setting,
  not a card setting.
* Plan quotas apply on production deployments only. Preview, development, test
  and staging deployments are not metered.

See [Billing](/dashboard/billing).

## Spending limits

Set per agent card, in US dollars. Every window is **rolling**, not a calendar
period. A foreign total is converted at a daily reference rate, plus a 3% safety
buffer, before it is checked. A currency with no rate, or a rate older than 48
hours, counts as an unknown total.

| Limit | Available on | Starter card default |
| - | - | - |
| Per transaction | All plans | \$75 |
| Per day | Plus and Enterprise | \$100 |
| Per week | All plans | \$200 |
| Per rolling 30 days | All plans | \$500 |

On Free, the starter agent card carries every default except the daily limit.

The daily, weekly and rolling 30-day limits apply on production deployments
only, like plan quotas. Preview, development, test and staging deployments are
not metered: they ignore those rolling caps and enforce only the per-transaction
cap.

<Warning>
  A card fill **counts against the limits the moment it lands**, before any order
  exists. If the order is never placed, that amount still counts until the window
  rolls past it.
</Warning>

A breach always refuses that fill with `limit_reached`. What happens next depends
on **Auto-pause on limit exceeded** (`autoPauseOnLimit`), which is on for every
agent card created in the dashboard:

* **On:** any breach, including a per-transaction one, changes the card's status
  to `limit`, and it stops filling until you resume it.
* **Off:** the card stays `active`. A smaller cart, or the same purchase once the
  window rolls past older fills, goes straight through.

## Sessions

| Limit | Value | What happens |
| - | - | - |
| Idle timeout | 15 minutes with no message and no call in flight | Closed with WebSocket code 4008, `idle_timeout`. A call the proxy is still working on counts as activity, so it is not cut off by this timer |
| Hard timeout | 60 minutes in total | Closed with WebSocket code 4009, `hard_timeout`, wherever the session is |
| Proxy per-call deadline | 120 seconds, or the call's own `timeout` plus 30 seconds, up to an hour | `unanswered`. **Not** terminal: the session survives |
| SDK per-call deadline | `SELF_AGENT_PAY_REQUEST_TIMEOUT_MS`, default 180000. A fill, a handoff or a `timeout: 0` call gets 65 minutes, and a call with its own `timeout` gets that plus 60 seconds | `request_timeout`. **Terminal**: the session closes |
| Dropped connection | The proxy holds the session 60 seconds for the client to come back | Past that, the session closes. The SDK redials for about 30 seconds, or up to 150 while the proxy is still letting go of the old connection |

Tokens never expire. Only sessions do.

### Concurrent sessions

| | Free | Plus | Enterprise |
| - | - | - | - |
| Live proxy sessions per account | 2 | 5 | 10 |

* Past the limit, `chromium.launch()` is refused with `session_limit`. The
  connect is refused, not queued, and nothing is opened or spent.
* Every session holds a slot until it closes. `browser.close()` returns once the
  proxy acknowledges the close, before the slot is released, so a launch right
  after it can still get `session_limit` at the plan limit. Speaking the
  protocol directly, close the WebSocket: a `browser.close` frame alone leaves
  the session open.
* A slot whose release failed, or one held by a proxy that crashed, frees itself
  within 3 minutes, when its lease runs out without a renewal.
* Close a session you no longer need, or wait for one to finish, then launch
  again with backoff. Retrying in a tight loop only spends the connect rate
  limit below.
* The limit is per account, so another API key does not raise it.

## Hosted MCP server

These apply only to the [hosted MCP server](/mcp/setup#connect-to-the-hosted-server).
The agent gets each refusal as a tool error carrying the message.

| Limit | Value | What happens |
| - | - | - |
| Idle session | 13 minutes without a successful call to its browser, checked once a minute. Catalog, login and recipe calls, and refused calls, do not count. For a session with no browser yet, every call counts | The session and its browser are closed. A later call naming it is told it ended because it was idle too long. Call `open_page` to start again |
| Session browser age | 56 minutes after its browser opened, checked once a minute. A session with a call in flight is skipped until the call ends | Closed a few minutes before the proxy's 60-minute hard timeout, so its report can still be read. A later call is told why. A call still running at minute 60 is cut off by the proxy, for example a `request_challenge_handoff` (up to 12 minutes) started after minute 48. That call fails with `hard_timeout`, "Proxy session closed: it reached the 60-minute session limit.", and it may already have acted, so check before repeating it |
| Session credential | Each session's credential lasts 75 minutes. The session is closed a minute before it expires, even with a call still in flight, such as a fill waiting on a live security code. Its browser closes once that call finishes, and counts toward the hosted session limit until then, or until the proxy's 60-minute limit ends it. A later call on the session waits up to 30 seconds for the close, then is told its report cannot be read yet | A later call is told the credential was about to lapse. Start a new session. A call started shortly before that point can outlast the credential, for example a 12-minute `request_challenge_handoff`. The call keeps running, but nothing in it can sign in with the credential again after it expires, so a dropped connection to the proxy cannot be resumed and the call fails |
| Tool call | 4 minutes. `request_challenge_handoff` gets 12 minutes | "did not finish within 4 minutes. Its outcome is unknown: check before repeating it." The call may still have acted |
| Request body | 1 MiB | HTTP 413, JSON-RPC error `-32600` "Request too large" |
| Request body arrival | 30 seconds | HTTP 408, JSON-RPC error `-32600` "Request body too slow" |
| Repeated call | 2 minutes | A call that changes something (`open_page`, `click`, `press`, the fills, `declare_page_hint`, the login writes, `submit_checkout_recipe` and others), sent again with the same arguments, session and JSON-RPC id, gets the first answer back and does not run again. A new id runs it again. See [Repeated and retried calls](/mcp/tool-reference#repeated-and-retried-calls) |
| Kept answers for repeated calls | The latest 200 per connection. A call that started a session counts twice | More than 200 such calls within 2 minutes on one connection drops the oldest answers early. Sending one of those again runs the action again, so reconcile with the page or `get_run_report` before you resend a call that changes something |
| Ended session report | Kept 1 hour, the latest 100 per connection, and only while the same gateway process runs | A call naming a session that ended is told why it closed and what its close found. After that, only that it ended |
| Gateway restart | Sessions, ended session reports and kept answers live only in the running gateway's memory | A restart ends every hosted session. A later call naming one gets only the generic ended message, with no report, and a resent call runs again. Check [**Activity**](/dashboard/activity), **Logins** and the merchant's order state before you repeat anything. A call that would start a session during the restart is told "The gateway is restarting. Retry in a moment." |
| Credential request | 10 seconds for Agent Vault to issue a new session's credential | `unavailable`: "Agent Vault could not be reached. Retry in a moment." Nothing ran. Retry the tool, with a pause between tries: Agent Vault may still have issued the credential after the wait ran out, and it counts toward the connection's [50 live credentials](#ceilings) until it expires. See [Tool errors](/sdk/error-codes#tool-errors) |
| Account at its session limit | The plan's live proxy sessions | When `open_page` hits `session_limit`, the gateway first closes a browser session of the same user, on any of their connections, that no call has named in the 2 minutes since its answer was handed out (one whose `open_page` answer reached no one). An answer still waiting for `get_pending_result` keeps its session. Then it closes this connection's least recently used idle session, and tries again. A session with a call in flight is never closed. Calls on the closed session are told why it was closed |
| Requests without a valid token | 30 a minute from one address, where the deployment turns this limit on. Requests with a valid token are not counted | HTTP 429 `rate_limited` with `Retry-After`, before any tool runs. See [Sign-in responses](/sdk/error-codes#sign-in-responses) |
| Live credentials | 50 per connection | `too_many_credentials`. See [Ceilings](#ceilings) |
| Connections | 200 per account | `too_many_connections`. See [Ceilings](#ceilings) |

## Waiting for the account holder

| Prompt | How long the agent waits |
| - | - |
| Purchase approval | 5 minutes |
| New site approval (a fill on a site that is not a [destination](/dashboard/destinations)) | 15 minutes, or less if the session's 60 minutes run out first |
| Live security code entry | About 3 minutes, or less while the proxy is shutting down. The account holder's link expires with the wait |
| Challenge handoff | About 10 minutes |
| Signup request | 24 hours. The agent holds a request id, not a paused browser |

## Rate limits

| What | Limit | Scope | `Retry-After` |
| - | - | - | - |
| `GET /v1/tokens` | 60, 300 or 600 per minute by plan, in a fixed window | Per key | **Yes**, whole seconds, at least 1 |
| `GET /v1/tokens` | 120, 600 or 1,200 per minute by plan | Per account | **Yes** |
| New proxy session (`chromium.launch()`) | 10, 30 or 60 per minute by plan | Per key | **Yes**, as `retryAfterSeconds` |
| New proxy session (`chromium.launch()`) | 20, 60 or 120 per minute by plan | Per account | **Yes**, as `retryAfterSeconds` |
| Token resolution (every identity, address, login, card and security code fill) | 120, 600 or 1,200 per minute by plan | Per key | **Yes**, as `retryAfterSeconds` |
| Token resolution | 240, 1,200 or 2,400 per minute by plan | Per account | **Yes**, as `retryAfterSeconds` |
| `POST /v1/recipes` | 20 per rolling 24 hours, by default | Per account | **No** |
| Login writes (the three `POST /v1/logins/...` endpoints). Polling a signup request is not counted | 20 per minute | Per key | **Yes** |
| Agent credential changes: setting a username, rotating a password, and the account holder approving a signup request | 25 in a 24-hour window, by default. The window starts at the first counted change and resets to zero 24 hours later, not at midnight | Per account | **No** |
| Pending signup requests | 5 awaiting a decision | Per account | **No** |

<Warning>
  The per-minute limits and the recipe budget all answer `rate_limited`. Tell them
  apart by **which call threw**:
  `chromium.launch()` or a fill (the proxy), `listTokens()` (the catalog),
  `submitRecipe()` (recipes) or a login write. Do not go by a missing
  `retryAfterSeconds`. A proxy can strip `Retry-After` from a catalog 429, and
  treating that as the daily recipe limit would stop token lookups for a day over a
  pause of seconds.
</Warning>

Filing a signup request with `POST /v1/logins/requests` counts toward the
per-minute login write limit, but not toward the 24-hour credential-change budget.
An ordinary `pending` request draws on that budget only when the account holder
approves it. If the budget is spent then, the request stays `pending` and the
agent never sees `daily_limit`. A pre-approved signup that completes at once
draws on its own allowance instead, listed under [Ceilings](#ceilings).

The catalog, connect and token resolution windows are fixed one-minute windows,
not sliding ones. A refused request records nothing, so retrying early is refused
again without pushing the window out. The per-account budgets mean another API
key does not raise the limit. A refused fill leaves the session open, so retry it
on the same page.

## Ceilings

| Ceiling | Value | Scope | How it clears |
| - | - | - | - |
| Logins an agent may add to | 500 logins in total, counting every login on the account, including ones the account holder added | Per account | Only by the account holder deleting logins. Logins they add themselves are not capped, so they can pass 500, but then no agent signup can complete. A new signup request is refused at once with `login_limit` (403) and none is created |
| Logins named after one site name | 50 labels per name: `example`, then `example2` up to `example50` | Per account | Only by the account holder deleting a login that holds one of those labels. See [Labels per site name](#labels-per-site-name) |
| **Other sign-in sites** on one login | 10, counted after each line is reduced to its host. Blank lines and repeats do not count | Per login | Removing entries. A save with more is refused with `login_extra_sites_limit`, the form shows **A login can have at most 10 additional sign-in sites.**, and nothing is saved. See [Logins](/dashboard/logins) |
| Logins a scoped key may add to its own list | 50 entries, counting the ones the account holder ticked (a list of more than 200 entries counts as full even if some are logins that no longer exist). The agent's signup request is still filed and answers `pending`: the check runs when the account holder approves, the approval fails with `scope_full`, which only the account holder sees, and the agent keeps polling `pending` | Per key | The account holder unticking logins or switching the key to **All logins**. Their own selection is not capped |
| Signup request lifetime | 24 hours | Per request | The request expires |
| Hosted MCP connections | 200 in total, counting revoked and lapsed ones. The first call after the next new sign-in is refused with `too_many_connections`. Connections you already have keep working | Per account | Not by revoking: a revoked connection still counts. Contact [team@self.xyz](mailto:team@self.xyz) to clear old ones. See [Connected apps](/dashboard/api-keys#connected-apps) |
| Live credentials per hosted MCP connection | 50 unexpired at once. Every open session holds one until it closes, including one started by `get_token_catalog` or a login tool. Recipe submission tools hold one only while they run. Another is refused with `too_many_credentials` (429), and the message gives the seconds until the earliest one expires | Per connection | Closing a session normally gives its credential back at once, but that is best effort: if Agent Vault cannot be reached at that moment, the credential stays counted until it expires. A credential can also be left with no session: when Agent Vault issues one after the server's 10-second wait has run out, the call fails with `unavailable` and that credential still counts until it expires. No session owns it, so `close_session` cannot give it back, and retrying `unavailable` in a tight loop can fill the 50. Back off between retries. If closing the sessions you know about does not clear the refusal, wait the seconds the message gives, at most 75 minutes. See [Hosted MCP server](#hosted-mcp-server) |
| Pre-approved signups that complete at once, per listed site | 2 | Per site on the [**Pre-approved signups**](/dashboard/logins#pre-approved-signups) list | Only by the account holder removing the site and adding it again, or saving it with a different email |
| Pre-approved signups that complete at once, per account | 60 in a 24-hour window, by default | Per account | Waiting. The window starts with the first pre-approved signup and the count resets to zero 24 hours later |
| Cards and addresses read to pick the [residential egress country](/sdk/tunnel#residential-egress-country) | 20 cards the key may use, and 20 saved addresses | Per key (cards) and per account (addresses) | Not refused. Past 20 cards, the cards are skipped and your addresses decide. Past 20 addresses too, the session leaves from the deployment's fallback country |
| Sites counted in one `untunneled_egress` event | 100 distinct sites per proxy session that the session reached directly over plain `http`, and a separate 100 for merchants an [attested checkout](/security/attested-detokenization) reached. The event keeps the names of the first five sites in each group and the count. The alert text names the first three and counts the rest. When the tunnel was unavailable, the whole session went out another way and the event names no plain `http` sites | Per proxy session | Not refused, and no traffic is blocked. Past 100, a new site is neither named nor counted. For plain `http` sites the count stops at 100 with nothing to say more were left out, so read a count of 100 as a lower bound. For attested checkout merchants the alert says "at least" once it stops counting. See [When traffic did not use the tunnel](/sdk/tunnel#when-traffic-did-not-use-the-tunnel) |
| Devices set up for **Background alerts** | 20. Turning it on in a 21st browser still succeeds, and silently removes the device set up longest ago, which stops getting alerts | Per account | Nothing to clear: the oldest device makes room. See [Set up this device](/dashboard/approvals-and-notifications#set-up-this-device) |

Past either pre-approved ceiling, `POST /v1/logins/requests` is not refused. It
answers `pending` instead of `approved`, and the request waits for the account
holder like any other.

Two more ceilings apply when the account holder adds sites to the
[**Pre-approved signups**](/dashboard/logins#setup-ceilings) list. Each one
refuses the whole add with `signup_preapproval_too_many`, and nothing from that
add is saved.

| Ceiling | Value | Scope | How it clears |
| - | - | - | - |
| Sites in one add | 100, counted by line | Per add | Splitting the list into several adds |
| Sites on the pre-approved list | 300 | Per account | Removing sites from the list. A site already on the list does not count again when it is entered again |

### Labels per site name

Every login gets a label made from its site's name, such as `example` for
`example.com`. A label holds at most 32 lowercase letters and digits, so a
longer site name is cut short. When that label is taken, Agent Vault tries `example2`,
`example3` and so on, up to 50 candidates. Sites with the same name share them,
so `example.com` and `example.co.uk` draw on the same 50.

When all 50 are taken, a new login for that name is refused, even below the
500-login ceiling:

* The account holder's **Add login** form does not save the login, and shows
  only **That login could not be saved. Check the fields.**
* An agent's signup request cannot be approved. It stays `pending`, and the
  request on **Logins** says **Could not find a free name for this login**. Its
  failure reason is `label_unavailable`. The agent never sees this code: its
  request simply stays `pending`. A pre-approved signup falls back to `pending`
  for the same reason.

To recover, the account holder deletes a login that holds one of those labels,
then adds the login or approves the request again. Editing a login does not
help: a label is never renamed, and changing a login's website does not change
its label.

## Payload sizes

| Payload | Limit | When exceeded |
| - | - | - |
| Recipe submission body | 64 KB | `413` with `invalid_recipe`. Counted as it streams |
| Signup and login write body (the three `POST /v1/logins/...` endpoints) | 4 KB | `413` with `bad_request` |
| Recipe submission note | 2,000 characters | Truncated |
| Session receipt image | 1,000 pixels wide, and 700,000 bytes once encoded | Downscaled first. A receipt still too large is dropped |
| Purchase approval screenshot | 768 KB once encoded | Dropped. The approval is still created, without it |
| `recordOutcome` `orderTotal` | 24 characters, with a currency sign, a currency code or two decimal places. A whole total of exactly three or four digits is dropped even with a marker (write `"$737.00"`, not `"$737"`) | Silently left out |

## Field lengths

| Field | Limit | When exceeded |
| - | - | - |
| API key name | 200 characters, counted after spaces at either end are removed | Refused with `api_key_label_too_long`. The dashboard shows only **Failed to create key**. Shorten the name |
| Saved login username and password | At least 4 characters each (the username counted after trimming) | Refused with `login_username_too_short` or `login_password_too_short`, and the form says which |
| Email addresses (agent signups, pre-approved signups, a login's email) | 4 to 254 characters, one `@`, no whitespace | `invalid_email` for an agent. The dashboard refuses to save |
| Address and identity labels | 1 to 16 lowercase letters or digits, and not a reserved word such as `card`, `city` or `email` | `address_label_shape` or `address_label_reserved`. The **Addresses** form shows only **That address could not be saved. Check the fields and try again.** |
| Phone number | At least 4 digits after the country code, at most 15 digits counting it | `phone_shape`, with a message naming the rule |
| Security code entry | 3 or 4 digits | The page asks for the 3- or 4-digit code and sends nothing |
| Spending limits | More than \$0. No upper bound | `agent_card_cap_not_enforceable`. Leave the field blank for no limit |
| Imported two-factor link | 6 to 10 digits, a period of at least 1 second, SHA1, SHA256 or SHA512 | The login is not saved |
| Feedback message | 2,000 characters, and 10 messages in any 24 hours | The form stops at 2,000. Past 10, `feedback_rate_limited`: **That is a lot of feedback for one day. Please try again tomorrow.** Despite the wording, a place frees 24 hours after each message was sent, not at midnight |
| Account sign-up | Password at least 8 characters. Email and password at most 512 characters each | The form says **Passwords need at least 8 characters.** or **That email or password is too long.** |

## Tool limits

| Tool | Limit |
| - | - |
| `snapshot_page` depth | Default 12, capped at 30 |
| `snapshot_page` result | 24,000 characters. The truncation note names the cursor and depth to continue with |
| `get_session_outcome` timeout | Default 15000 ms, maximum 30000 ms. `0` or less means the default |
| `wait_for_popup` timeout | Default 10000 ms. Values over 60000 ms are reduced to 60000, not rejected |
| `wait_for_selector`, `waitForSelector`, `waitFor` timeout | Default 30000 ms |
| `goto` timeout | Default 45000 ms, waiting for `domcontentloaded` |
| `screenshot` timeout | Default 20000 ms |
| `fill_login` field wait | Each field gets 3 seconds to appear before the call refuses with "Nothing was filled" |
| `get_run_report` and the report `close_session` reads | The proxy gets 5 seconds to answer, or the read refuses and says so |
| Recipe registry calls (`get_checkout_recipes`, `submit_checkout_recipe`, `get_recipe_submission`) | 20 seconds per call, then "The recipe registry did not answer in time." |
| `observe_page` `errorText` | At most 5 strings, each cut to 200 characters |
| `counts()` visibility | Above 50 matches, `visible` is `undefined` |
| `get_network_log`, `networkLog()` | The page's last 25 requests, each path cut to 200 characters |
| Error messages passed through from the browser | Cut to 2,048 characters, ending with a note of how much was cut |
| Selectors after a token fill | 2,048 characters. A longer `selector` or `frame` is refused with `sensitive_data_blocked` |
| `submit_checkout_recipe` wait for a verdict | About 45 seconds, then poll |
| Page hint `selector` and `frame` | 512 characters each, after surrounding spaces are trimmed. A longer one is refused with `invalid_hint` and nothing is recorded |
| Page hint matches | At most 8 elements for an `order.total` hint, and each element's text or attribute at most 4,000 characters. Past either, the hint adds nothing: `accepted: false`. For `login.totp_secret`, `hint_unresolved` |
| Framed `order.total` hint | The frame's whole text is read with the hint. Over 20,000 characters, the hint is dropped: `accepted: false`, even though the selector matched. Point at a total outside the frame, or the total stays unknown |
| Other frames beside a framed hint | The vault also reads every other frame on the page. More than 32 frames, one that does not answer, one over 20,000 characters, or more than 24,000 characters across them, and the total is unknown: `accepted: false` |
| Checkout text the vault parses | 16,384 characters per look: the page's own text plus every hint's text, a framed hint's whole frame included. A hint whose element read nothing and that would pass it is left out, and the page is read without it. A hint whose element matched and that would pass it makes the total unknown. Either way the reply is `accepted: false`. A page over it on its own is not parsed at all: a capped card is refused with `checkout_total_unknown`, whose message says reading the page failed, and retrying does not help |
| Page hints per page | 4 of each kind. A fifth distinct hint of the same kind on that page is refused with `invalid_hint`. Declaring the same hint again does not count, a dropped hint (`accepted: false`) frees its place, and any navigation of the page, a route change included, clears the count |

## Generated passwords

Applies to the passwords the vault generates for agent signups and password
rotations (`createLogin`, `rotateLoginPassword`, the `create_login` and
`rotate_login_password` tools, and `POST /v1/logins/requests` and
`POST /v1/logins/password`).

| Rule | Bound | When exceeded |
| - | - | - |
| Length | 12 to 64 characters, 24 by default | Fixed by the vault |
| `minLength` | At most 64, and no more than `maxLength` | `invalid_policy`. A site that needs more than 64 characters cannot get a generated password: the account holder adds that login themselves |
| `maxLength` | At least 12 | `password_too_short`. No rule clears it: sign in with an existing login, or ask the account holder to add it |
| `minLength` and `maxLength` values | Whole numbers | `invalid_policy` |
| `symbols` | Filtered to `!@#$*+-_=.?`. Other characters are ignored | `invalid_policy` when none of them is in that set. Pass `noSymbols` for a site that takes no punctuation |
| Character classes | Always at least one lowercase letter, one uppercase letter and one digit, plus one symbol unless `noSymbols` is set | Cannot be turned off |

## Automatic key revocation

| Trigger | Result |
| - | - |
| 3 cross-account token refusals (`cross_user_denied`) within 5 minutes | The API key is revoked. The agent receives `key_auto_revoked` |

Each refusal is counted once, at the proxy.

## Data retention

| Data | Kept |
| - | - |
| Sessions, session events and receipts | 30 days from session start, on every plan |
| Card fill history, the Activity feed, webhook delivery logs, and purchase approval, security code, verification and signup requests (answered or expired) | Until you delete the account. Nothing removes them sooner |
| Everything else | Until you delete it, or delete the account. See [Data and deletion](/dashboard/data-and-deletion#what-is-stored) for the exceptions |

Expired data is removed hourly.

## Attested checkout concurrency

Applies only to [attested detokenization](/security/attested-detokenization),
which is off by default.

One code, `attested_checkout_busy`, covers four ceilings on attested checkouts
running at once: by default 1 per session, 2 per key, 2 per account and 4 per
proxy instance. It is the one attested code that clears on its
own. Back off and retry rather than opening more sessions: the per-key and
per-account ceilings mean extra keys do not raise the limit.

## Related

* [Billing](/dashboard/billing)
* [Sessions and timeouts](/sdk/sessions-and-timeouts)
* [Troubleshooting](/reference/troubleshooting)


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