---
name: caprid
description: Build on Relay (Mux's Media over QUIC relay network), Switchboard (its control plane), and the Relay SDKs (@caprid/relay, caprid-relay). Use when writing code that mints Client tokens, calls the Switchboard API, or publishes or subscribes to tracks through a relay.
---

# Caprid: Relay and Switchboard

Look things up before writing code; don't guess API shapes.

- Docs index (every page, as Markdown): https://www.caprid.com/llms.txt
- Any page as Markdown: append `.md` to its URL without the trailing slash.
- Account API: https://www.caprid.com/docs/reference/http/account.md (OpenAPI: https://www.caprid.com/openapi/account.yaml)
- Relay plane API: https://www.caprid.com/docs/reference/http/relay.md (OpenAPI: https://www.caprid.com/openapi/relay.yaml)
- TypeScript SDK guide: https://www.caprid.com/docs/sdks/typescript/guide.md
- Python SDK guide: https://www.caprid.com/docs/sdks/python/guide.md
- Every public SDK name, both languages: https://www.caprid.com/docs/spec/vocabulary.md
- What an SDK must do: https://www.caprid.com/docs/spec/behavior.md

## Integration rules

These are the facts an agent most often gets wrong. Each links to where it's specified.

1. **Only a Client token goes to a browser or app.** API keys (`sb_...`) and Signing keys stay on
   the server. A leaked API key or Signing key lets anyone mint tokens. ([Credentials](https://www.caprid.com/docs/guides/credentials/))
2. **Mint tokens on the backend, per Client, short-lived.** Grant the least you can: a viewer gets
   `subscribe` (`get`) only, under its own room. There is no way to revoke a single Client token;
   deleting the Signing key revokes every token it signed. ([Client tokens](https://www.caprid.com/docs/guides/credentials/#client-tokens))
3. **Sign tokens yourself in production.** A Client token is an HS256 JWT with the Signing key's
   `kid` in its header and `root`, `put`, `get` and `exp` claims. `root` is the full path, starting
   with the Namespace. The API's `path` is relative to the Namespace; a token's `root` is not.
   ([Sign your own](https://www.caprid.com/docs/guides/credentials/#sign-your-own))
4. **A token's grants must fit the Signing key's scope**, or a relay refuses it when a Client uses it.
   Check one with `POST /v1/namespaces/{namespace}/tokens/verify`.
   ([`verifyClientToken`](https://www.caprid.com/docs/reference/http/account/operations/verifyclienttoken/))
5. **The token travels as `?jwt=` on the relay URL.** Browsers can log that URL on a failed handshake,
   so scrub `jwt` values from diagnostics and error reports.
   ([Browser token exposure](https://www.caprid.com/docs/sdks/typescript/guide/#browser-token-exposure))
6. **Root the endpoint at the token's root.** Use `roomEndpoint(root)` (`room_endpoint(root)` in
   Python), which roots the hosted Relay, `DEFAULT_ENDPOINT`. Name namespaces relative to it. ([Room-rooted endpoints](https://www.caprid.com/docs/spec/behavior/#room-rooted-endpoints))
7. **Retry request error 16 on a fresh namespace.** A subscription can be rejected with `unroutable`
   for about a round trip while the publisher's announce propagates; a publisher's `announced` says
   its announce reached the wire. ([Subscription lifecycle](https://www.caprid.com/docs/sdks/typescript/guide/#subscription-lifecycle))
8. **A write waits for demand.** `write` doesn't send until a subscriber wants the track, and a
   completed write means the transport accepted it, not that anyone received it.
   ([Publishing lifecycle](https://www.caprid.com/docs/sdks/typescript/guide/#publishing-lifecycle))
9. **Never replay a failed write automatically.** Delivery may have been partial; decide on fresh data
   or an explicit replay policy. ([Errors and recovery](https://www.caprid.com/docs/sdks/typescript/guide/#errors-and-recovery))
10. **Plain connections never reconnect.** For anything long-lived use `connectSupervisedPublisher` /
    `connectSupervisedSubscriber` (`connect_supervised_*` in Python), which also rotate before the
    credential expires. ([Supervised connections](https://www.caprid.com/docs/sdks/typescript/guide/#stay-connected-supervised-connections))
11. **Branch on `RelayError` codes, not messages.** Codes and phases are stable.
    ([Error identities](https://www.caprid.com/docs/spec/behavior/#error-identities-and-operation-phases))
12. **An API key can't manage Members or Invitations, or delete a Namespace.** Those need a person
    signed in to the dashboard. ([Members and Owners](https://www.caprid.com/docs/guides/organizations/#members-and-owners))
13. **Install matching versions.** The packages are `@caprid/relay` on npm (add `@caprid/relay-quic`
    for Node) and `caprid-relay` on PyPI. Each SDK's packages release together, so pin them to one
    version. Before 1.0, a minor version can break the API.
    ([TypeScript](https://www.caprid.com/docs/sdks/typescript/#install), [Python](https://www.caprid.com/docs/sdks/python/guide/#use-the-local-packages-in-your-application))
