Keys
The four key kinds, what each can do, and which ones may reach a browser.
Two kinds of key, each with a live and a test variant. The split that matters is not live versus test — it is integration versus embed, because only one of them may be seen by a visitor.
| Prefix | Safe in a browser | What it can do | Where it is stored | Blast radius |
|---|---|---|---|---|
embed_test_… | Yes | Public chat against a fixed test answer. Never reaches your documents, never counts against message quota. | Anywhere a browser can read it — HTML attribute, public env var, committed config. | None. A leaked test embed key returns the same canned answer to anyone who tries it. |
embed_live_… | Yes | The same calls a live integration key can make, from an allowlisted browser origin only. Cannot read leads, conversations, documents, or keys. | Browser-visible config: a data-embed-key attribute or a PUBLIC_-prefixed build variable. | Bounded by your origin allowlist and the project it belongs to. Someone who copies it cannot use it from their own domain. |
sk_test_… | No | The same reach as a live integration key, but chat returns the fixed test answer instead of touching your documents. | Server-side secret store or .env that is not bundled for the browser. | Writes real leads and tickets into the workspace even though chat is stubbed. Treat it like a live secret. |
sk_live_… | No | Server-to-server: chat and streaming chat, message feedback, creating a lead, opening a ticket, and escalating a conversation — plus Extract. Cannot read leads, conversations, or documents: those need a dashboard JWT. | Server-side secret store only. Never in a repository, never in a client bundle. | Every project in the workspace. A leaked live key can ask anything of your documents and read the answers, and can write leads and tickets. It cannot read back stored leads, conversations, or documents. |
What a key can reach
Keys authenticate a caller, not a person, so they reach only the endpoints that were designed to be called without a signed-in user. That is a short list, and it is worth knowing before you plan an integration around it:
POST /v1/chatPOST /v1/chat/streamPOST /v1/chat/feedbackPOST /v1/projects/{projectId}/leadsPOST /v1/projects/{projectId}/ticketsPOST /v1/projects/{projectId}/conversations/{conversationId}/escalate
Extract is in addition to these, on its own service. Everything else needs a dashboard session:
- Reading or exporting leads
- Reading conversations
- Listing, uploading, or deleting documents
- Managing projects, keys, members, or billing
Choosing
- Calling from a browser? Embed key. Always.
- Calling from your server? Integration key.
- Wiring things up? The
testvariant of either, which answers from a fixture and never touches your documents. - Need browser code to do something an embed key cannot? Put it behind your own endpoint — the BFF recipe.
Naming that carries the boundary
# Ships to the browser. Public by construction.
PUBLIC_OPRAG_EMBED_KEY=embed_live_...
# Server only. Never referenced from client code.
OPRAG_SECRET_KEY=sk_live_... Bundlers expose variables by prefix. If the browser key is the only one named PUBLIC_, the build tooling refuses to leak the other one for you.
Rotation and revocation
Keys are issued and revoked per project. Rotating issues a new key and retires the old one; revoking a key takes effect immediately and returns 401 on the next call. See the keys reference.
Ready to ship?
Get started free