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

# Realtime, errors, and quotas

> Subscribe safely, recover from disconnects, and handle Tayho API errors and quota responses.

# Realtime, errors, and quotas

Realtime subscriptions reduce display latency. Indexed API reads and receipt status endpoints remain
the durable way to establish state after startup, reconnect, or uncertainty.

## Subscribe

```ts theme={null}
const subscription = actionClient.realtime.subscribe({
  channels: [`account:${account}`, "market:1"],
  onEvent: (event) => console.info(event.eventId, event.data),
  onSnapshot: (snapshot) => console.info("snapshot", snapshot),
  onError: (error) => console.error(error),
});

// Later
subscription.close();
```

The SDK obtains a short-lived single-use ticket over authenticated HTTPS and puts only that ticket
in the WebSocket URL. It never puts the API key in the URL.

After an unexpected close, the client reconnects with exponential backoff from 500 milliseconds to
15 seconds. It obtains a new ticket, resumes each channel from its latest sequence, and removes
duplicate event IDs in a rolling 2,048-event window. Snapshots establish a new channel cursor.
Applications should make event handlers idempotent and refresh relevant API state if the server
cannot satisfy a resume.

Calling `close()` cancels a pending reconnect and closes the socket. At least one channel is
required.

## Error model

API and RPC failures use `TayhoError`:

```ts theme={null}
try {
  await actionClient.markets.list();
} catch (error) {
  if (error instanceof TayhoError) {
    console.error(error.code, error.status, error.requestId);
  }
}
```

| Code                               | Meaning                                                             | Caller response                                                            |
| ---------------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `invalid_configuration`            | Client options or deployment configuration are incompatible         | Correct the environment, URL, signer, or reader; do not retry unchanged    |
| `authentication_failed`            | Key is missing, invalid, expired, revoked, or suspended             | Replace or reactivate the key                                              |
| `forbidden`                        | Key lacks the required scope or violates an origin/CIDR restriction | Correct key policy; do not retry unchanged                                 |
| `not_found`                        | Resource is absent or the supplied receipt capability is invalid    | Verify the identifier and capability                                       |
| `rate_limited` or `quota_exceeded` | A project, key, IP, wallet, or action quota rejected the request    | Wait for `retryAfterSeconds` when present                                  |
| `invalid_request`                  | Input failed public validation                                      | Correct the request; do not retry unchanged                                |
| `dependency_unavailable`           | API or an upstream dependency could not serve the request           | Retry with backoff when the operation is safe                              |
| `rpc_chain_mismatch`               | A configured reader returned a different chain                      | Correct the RPC configuration immediately                                  |
| `rpc_unavailable`                  | Configured RPC reads failed                                         | Retry/fail over, or remove the reader to use bounded API preparation reads |

The server can return additional stable lowercase codes for route-specific failures. Record
`requestId` in diagnostics. Respect `retryAfterSeconds` instead of selecting a shorter delay.

## Safe retries

* Reads are safe to retry with bounded exponential backoff.
* For an action or order submission, call `submit()` again on the **same prepared object**. It reuses
  the same signed envelope and idempotency key.
* Do not call the prepare method again just to retry a transient submission failure; that creates a
  new nonce and idempotency key.
* A `duplicate` intent receipt is an accepted replay of the same submission.
* After an ambiguous timeout, query the returned receipt/status capability when available before
  creating a new action.
* Fix configuration, authentication, scope, chain mismatch, and validation failures instead of
  retrying them.

## Quota classes

Keys can grant `read`, `realtime`, and `actions:submit` scopes. Projects enforce separate budgets for
indexed reads, live reads, realtime connection creation, and submissions. They can also cap pending
actions, per-transaction sponsored gas, and daily sponsored gas. IP and wallet protections may be
stricter than the project limit.

Exact allocations belong to the issued project and key; the SDK does not assume universal numeric
limits. On HTTP `429`, inspect `retryAfterSeconds` and the response's request ID. Realtime clients
should let the built-in reconnect backoff obtain a fresh ticket rather than creating parallel
subscriptions.
