# Agents & Automation Source: https://docs.prem.io/agents Build unattended, agentic, or automated systems on Prem API: which integration to use, required config, and the operational gotchas that matter at scale. This page is for anyone building a system that calls Prem API **without a human watching each call**: agent loops, batch jobs, evaluation harnesses, or any tool that fires many requests unattended. Unattended callers hit different failure modes than an interactive app. A truncated response, a burned rate-limit budget, or a duplicated retry may not be noticed until it has already happened many times. If you want an AI coding agent (Claude Code, Cursor, Windsurf) to help you **write** this integration, see [Use LLMs](/use-llms). It loads the Prem API docs into your coding agent's context. This page is about the system you build calling Prem API, not the coding agent that helps you build it. ## Pick your integration | | TypeScript SDK | Confidential Proxy | Router (Beta) | | ---------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | **Best for** | Node.js agents, single-process automation | Polyglot agent frameworks (LangChain, Python/Go agent loops), anything already using an OpenAI or Anthropic SDK | OpenAI-compatible tools that need the broader Router model catalog | | **Encryption** | In-process | A local HTTP process encrypts/decrypts for you | Not confidential | | **Code changes** | Use `@premai/api-sdk` instead of `openai` | Point `baseURL` at the proxy | Point a Chat Completions client at `https://router.prem.io/v1` | The TypeScript SDK and Confidential Proxy are the confidential paths. Prem Router uses a separate API key and does not provide enclave confidentiality. Use Router only when its broader model catalog is more important than confidential processing. See [Developer Experience](/developer-experience) for both, in detail. For an agent that runs continuously, run the Confidential Proxy as a **background daemon** rather than in the foreground: ```bash theme={"system"} confidential-proxy start --compat openai --kek "$CLIENT_KEK" confidential-proxy status ``` See [Confidential Proxy](/confidential-proxy#run-the-proxy-as-a-daemon) for the full daemon lifecycle (`start` / `stop` / `status`, PID and log files, graceful shutdown). ## Required configuration Both confidential integration paths need the same four values. Set them once in the environment where the agent runs, not for each call: | Variable | Used by | Description | | -------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | `PREM_API_KEY` | Both | Your API key. See [API Keys](/api-keys). | | `CLIENT_KEK` | Both | A 32-byte key encoded as 64 hexadecimal characters. You generate and keep it. See [Encryption](/encryption). | | `PROXY_URL` | Both | The Prem API Gateway endpoint. | | `ENCLAVE_URL` | Both | The enclave endpoint. | Get the current `PROXY_URL` / `ENCLAVE_URL` values from [`dashboard.prem.io/endpoints.json`](https://dashboard.prem.io/endpoints.json) at deploy time rather than hardcoding them. Router uses `PREM_ROUTER_API_KEY` instead. See [Router custom providers](/router/integrations) for harness-specific configuration. ## Gotchas that only show up at automation scale These are easy to miss in a one-off script and expensive to miss in a loop that runs thousands of times. Reasoning models generate a chain-of-thought before every answer unless you turn it off. Those tokens are billed, count against your rate limit, and share the same inference's token budget as the visible answer. Reasoning can consume a tight `max_completion_tokens` value and leave `message.content: null`. Default to `reasoning_effort: "none"` in automated pipelines unless you explicitly consume the reasoning trace. The encrypted `/rvenc/chat/completions` endpoint (used by the TypeScript SDK) always responds over `text/event-stream`, even when you don't set `stream: true` in your request. If a second call comes in on the same API key while one is still in flight, it can return `429` with: ```json theme={"system"} {"status": 429, "error": "You already have an active chat stream. Please wait for it to complete or try again in a few moments."} ``` This is separate from the per-tier concurrent-request limits in [Rate limits](/rate-limits). If your agent parallelizes work on the same key, serialize calls per key or use a distinct API key per concurrent worker. Some `429`s come with a structured body and a `Retry-After` header. Others, including throttling applied upstream of the gateway, return a generic body with no `Retry-After` header. Branch on the HTTP status code, not the body shape, and fall back to your own exponential backoff when the header is missing. See [Rate limits](/rate-limits#non-standard-429-responses). An agent that retries blindly after a timeout or a `5xx` can duplicate the underlying action (a file upload, a resource creation). Send an `Idempotency-Key` header so a retried request has the same effect as the original. See [Idempotency](/idempotency). A `null` or empty `message.content` is not necessarily a failed call. If `finish_reason` is `"length"`, reasoning, a long answer, or both consumed the token budget. Treat it as truncation and retry with a larger `max_completion_tokens` or a lower `reasoning_effort`, not as an empty result to discard. ## A request shaped for unattended use ```typescript theme={"system"} import createRvencClient from "@premai/api-sdk"; const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK, }); const response = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Summarize this log file." }], reasoning_effort: "none", // skip reasoning unless you consume it max_completion_tokens: 4096, // headroom for the answer }, { headers: { "Idempotency-Key": crypto.randomUUID() }, // safe to retry }); const choice = response.choices[0]; if (choice.finish_reason === "length") { // Truncated: raise max_completion_tokens or retry; do not treat as empty. } console.log(choice.message.content); ``` ## Before you deploy * [ ] `reasoning_effort` set deliberately (`none` by default, or a value you've budgeted `max_completion_tokens` for) * [ ] Retries use exponential backoff and don't assume `Retry-After` is present * [ ] Idempotency keys on any retried write * [ ] Concurrent calls on the same API key are serialized, or spread across multiple keys * [ ] Every response checks `finish_reason` before treating `content` as final * [ ] `support_id` is logged on every error for support escalation This checklist is agent-specific. For the full production checklist (key management, tiers, attestation), see [Production Checklist](/production-checklist). ## Related Limits by tier, retry code, and non-standard 429 bodies. Make retries safe with the Idempotency-Key header. Daemon mode, config, and connecting any language. Connect OpenCode to Prem API through the local proxy. Configure a Prem-backed OpenClaw provider and its tool boundary. Use an explicit Chat Completions transport through Prem. Connect Goose's built-in OpenAI provider to Prem. Review why Cursor's provider override is not a confidential local path. Review the current Responses API protocol mismatch. # API Keys Source: https://docs.prem.io/api-keys Learn how API keys work, how to manage them, and how to use scopes and IP restrictions. API keys are the primary method of authentication for our APIs. Each API key belongs to an organization and has permission scopes. You can also protect an API key with IP restrictions. You can generate and manage API keys in your Prem API Dashboard > Developers > API Keys section. ## API keys and organizations We issue API keys **at the organization level**, not to individual users. This means: * The API attributes each action of an API key to the **owning organization**. * Many team members can work together with separate keys in the same organization. * API keys stay valid independently of individual user accounts. ## Unlimited Keys per Organization Each organization can create **any number of API keys**. This gives flexibility across environments, services, or teams. Common usage patterns include: * One key per environment (e.g., `development`, `staging`, `production`) * One key per integration (e.g., billing automation, analytics) * Temporary keys for CI/CD or testing purposes ## IP Restrictions For better security, you can **restrict an API key to specific IP addresses or subnets**: * The restrictions **support IPv4**, **IPv6**, and **CIDR notation** * The API rejects requests from unauthorized IPs with a `403 Forbidden` error Example entries: * `192.168.1.10` * `2001:0db8::/32` * `203.0.0.0/8` Use IP allowlists to protect critical integrations (e.g., production). ## Scoped Permissions You can limit each API key to a specific set of **scopes**. The scopes define the parts of the API that the key can access. The dashboard asks you to set these scopes when you create an API key. If you do not specify scopes, the API key has full permissions. ## API Scopes Reference | Scope | Description | | ------------------------ | --------------------------------------------------------- | | `api_keys.read` | View and list API keys associated with your organization. | | `chats.completion` | Access chat completion functionality. | | `tools.execute` | Execute tools and integrations. | | `files.encrypted.read` | Read encrypted files. | | `files.encrypted.create` | Upload encrypted files. | | `files.encrypted.delete` | Delete encrypted files. | | `audio.transcription` | Transcribe audio files to text. | | `audio.translation` | Translate audio from any language to English. | This lets you apply the **principle of least privilege** and isolate permissions for each use case. A request from a key without the required scope returns `403 Forbidden`. ## Best Practices * **Rotate keys regularly** to decrease exposure * **Use least privilege**: assign only the required scopes * **Restrict by IP** where possible * **Do not share keys between environments** or teams For key management, go to your **Dashboard > API > API Keys**. If you need help, contact [support](mailto:support@premai.io) or refer to the [Authentication Guide](/authentication). # Get an API key Source: https://docs.prem.io/api-reference/api-keys/get-api-key get /developers/api_keys/{id} Get details of a specific API key by ID # Get API key analytics Source: https://docs.prem.io/api-reference/api-keys/get-api-key-analytics get /analytics/api_keys Get analytics data for API key usage # Get API key request Source: https://docs.prem.io/api-reference/api-keys/get-api-key-request get /developers/api_keys/requests/{id} Get details of a specific API key request/usage log entry by ID # List API key requests Source: https://docs.prem.io/api-reference/api-keys/list-api-key-requests post /developers/api_keys/requests Get a filtered list of API key requests/usage logs # List API key scopes Source: https://docs.prem.io/api-reference/api-keys/list-api-key-scopes get /developers/api_keys/scopes List all available scopes for API keys # List API keys Source: https://docs.prem.io/api-reference/api-keys/list-api-keys get /developers/api_keys List all API keys for the organization # Encrypted Audio Transcriptions (RVENC) Source: https://docs.prem.io/api-reference/audio-transcriptions post /rvenc/audio/transcriptions Simple pass-through to enclave audio transcription endpoint with encrypted payload. Rvenc = raw volatile encrypted. Accepts an encrypted audio file and inference parameters. Transcribes audio into the input language. Only provides authentication, security checks, and rate limiting. No file storage or custom features. ## TypeScript SDK The SDK is available on GitHub: [premAI-io/api-sdk-ts](https://github.com/premAI-io/api-sdk-ts) ### Basic Setup Create a 32-byte KEK. Encode it as 64 hexadecimal characters. Keep the KEK. Reuse it for later requests. ```bash theme={"system"} export CLIENT_KEK="$(openssl rand -hex 32)" ``` Create the client: ```typescript theme={"system"} import { createRvencClient } from "@premai/api-sdk"; import fs from "fs"; const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK }); ``` ### Pre-generate Keys You can pre-generate encryption keys and reuse them with the same KEK: ```typescript theme={"system"} import { createRvencClient, generateEncryptionKeys } from "@premai/api-sdk"; const encryptionKeys = await generateEncryptionKeys(); const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK, encryptionKeys, requestTimeoutMs: 60000, // optional maxBufferSize: 20 * 1024 * 1024, // optional }); ``` ### Basic Transcription ```typescript theme={"system"} const transcription = await client.audio.transcriptions.create({ file: fs.createReadStream('./audio.wav'), model: 'deepgram/general-nova-3', smart_format: true, }); console.log(transcription.results?.channels?.[0]?.alternatives?.[0]?.transcript ?? ''); ``` ### Transcription with Options ```typescript theme={"system"} const transcription = await client.audio.transcriptions.create({ file: fs.createReadStream('./audio.mp3'), model: 'deepgram/general-nova-3', diarize: true, smart_format: true, }); ``` ### Configuration | Option | Default | Description | | -------------- | -------- | --------------------------------------------------------- | | `file` | required | Audio file (ReadStream, Buffer, Blob, etc.) | | `model` | required | Current enabled model ID: `'deepgram/general-nova-3'` | | `diarize` | `false` | Ask the backend to identify speakers | | `smart_format` | `false` | Ask the backend to format dates, numbers, and punctuation | > **Note:** The current Deepgram route is non-streaming and returns Deepgram-style `metadata` and `results`, not an OpenAI `{ text }` object. ## OpenAI-Compatible API Server Run the SDK as a standalone server. The proxy requires the same 32-byte KEK described above and automatically manages one DEK store for each API key: ```bash theme={"system"} bunx -p @premai/api-sdk@1.0.59 confidential-proxy --kek "$CLIENT_KEK" # Server runs on http://127.0.0.1:8787 ``` Use the server with any OpenAI-compatible client: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/audio/transcriptions \ -H "Authorization: Bearer your-api-key" \ -F "file=@audio.mp3" \ -F "model=deepgram/general-nova-3" ``` You can also use the OpenAI SDK against the local proxy: ```typescript theme={"system"} import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.PREM_API_KEY, baseURL: "http://127.0.0.1:8787/v1", }); const transcription = await client.audio.transcriptions.create({ file: fs.createReadStream('./audio.mp3'), model: 'deepgram/general-nova-3', }); console.log(JSON.stringify(transcription, null, 2)); ``` The server establishes a secure client instance for each API key. ## API Reference # Encrypted Audio Translations (RVENC) Source: https://docs.prem.io/api-reference/audio-translations post /rvenc/audio/translations Simple pass-through to enclave audio translation endpoint with encrypted payload. Rvenc = raw volatile encrypted. Accepts an encrypted audio file and inference parameters. Translates audio into English text regardless of the input language. Only provides authentication, security checks, and rate limiting. No file storage or custom features. ## TypeScript SDK The SDK provides `client.audio.translations.create(...)`. The Confidential Proxy provides the `/v1/audio/translations` route. ### Set the KEK Create a 32-byte KEK. Encode it as 64 hexadecimal characters. Keep the KEK. Reuse it for later requests. ```bash theme={"system"} export CLIENT_KEK="$(openssl rand -hex 32)" ``` ### List the models Start the proxy: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --compat openai \ --kek "$CLIENT_KEK" ``` List the available models: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/models \ -H "Authorization: Bearer $PREM_API_KEY" ``` Use a model ID that supports audio translation. ## API Reference # Encrypted Chat Completions (RVENC) Source: https://docs.prem.io/api-reference/chat-completions post /rvenc/chat/completions Simple pass-through to enclave chat completion endpoint with encrypted payload. Rvenc = raw volatile encrypted. Accepts a single encrypted inference parameter that encapsulates all chat completion data. Only provides authentication, security checks, and token rate limiting. No chat history, file management, or custom features. ## TypeScript SDK The SDK is available on GitHub: [premAI-io/api-sdk-ts](https://github.com/premAI-io/api-sdk-ts) ### Basic Setup Create a 32-byte KEK. Encode it as 64 hexadecimal characters. Keep the KEK. Reuse it for later requests. ```bash theme={"system"} export CLIENT_KEK="$(openssl rand -hex 32)" ``` Create the client: ```typescript theme={"system"} import createRvencClient from "@premai/api-sdk"; const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK }); ``` ### Pre-generate Keys You can pre-generate encryption keys and reuse them: ```typescript theme={"system"} import createRvencClient, { generateEncryptionKeys } from "@premai/api-sdk"; const encryptionKeys = await generateEncryptionKeys(); const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK, encryptionKeys, requestTimeoutMs: 60000, // optional maxBufferSize: 20 * 1024 * 1024, // optional }); ``` ### Non-streaming Requests ```typescript theme={"system"} const response = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Hello!" }], }); ``` ### Streaming Requests ```typescript theme={"system"} const stream = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Hello!" }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } ``` ### Configuration | Option | Default | Description | | ------------------ | -------------- | -------------------------------------------- | | `apiKey` | required | Authorization token | | `encryptionKeys` | auto-generated | Pre-generated `{ sharedSecret, cipherText }` | | `requestTimeoutMs` | 30000 | Request timeout in ms | | `maxBufferSize` | 10MB | Max SSE buffer size | ## OpenAI-Compatible API Server Run the SDK as a standalone server. The server automatically manages one DEK store for each API key: ```bash theme={"system"} bunx -p @premai/api-sdk@1.0.59 confidential-proxy --kek "$CLIENT_KEK" # Server runs on http://127.0.0.1:8787 ``` Use the server with any OpenAI-compatible client: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/chat/completions \ -H "Authorization: Bearer your-api-key" \ -H "Content-Type: application/json" \ -d '{"model": "glm-5.2", "messages": [{"role": "user", "content": "Hello!"}], "stream": false}' ``` You can also use the SDK directly in Node.js: ```typescript theme={"system"} import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.PREM_API_KEY, baseURL: "http://127.0.0.1:8787/v1", }); const stream = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Count to 10" }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } ``` The server establishes a secure client instance for each API key. ## Reasoning models Reasoning tokens are billed and count against your token rate limits. See [Usage](/billing/usage) for billing, output-token interaction, and instructions to disable or limit reasoning. ```typescript theme={"system"} const response = await client.chat.completions.create({ model: "", messages: [{ role: "user", content: "Hello!" }], reasoning_effort: "none", // disable reasoning }); ``` ## API Reference # List available models Source: https://docs.prem.io/api-reference/list-models get /models Get a list of all available models # Attestation Source: https://docs.prem.io/attestation What the current Prem attestation client verifies, what remains policy work, and how the SDK uses the result. Attestation is hardware-signed evidence about a confidential-computing environment. A verifier combines that evidence with manufacturer collateral, freshness checks, and an explicit policy before deciding whether to trust a runtime. Evidence verification and policy verification are different. A valid manufacturer signature proves that evidence came from the relevant hardware trust chain. It does not, by itself, prove that the measured software matches an approved Prem release. ## Current implementation status This table reflects `@premai/reticle` `0.5.1` and the reviewed Reticle source at commit `6024585`. | Module | Implemented in the high-level client | Current limitation | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | AMD SEV-SNP | Generates a nonce, fetches the report and AMD certificate material, and verifies the report with the nonce | `attest_sev` still contains an explicit TODO for expected measurement comparison | | Intel TDX | Generates a nonce, fetches the quote and Intel collateral, and runs quote verification with the nonce | `attest_tdx` still contains an explicit TODO for expected measurement comparison | | NVIDIA GPU | Generates a nonce; verifies the overall and detached GPU JWTs; checks detached-token digests, nonce values, and the overall result; iterates the returned GPU claims | The public result does not expose per-GPU verdicts or prove that backend scheduling is bound to exactly that GPU set; the selected per-GPU check semantics also need code-owner review | The client discovers one CPU module (`SEV-SNP` or `TDX`) and an optional NVIDIA evidence bundle, then calls the corresponding high-level methods. The NVIDIA EAT token can contain multiple detached GPU claims even though module discovery reports only the NVIDIA vendor module. Prem documentation and deployments target AMD SEV-SNP, Intel TDX, and NVIDIA confidential-computing GPUs. The exact hardware model, firmware, TCB, and policy acceptance decision must come from verified evidence and an explicit policy, not from a model alias. ## What verified attestation policy means A complete policy decision needs four distinct inputs: 1. **Authenticity:** the report or token verifies against manufacturer collateral. 2. **Freshness:** the evidence contains the verifier's nonce. 3. **Configuration:** security-relevant claims meet required TCB, debug, and confidential-mode rules. 4. **Identity:** measured values match a release-specific approved reference set. Reticle's high-level methods perform evidence and freshness checks. The reviewed CPU paths do not yet complete step 4, so the client does not prove that the expected code is running or automatically match a published image fingerprint. ## Where Reticle runs Reticle is the verifier library, not a component that must run on the GPU cluster. ```mermaid theme={"system"} flowchart LR subgraph Client["Client device or local proxy"] SDK["Prem SDK / Confidential Proxy"] R["@premai/reticle verifier"] SDK --> R end subgraph Prem["Prem services"] G["Gateway and model routing"] A["Attestation endpoint in the confidential runtime"] M["Model backend"] end R -->|"request evidence with nonce and model"| G G --> A A -->|"hardware evidence and session header"| R SDK -->|"encrypted request with session header"| G G --> M ``` The attestation endpoint runs with the confidential runtime to obtain hardware evidence. Reticle runs in the SDK or proxy process and verifies that evidence. The Rust implementation is also published as a WASM package for supported JavaScript environments. ## How the SDK gates a request With the default `attest: true`, `@premai/api-sdk` performs this sequence: 1. Builds a Reticle client against `PROXY_URL` and authenticates with the Prem API key. 2. Adds the requested model to the attestation query. 3. Calls the high-level `client.attest()` method. 4. Reads an `x-session-id` from the returned attestation headers. 5. Sends the encrypted inference request with that session ID. The SDK caches a successful session ID for 30 seconds by API key and model and coalesces simultaneous attestation work. Transient attestation transport errors can be retried up to four attempts. A non-transient failure, or a missing session ID, throws before the protected inference request is sent. `attest: false` and the proxy flag `--no-attest` bypass this gate. Do not use them for a workflow that claims attested confidential inference. ## Routing and session pinning Routing has two separate meanings: * **Commercial/API routing:** the gateway authenticates the API key, applies limits and billing metadata, and selects the service path for the requested alias while handling encrypted content. * **Attestation-linked routing:** the attestation response supplies a session ID that the SDK attaches to the encrypted inference request so the platform can associate it with the attested backend path. The client source confirms model-scoped attestation and session-header forwarding. It does not prove every backend scheduling, load-balancing, or billing implementation detail. Billing is associated with the authenticated request; an attestation failure stops the inference request before completion, but final billing semantics must be confirmed from Prem usage records. ## Model aliases are not assurance profiles Model-list and completion types do not include `assurance`, `attestation`, `tee`, or provider-backed/Reticle-verifiable fields. * OpenAI-format and direct SDK completions can return a backend-resolved model ID in `response.model`. * The Anthropic adapter returns the requested alias in its `model` field. * Neither response is a substitute for an assurance profile. Do not classify a model as provider-backed or Reticle-verifiable from its name. See [Platform Status](/platform-status) for the current contract gap. ## Use the high-level Reticle API Install the published package: ```bash theme={"system"} npm install @premai/reticle@0.5.1 ``` Use `attest()` or a high-level component method: ```typescript theme={"system"} import { ClientBuilder, QueryParams } from "@premai/reticle"; const apiKey = process.env.PREM_API_KEY; const proxyUrl = process.env.PROXY_URL; if (!apiKey || !proxyUrl) { throw new Error("Set PREM_API_KEY and PROXY_URL"); } const client = await new ClientBuilder(proxyUrl) .with_authorization(apiKey) .build(); client.set_query(new QueryParams().with("model", "glm-5.2")); const result = await client.attest(); console.log("Modules:", result.modules()); console.log("GPU session:", result.headers().gpu()?.get("x-session-id")); ``` The component methods are: ```typescript theme={"system"} await client.attest_sev(); await client.attest_tdx(); await client.attest_nvidia(); ``` Call only the method for hardware that the endpoint reports. `client.attest()` performs module discovery first. ## Do not use request methods as verification The published type definitions warn that these low-level methods request and parse evidence but do not perform cryptographic or measurement checks: ```typescript theme={"system"} await client.request_sev(nonce); await client.request_tdx(nonce); await client.request_nvidia(nonce); ``` Use them only when you are implementing and testing your own complete verifier. A successful `request_*` call is not an attestation pass. ## Multi-GPU attestation and load balancing Multi-GPU inference can use several physical GPUs for one model backend. The inspected NVIDIA parser accepts multiple detached GPU JWTs, checks that overall-token submodule digests have matching detached tokens, verifies the returned JWT signatures, and applies nonce validation to each returned GPU claim. That is only part of a multi-GPU decision. The reviewed client does not expose the per-GPU claims or verdicts to the application, reject every possible extra detached token through an exact-set comparison, or demonstrate that the session and load balancer are bound to the appraised GPU set. The current `CheckValidator` implementation for selected per-GPU certificate fields also requires code-owner review because its boolean failure condition does not match the field names' apparent meaning. End-to-end multi-GPU attestation is not established until the application can audit the exact GPU set and the serving path fails closed when topology or scheduling changes. ## CPU technologies and other processors AMD SEV-SNP and Intel TDX are confidential-VM technologies with remote-attestation formats and manufacturer collateral that Reticle implements. Other CPUs are not automatically insecure; they are unsupported by this verifier unless there is a corresponding confidential-computing architecture, evidence format, collateral path, and policy implementation. Support for one CPU family is significant because it lets a remote client authenticate hardware evidence and freshness for that family. It does not make all workloads, measurements, or firmware states acceptable without policy checks. ## GPU technologies NVIDIA confidential computing is hardware- and firmware-dependent. “Hopper” and “Blackwell” are architecture families, not sufficient policy inputs. A verifier must examine the actual signed token and claims for the deployed SKU and software stack. GPUs without a supported confidential-computing mode and attestation format cannot provide the same remote evidence through Reticle. They may still run inference, but the client cannot transfer NVIDIA confidential-computing claims to them. ## Audit records An audit record should contain non-sensitive decision metadata, such as: * request/support ID and time; * requested alias and resolved model ID when exposed; * verifier and policy version; * evidence type, hardware identity fields, and TCB result; * nonce/freshness result; * reference-set identifier and measurement result when implemented; * allow/deny decision and reason. Do not record prompts, responses, API keys, KEKs, raw decrypted payloads, or secrets in an attestation audit log. ## Known gaps 1. Expected CPU measurement comparison remains TODO in the reviewed client source. 2. The API does not expose a model assurance profile. 3. NVIDIA tokens can carry multiple GPU claims, but exact-set appraisal, exposed per-GPU verdicts, and scheduler binding are not established end to end. 4. Reproducible release images and a public release-to-measurement mapping are separate requirements. 5. Package and runtime version reporting currently disagree for the Confidential Proxy. These findings apply to the reviewed package and source versions. Changes to Reticle, the SDK, endpoints, or policy require new source and live validation. # Authentication Source: https://docs.prem.io/authentication Authenticate with the Prem API using your API key and client encryption key (KEK). ## Basic API Key Authentication Authenticate with your API key: ```typescript theme={"system"} import createRvencClient from "@premai/api-sdk"; const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK }); // Verify authentication const response = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); ``` ## Use Pre-generated Keys Use pre-generated encryption keys for better performance: ```typescript theme={"system"} import createRvencClient, { generateEncryptionKeys } from "@premai/api-sdk"; // Generate keys once const encryptionKeys = await generateEncryptionKeys(); // Reuse keys across multiple requests const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, encryptionKeys, }); const response = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); ``` ## Use Custom Options Configure custom options such as timeout and buffer size: ```typescript theme={"system"} const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK, requestTimeoutMs: 60000, // 60 seconds maxBufferSize: 20 * 1024 * 1024, // 20MB }); ``` # Balance Source: https://docs.prem.io/billing/balance Manage your account balance and auto top-up settings. Prem API uses a **prepaid balance** model. Each API request decreases your account balance. The cost of each request depends on the model and on the number of tokens. View and manage your balance in the [dashboard](https://dashboard.prem.io/billing). ## Top Up Balance You can add funds to your account at any time in the dashboard. * **Minimum amount:** \$1 * **Payment processing:** Stripe When Stripe confirms the payment, the funds go to your balance immediately. Keep your balance above zero to prevent interruptions. If your balance is zero, Prem API blocks API requests until you top up. ## Auto Top-Up Auto top-up adds funds automatically when your balance goes below a configured threshold. This helps you keep API access without interruption. To enable auto top-up, configure these settings in the dashboard: | Setting | Description | | ----------------------------- | ------------------------------------------------------------------------------- | | **Minimum Balance Threshold** | When your balance goes below this amount, the system starts an automatic top-up | | **Top Up To** | The target balance after the auto top-up completes | You must set a [default payment method](/billing/payment) before you enable auto top-up. ### Example This example uses a Minimum Balance Threshold of 10 USD and a Top Up To value of 100 USD: * When your balance goes below the threshold, the system automatically charges your default payment method. * The system tops up your balance to 100 USD. # Limits Source: https://docs.prem.io/billing/limits Configure monthly spending limits. ## Monthly Limit (Budget) Each organization has a **monthly spending limit** (budget). This is the maximum amount that you can spend on API usage in a calendar month. When your spending reaches the budget limit, Prem API blocks API requests until: * The next billing period starts (the 1st of the next month), or * You increase the limit in the [dashboard](https://dashboard.prem.io/billing) Your monthly spending (`period_spent`) goes to zero automatically on the **1st of each month**. ## Reasons to change your limit * **Cost protection**: prevent unexpected charges from scripts that do not stop or from integrations with an incorrect configuration * **Team budget control**: keep the spending in the budget of your organization * **Scale up**: increase the limit when your usage grows ## Default limits by tier Each [usage tier](/rate-limits#usage-tiers) has a default monthly budget limit: | Tier | Monthly Budget Limit | | ------ | -------------------- | | Base | \$100 | | Tier 1 | \$500 | | Tier 2 | \$5,000 | | Tier 3 | \$20,000 | You can change your monthly limit at any time in the dashboard. An increase of the limit is effective immediately. # Models & Pricing Source: https://docs.prem.io/billing/models-and-pricing Explore available models, pricing tiers, and deployment strategies, from shared infrastructure to air-gapped on-prem. This page shows the prices for all available models on the Prem API platform. All prices are pay-as-you-go, based on usage. ## Available Models | Model | API ID | Type | Modalities | Pricing | | :---------------------------------------------------------------------- | :------------------------ | :------------------ | :----------------- | :--------------------------------- | | **Qwen 3.6** · 27B ([weights](https://huggingface.co/Qwen/Qwen3.6-27B)) | `qwen36-27b` | Chat | Text, Image, Video | \$0.30 in · \$2.00 out / 1M tokens | | **DeepGram General Nova 3** | `deepgram/general-nova-3` | Audio Transcription | Audio | \$0.05 / audio minute | | **GLM 5.2** · 753B ([weights](https://huggingface.co/zai-org/GLM-5.2)) | `glm-5.2` | Chat | Text | \$1.15 in · \$4.12 out / 1M tokens | * **Qwen 3.6**: A sparse Mixture-of-Experts architecture. It gives frontier-level intelligence with low latency. * **DeepGram General Nova 3**: A general-purpose ASR model with the highest performance. Use it for meetings, captions, and multilingual audio. * **GLM 5.2**: A high-performance multimodal model. It is good at advanced reasoning, full-stack coding, multilingual processing, mathematics, and precise instruction following. It has robust tool-use capabilities. Try in Playground You get access to all models through an OpenAI-compatible API. See the [Quickstart](/quickstart) guide to start. ## Deployment Strategies | Feature | Explorer | Developer | Enterprise | | -------------------- | ----------------- | ------------------- | -------------------- | | **Rate Limit** | 10 req / sec | Scalable | Dedicated | | **API Deployment** | Multi-Tenant APIs | Dedicated APIs | Dedicated APIs | | **Model Deployment** | Curated Models | Custom Model Choice | Custom Model Choice | | **Infrastructure** | Shared Instances | Dedicated Instances | air-gapped / on-prem | See the [Rate Limits](/rate-limits) page for the rate limits of each tier. The page includes token limits (TPM), audio processing limits, and concurrent request limits. Prem serves all APIs in agreement with its security model. For more information, see the [Security Model](/security-model) page. ## Get Started Register. Then start to use the Confidential APIs. Make your first API call in minutes. Contact Prem for enterprise features, custom models, or dedicated infrastructure. # Payment Source: https://docs.prem.io/billing/payment Manage payment methods. Manage your payment methods in the [dashboard](https://dashboard.prem.io/billing). Prem uses payment methods for [balance top-ups](/billing/balance) and [auto top-up](/billing/balance#auto-top-up). ## Add a payment method Add a new card in the dashboard. Stripe stores the card securely. You can use the card for subsequent transactions. ## Default payment method You can store more than one payment method. But **you must set one payment method as the default**. Prem uses the default payment method for: * Manual balance top-ups * Automatic top-ups, when your balance goes below the configured threshold You can change the default payment method at any time in the dashboard. # Usage Source: https://docs.prem.io/billing/usage Monitor your resource consumption and token usage. The Usage section in the [dashboard](https://dashboard.prem.io/billing/usage) gives analytics on the resource consumption of your organization. ## Storage & Resources Monitor your current resource usage: | Metric | Description | | ---------------- | ---------------------------------------- | | **Storage used** | The total size of uploaded files | | **Files count** | The number of stored files | | **User seats** | The number of users in your organization | ## Token Usage (Daily) View a daily breakdown of token consumption for the **last 30 days**: * **Prompt tokens**: the tokens in your input messages * **Completion tokens**: the tokens generated in responses * **Reasoning tokens**: the tokens used for model reasoning * **Total cost**: the calculated cost, based on model pricing This data helps you find usage trends. It also helps you plan your budget. Reasoning models generate reasoning tokens by default, and they are billed at the completion-token rate even though they never appear in `message.content`. See [Agents & Automation](/agents) for how to disable or bound reasoning. ## Token Usage by Model See the distribution of token consumption across the models for the **last 30 days**. This breakdown helps you: * Find the models that use the most resources * Decrease costs when you select the correct model for each task * Monitor the adoption of the models in your organization # Changelog & Release Notes Source: https://docs.prem.io/changelog Unified changelog and release notes for the Confidential API. # Confidential Proxy Source: https://docs.prem.io/confidential-proxy Run a local proxy for supported OpenAI- and Anthropic-compatible routes. Point a compatible client at the local base URL. The **Confidential Proxy** is a local termination proxy that comes with [`@premai/api-sdk`](https://www.npmjs.com/package/@premai/api-sdk). It exposes supported **OpenAI- and Anthropic-compatible** HTTP routes on your machine. For a supported OpenAI client, set the base URL to `http://127.0.0.1:8787/v1`. Anthropic clients use the same local base URL when the proxy runs in Anthropic mode. In `both` mode, use the separate prefixes documented below. If you use the TypeScript SDK, you do not need the Confidential Proxy. The SDK encrypts in the same process. Use the Confidential Proxy for other languages and for existing OpenAI or Anthropic codebases. ## How it works The Confidential Proxy runs on your machine and applies the SDK's client-side encryption. It encrypts the request payload **before** network egress. The Prem API Gateway receives payload ciphertext plus operational metadata. The selected confidential runtime decrypts the request inside its Trusted Execution Environment (TEE). ```mermaid theme={"system"} flowchart LR subgraph Local["Your Machine"] A[OpenAI / Anthropic client] -->|"baseURL → 127.0.0.1:8787"| B[confidential-proxy] B -->|"encrypt"| C[Encrypted request] end subgraph Gateway["Prem API Gateway"] D[Payload ciphertext and metadata] end subgraph Enclave["Prem API Enclave (TEE)"] E[Decrypt → process → encrypt] end C --> D --> E E --> D D -->|Encrypted response| B B -->|Decrypted response| A ``` See [Encryption](/encryption) for the full cryptographic design: the XWing key exchange, the two-server model, and the threat model. ## Run the server Run the Confidential Proxy directly with `bunx` or `npx`. No installation is necessary. As an alternative, install it globally: ```bash theme={"system"} # Run without installing (bun or npm) bunx -p @premai/api-sdk@1.0.59 confidential-proxy --kek "$CLIENT_KEK" npx -p @premai/api-sdk@1.0.59 confidential-proxy --kek "$CLIENT_KEK" # Or install globally, then run (confirm your global bin dir is on your PATH) npm i -g @premai/api-sdk # or: bun i -g @premai/api-sdk confidential-proxy --kek "$CLIENT_KEK" ``` A prebuilt Docker image is available at `ghcr.io/premai-io/confidential-proxy:latest`: ```bash theme={"system"} docker pull ghcr.io/premai-io/confidential-proxy:latest # OpenAI-compatible server on port 8787 docker run -p 8787:8787 \ -e PROXY_URL=... -e ENCLAVE_URL=... \ ghcr.io/premai-io/confidential-proxy:latest \ --kek "$CLIENT_KEK" # Extra arguments go to the CLI docker run -p 8787:8787 \ -e PROXY_URL=... -e ENCLAVE_URL=... \ ghcr.io/premai-io/confidential-proxy:latest \ --kek "$CLIENT_KEK" --log-level debug ``` The image sets `HOST=0.0.0.0` and `PORT=8787`. You can override these values with environment variables or CLI flags. Use CLI flags for `--compat` and `--tls`. You cannot set these two flags with environment variables. By default, the server listens on **`http://127.0.0.1:8787`**. `8787` is the default in `@premai/api-sdk` and in the `confidential-claude` launcher. Port `8000` also works when you start the proxy with `--port 8000` and point every client at the same port. A client configured for `8000` cannot reach a proxy that was started without a port override, because that proxy listens on `8787`. Set `PROXY_URL` and `ENCLAVE_URL` to the values for your environment. Get the latest values from [`dashboard.prem.io/endpoints.json`](https://dashboard.prem.io/endpoints.json). ## Configuration Configure the Confidential Proxy with environment variables or CLI flags. Flags have precedence. ### Environment variables | Variable | Required | Default | Description | | ------------------------------ | -------- | ----------- | ---------------------------------------------------------------------------------- | | `ENCLAVE_URL` | Yes | - | The enclave endpoint that decrypts the data and runs inference | | `PROXY_URL` | Yes | - | The Prem API Gateway endpoint that routes encrypted payloads | | `JSON_BODY_LIMIT` | No | `32mb` | The maximum size of the request body | | `HOST` | No | `127.0.0.1` | The interface to bind | | `PORT` | No | `8787` | The port to listen on | | `CONFIDENTIAL_PROXY_LOG_LEVEL` | No | `info` | `error`, `warn`, `info`, `http`, `verbose`, `debug`, or `silly` | | `PREM_API_KEY` | No | - | A default API key. The proxy uses this key when a client does not send its own key | Set `PREM_API_KEY` to a default API key. The proxy applies this key when a client does not supply its own key. Each client can also send its own API key with each request. Use `Authorization: Bearer ` for OpenAI routes. Use `x-api-key: ` for Anthropic routes. The Confidential Proxy keeps one client in memory for each API key. `CLIENT_KEK` is a separate key. The Confidential Proxy uses it only to wrap encryption keys. It is not an API key. The current CLI does not bind the `CLIENT_KEK` environment variable to the server option. Pass the value explicitly with `--kek "$CLIENT_KEK"`. ### CLI options All commands accept the same server options: ```bash theme={"system"} # Bind host / port confidential-proxy --host 127.0.0.1 --port 8787 --kek "$CLIENT_KEK" # Override backend endpoints confidential-proxy --proxy-url https://gateway.prem.io --enclave-url https://conf-engine.prem.io --kek "$CLIENT_KEK" # Pass the client KEK from the environment # The value must be 32 bytes encoded as 64 hexadecimal characters. confidential-proxy --kek "$CLIENT_KEK" # Raise the JSON body size limit confidential-proxy --json-body-limit 64mb --kek "$CLIENT_KEK" ``` ## Compatibility modes Use `--compat` to select the API surface: | Mode | Routes | Description | | ----------- | ------------------------------------ | -------------------------------------------------- | | `openai` | `/v1/*` | The OpenAI-compatible API only | | `anthropic` | `/v1/*` | The Anthropic-compatible Messages API only | | `both` | `/openai/v1/*` and `/anthropic/v1/*` | The two APIs together, each with a separate prefix | ```bash theme={"system"} # OpenAI only (default surface) confidential-proxy --compat openai --kek "$CLIENT_KEK" # Anthropic only confidential-proxy --compat anthropic --kek "$CLIENT_KEK" # Both, with custom prefixes confidential-proxy --compat both --openai-prefix /openai --anthropic-prefix /anthropic --kek "$CLIENT_KEK" ``` In `both` mode, the Confidential Proxy serves the two APIs under separate prefixes. This prevents route conflicts. Set the base URL to `http://127.0.0.1:8787/openai/v1` for OpenAI clients. Set it to `http://127.0.0.1:8787/anthropic/v1` for Anthropic clients. The Anthropic surface translates each Anthropic Messages request into the internal OpenAI-compatible enclave pipeline. It then returns the response as Anthropic SSE events. ## Connect a client ### OpenAI Set the base URL to `http://127.0.0.1:8787/v1`. If you use `--compat both`, set it to `http://127.0.0.1:8787/openai/v1`. Send your API key as a bearer token: See [OpenAI-compatible clients](/guides/openai-compatible-clients) for supported routes, configuration patterns, production controls, and compatibility limits. ```bash theme={"system"} curl http://127.0.0.1:8787/v1/chat/completions \ -H "Authorization: Bearer your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.2", "messages": [{"role": "user", "content": "Hello!"}], "stream": false }' ``` As an alternative, use the OpenAI SDK in Node.js: ```typescript theme={"system"} import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.PREM_API_KEY!, baseURL: "http://127.0.0.1:8787/v1", }); const stream = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Count to 10" }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } ``` The same pattern applies to all other languages. This is a Python example: ```python theme={"system"} from openai import OpenAI client = OpenAI( api_key="your-api-key", base_url="http://127.0.0.1:8787/v1", ) response = client.chat.completions.create( model="glm-5.2", messages=[{"role": "user", "content": "Hello, privately."}], ) print(response.choices[0].message.content) ``` ### Anthropic Set the base URL to `http://127.0.0.1:8787/v1`. If you use `--compat both`, set it to `http://127.0.0.1:8787/anthropic/v1`. Authenticate with `x-api-key`. Send the `anthropic-version` header: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/messages \ -H "x-api-key: your-api-key" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.2", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello!"}] }' ``` Add `"stream": true` for incremental responses: ```bash theme={"system"} curl -N http://127.0.0.1:8787/v1/messages \ -H "x-api-key: your-api-key" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.2", "max_tokens": 1024, "messages": [{"role": "user", "content": "Count to 10"}], "stream": true }' ``` The Anthropic surface supports system prompts, tool use, image inputs, stop sequences, `temperature`, and `top_p`. Streaming responses follow the Anthropic SSE format (`message_start`, `content_block_start`, `content_block_delta`, `content_block_stop`, `message_delta`, `message_stop`). See [Anthropic-compatible clients](/guides/anthropic-compatible-clients) for translation behavior, model substitution, token estimates, tool handling, and known limits. ### Agent harnesses Use a dedicated guide for each harness. The provider schema, protocol selection, tool boundary, and startup command differ. Configure an OpenAI-compatible provider in a project. Launch Claude Code through the bundled Anthropic adapter. Configure a custom Chat Completions provider. Use a named custom provider with an explicit Chat Completions transport. Point the built-in OpenAI provider at the local proxy. Review the current boundary around Loupe's embedded Claude Code workers. Understand why Cursor's base URL override is not a confidential local path. Review the current Responses API protocol mismatch. ### Router alternative Beta Router is a separate, non-confidential service with a broader current chat-model catalog. It does not use the Confidential Proxy, client KEK, or attestation path. Use the [Router integrations guide](/router/integrations) for supported Chat Completions harnesses. Router uses `PREM_ROUTER_API_KEY` and `https://router.prem.io/v1`; the Confidential API uses `PREM_API_KEY`, a client KEK, and the local proxy. Their credentials and data-handling properties are not interchangeable. ### Claude for Microsoft 365 The Confidential Proxy can be the gateway for the [Claude for Microsoft 365 add-in](https://support.claude.com/en/articles/13945233-use-claude-for-microsoft-365-with-third-party-platforms). The add-in uses HTTPS and CORS. Start the proxy with these flags: See [Claude for Microsoft 365](/guides/claude-microsoft-365) for tenant setup, key custody, network placement, acceptance tests, and security boundaries. ```bash theme={"system"} confidential-proxy \ --host 0.0.0.0 --port 8787 \ --compat anthropic \ --kek "$CLIENT_KEK" \ --default-model glm-5.2 \ --cors-origin https://pivot.claude.ai \ --tls --tls-cert ./cert.pem --tls-key ./key.pem ``` The add-in cannot connect to `127.0.0.1` on macOS. macOS prevents cross-origin browser requests to loopback addresses. Run the proxy on a different machine on your network, or use a container with its own network interface. Point the add-in at that hostname. ## Run the proxy as a daemon By default, the Confidential Proxy runs in the foreground. The CLI can also manage it as a background daemon. | Command | Description | | --------------------------- | ----------------------------------------------- | | `confidential-proxy` | Run in the foreground, attached to the terminal | | `confidential-proxy start` | Start the server as a background daemon | | `confidential-proxy stop` | Stop the daemon with a graceful shutdown | | `confidential-proxy status` | Show if the daemon runs and is reachable | The `start` command does these steps: * It checks for a daemon that is in operation. It does not start a second daemon. * It spawns itself as a child process. It sends the logs to the log file that you configure. * It polls the HTTP endpoint until the server is reachable. * It then exits and the daemon continues to run. The `stop` command stops the daemon. It waits a maximum of 5 seconds. If the daemon does not stop, the command stops it immediately. The `status` command checks if the process is alive and if the HTTP endpoint is reachable. These options apply to the daemon commands (`start`, `stop`, `status`): | Option | Default | Description | | -------------------- | ---------------------- | --------------------------------------------------------------------------------- | | `--pid-file` | `/proxy.pid` | A custom path for the PID file | | `--log-file` | stdout/stderr | The file for the daemon logs (with `start`) | | `--log-level` | `info` | The log verbosity (`error` … `silly`) | | `--shutdown-timeout` | `30000` | The maximum time (ms) to wait for requests in progress during a graceful shutdown | ```bash theme={"system"} # Start in the background, then confirm it's up confidential-proxy start --compat openai --kek "$CLIENT_KEK" confidential-proxy status # Stop it when you're done confidential-proxy stop ``` ## Next steps The step-by-step guide to get your first request working. The two ways to integrate: the TypeScript SDK and the Confidential Proxy. The chat API in detail, with streaming and vision payloads. The key exchange and the end-to-end encryption in detail. Running the proxy behind an unattended agent or automation? Start here. Connect OpenCode to the encrypted OpenAI-compatible route. Launch Claude Code through the encrypted Anthropic-compatible route. Connect existing OpenAI SDKs and applications. Use the Messages API shape with explicit compatibility limits. Deploy the proxy as a Microsoft 365 add-in gateway. The SDK also includes `confidential-claude`. It launches Claude Code with the local Anthropic-compatible proxy and forwards Claude Code's arguments. See [Claude Code](/guides/claude-code) for the exact environment variables, model picker, stop command, and plaintext boundary. # Contact Us Source: https://docs.prem.io/contact-us Get help with integrations, billing, APIs, or anything else. **Live Chat:** This is the fastest option. Open the dashboard. Click the support button in the header. Select "Live Chat". Prem usually replies in a few minutes. **Email:** Send an email to `support@premai.io` for questions that need more detail. Prem replies within 1 business day. **Platform status:** Check the live platform availability on [status.premai.io](https://status.premai.io). ## Product-specific help Read the [Router overview](/router/overview) to get set up. Request API keys through the [Contact Us form](https://form.typeform.com/to/vZnBDhzs). Read the [Prem API quickstart](/quickstart) to get set up. For access, use Live Chat or email. # Data Retention Source: https://docs.prem.io/data-retention What Prem API stores, what it never stores, and how you can verify the policy. **The policy in one sentence:** Prem API does not store your prompts, completions, or audio after it returns the response. The platform keeps zero inference content. ## What Prem API never stores The platform does not retain inference content: * **Prompts and messages**: not stored after the response * **Completions and model output**: not stored after the response * **Audio for transcription and translation**: not stored after the response The encrypted inference endpoints are named RVENC: raw **volatile** encrypted. Your content exists inside the enclave only in volatile memory, for the duration of the request. See [How It Works](/how-it-works) for the request lifecycle. ## What Prem API stores Some data is necessary to operate the platform: * **Files that you upload**: The platform stores files that you upload to the file endpoints. The files are encrypted with your keys. Only you can decrypt them. You can delete them at any time. See [Encryption](/encryption). * **Request metadata**: The gateway records the time of each request, the payload size, the API key, and the rate limit counters. Prem uses this metadata for billing, rate limits, and analytics. The metadata does not include your content. See the [Security Model](/security-model). * **Error traces**: Error responses include a `support_id`. Prem keeps the trace for that identifier for up to one week. See [Errors](/errors). ## Why you do not have to trust this page Most providers publish a retention policy and ask you to trust it. Prem API adds two verifiable protections: 1. **The platform cannot read your content.** Your device encrypts all content before it leaves. The gateway routes only encrypted bytes. Decryption happens only inside the sealed enclave. A stored copy of your traffic would be useless ciphertext. See [Encryption](/encryption). 2. **You can verify hardware evidence and freshness.** An approved measurement policy is still required to prove that the expected release image is running. See [Attestation](/attestation). Questions about retention, or requirements for a specific agreement? Contact us at [support@premai.io](mailto:support@premai.io). # Developer Experience Source: https://docs.prem.io/developer-experience What it is like to build on Prem API: SDK integration and OpenAI compatibility. **You do not need encryption knowledge.** The encryption layer is not visible in your application code. If you used the OpenAI API before, you know how to use Prem API. The SDK does all cryptography automatically. You write normal API calls and you get normal responses. ## Two Ways to Integrate ### Option 1: Prem API TypeScript SDK (Recommended) Install the SDK. Use it like an OpenAI client: ```typescript theme={"system"} import { createRvencClient } from "@premai/api-sdk"; const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK, // Your master key. You generate it, we never see it }); // This looks exactly like an OpenAI call, because it is const chat = await client.chat.completions.create({ model: "glm-5.2", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "Summarize this quarterly report." }, ], stream: true, }); for await (const chunk of chat) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } ``` The SDK does these steps for you: * It encrypts your messages before it sends them. * It does a secure key exchange with the enclave. * It decrypts each streaming chunk when the chunk arrives. You do not see these steps. Your code looks like an OpenAI integration. ### Option 2: Confidential Proxy (Any Language) The SDK includes the Confidential Proxy, a local server that does all encryption for you. Use it for Python, Go, Java, or other languages with an OpenAI-compatible client library: ```bash theme={"system"} # Start the local proxy (one command) bunx -p @premai/api-sdk@1.0.59 confidential-proxy --kek "$CLIENT_KEK" ``` Then point your existing code at `localhost`: ```python theme={"system"} from openai import OpenAI # Your existing OpenAI code. Just change the base URL client = OpenAI( base_url="http://localhost:8787/v1", api_key="your-api-key", ) response = client.chat.completions.create( model="glm-5.2", messages=[{"role": "user", "content": "Hello, privately."}], ) ``` You make **zero code changes** to your application logic. The Confidential Proxy encrypts outbound requests and decrypts responses automatically. See [Confidential Proxy](/confidential-proxy) for the full configuration and Anthropic support. ## What You Can Do ### Chat with AI Models The chat API is OpenAI-compatible. It includes these features: | Feature | Details | | ---------------------------- | ----------------------------------------------------------------------------- | | **Streaming** | Output in real time, word by word. Prem API encrypts each chunk separately | | **JSON mode** | Structured output for reliable parsing | | **System messages** | Control the behavior and personality of the model | | **Multi-turn conversations** | Full conversation history and context management | | **Audio transcription** | Convert speech to text with the currently enabled Deepgram model | | **Audio translation** | Route exists, but no enabled translation model was returned on August 4, 2026 | ## Error Handling Prem API uses standard HTTP status codes with structured error responses: | Code | What It Means | What to Do | | ---- | ------------------------ | ----------------------------------------------------------------- | | 400 | Invalid request format | Check your input against the API spec | | 401 | Invalid API key | Make sure that your API key is correct and active | | 403 | Insufficient permissions | Check the scopes of your API key | | 429 | Rate limited | Use exponential backoff (examples in [Rate Limits](/rate-limits)) | | 503 | Temporarily unavailable | Wait, then send the request again | Each error includes a `support_id`. Give this ID to the Prem team to help with debugging. ## Rate Limits Rate limits apply to each organization, across four dimensions: | Dimension | What It Limits | Why | | ----------------------------- | ------------------------------ | ------------------------------------------------------- | | **RPS** (Requests per second) | How fast you can send requests | Prevents bursts that overload the system | | **TPM** (Tokens per minute) | Total token throughput | Manages inference capacity | | **Concurrent** | Simultaneous active requests | Makes sure that all users get a fair share of resources | Limits increase across the tiers (Free, Tier 1, Tier 2, Tier 3) when your usage grows. See [Rate Limits](/rate-limits) for the values and for retry strategies with code examples. See the [Quickstart](/quickstart) for the step-by-step setup. See the [Guides](/guides/chat-completion) for examples that you can copy. If you're building an unattended agent or automation on top of Prem API, see [Agents & Automation](/agents) for the config and operational gotchas that matter at scale. # Encryption Source: https://docs.prem.io/encryption Learn about the end-to-end encryption architecture that protects your data. Prem API uses **end-to-end encryption** (E2EE) to make sure that your data stays private and secure. All encryption occurs on your device. Your plaintext data does not leave your device without encryption. Prem cannot read your data. ## Zero-Knowledge Architecture The platform operates on a **zero-knowledge** principle: Your device encrypts all data before transmission The servers see only encrypted data, never your actual data You generate the encryption keys, and you store them only on your side The encryption protects your data against current threats and future quantum computing threats ## How It Works The encryption architecture uses a **two-server model** for defense in depth: ```mermaid theme={"system"} flowchart LR subgraph Client["Your Device"] A[Plaintext Data] --> B[Encryption] B --> C[Encrypted Data] K[Your Keys] --> B H[Encrypted Response] --> I[Decryption] K --> I I --> J[Plaintext Response] end subgraph Proxy["Prem API Gateway"] D[Encrypted Data Only] end subgraph Enclave["Prem API Enclave"] E[Decrypt] F[Process] G[Encrypt Response] E --> F --> G end C --> D D --> E G --> D D --> H ``` ### Prem API Gateway vs Prem API Enclave | Component | What It Sees | Purpose | | -------------------- | ------------------- | ------------------------------------------------------------------------------------ | | **Prem API Gateway** | Only encrypted data | Stores and routes encrypted data. It never sees plaintext | | **Prem API Enclave** | Decrypted data | Decrypts requests, processes them, and encrypts responses in an isolated environment | The **Prem API Gateway** is a secure gateway that **never has access to your plaintext data**. It only sends encrypted payloads forward to the Prem API Enclave. If an attacker gets control of the Prem API Gateway, the attacker sees only encrypted data. The **Prem API Enclave** operates in a **Trusted Execution Environment (TEE)**. A TEE is an isolated environment with hardware protection. In the TEE: * Decryption occurs in a secure environment * Processing occurs in full isolation * The enclave encrypts the responses before the responses leave * Server operators cannot access the plaintext, because no operator access mechanism exists in the images ### Key Types * **Key Encryption Key (KEK)**: Your master key (32 bytes). It protects all other keys. It does not leave your device without encryption. * **Data Encryption Key (DEK)**: A unique key for each file (32 bytes). Each file gets its own key for isolation. * **RAG DEK**: A persistent key (32 bytes). The system uses it for encrypted document search operations. * **CONNECTOR DEK** - A persistent key (32 bytes). The enclave uses it to decrypt connectors. * **KID (Key Identifier)**: The system derives the KID from your KEK. The KID identifies your keys on the server and does not show the KEK. Your master key (KEK) does not leave your device without encryption. The SDK wraps the file keys (DEKs) with your KEK before transmission. The server stores the RAG DEK wrapped with your KEK. For RAG operations, the SDK encrypts the file DEKs and the RAG DEK with a temporary shared secret from the XWing key exchange. Then the SDK sends these keys to the enclave. ## Cryptographic Algorithms Prem API uses modern, proven cryptographic algorithms: | Algorithm | Type | Purpose | | ---------------------- | ---------- | ------------------------------------------------- | | **XChaCha20-Poly1305** | AEAD | Encrypts all your data with authentication | | **AES-KWP** | Key Wrap | Securely wraps the file keys with your master key | | **XWing** | Hybrid KEM | Post-quantum secure key exchange | ### Post-Quantum Security with XWing **XWing** is a hybrid key encapsulation mechanism. It combines two algorithms for maximum security: ```mermaid theme={"system"} flowchart LR subgraph XWing["XWing Hybrid KEM"] A["ML-KEM768 (Post-Quantum)"] --> C["Combined Shared Secret"] B["X25519 (Classical)"] --> C end C --> D["XChaCha20-Poly1305 Encryption"] ``` * **ML-KEM768 (Kyber)**: A NIST-standardized quantum-resistant algorithm * **X25519**: A proven elliptic curve algorithm This hybrid method makes sure that your data is safe against current attacks and future quantum computers. If an attacker breaks one algorithm, the other algorithm keeps your data secure. Sufficiently powerful quantum computers will break traditional encryption, for example RSA and standard elliptic curves. XWing protects against "Harvest Now, Decrypt Later" attacks. In these attacks, adversaries store encrypted data today. They decrypt the data when quantum computers become powerful. ## File Encryption Each file that you upload goes through a secure encryption process: ```mermaid theme={"system"} flowchart TD A[Original File] --> B[Generate Random File Key] B --> C[Encrypt File Content] B --> D[Encrypt Metadata] C --> E[Encrypted File] D --> F[Encrypted Metadata] G[Your Master Key] --> H[Wrap File Key] B --> H H --> I[Wrapped File Key] E --> J[Upload to Server] F --> J I --> J ``` ### Encryption Process The SDK creates a unique random 32-byte key for this specific file The SDK encrypts the file content and the metadata (filename, type) with XChaCha20-Poly1305 The SDK encrypts the file key with your master key. It uses AES-KWP The SDK sends only encrypted data and wrapped keys to the server ### Decryption Process When you get a file: 1. **Download** the encrypted file and the wrapped key from the server 2. **Unwrap** the file key with your master key 3. **Decrypt** the file content and the metadata ## RAG Encryption For encrypted document search, Prem API uses a **secure key exchange scheme**. This scheme permits AI-powered search and keeps your data private: ```mermaid theme={"system"} flowchart TD subgraph Client["Your Device"] A[XWing Key Exchange] --> B[Generate Shared Secret] C[File Key DEK] --> D[Encrypt with Shared Secret] D --> E[Encrypted File Key] F[RAG Key] --> G[Encrypt with Shared Secret] G --> H[Encrypted RAG Key] B --> D B --> G end subgraph Enclave["Prem API Enclave"] I[Decrypt with Shared Secret] J[Decrypt File Key] K[Decrypt RAG Key] L[Decrypt File Content] M[Build Search Index] I --> J I --> K J --> L K --> M L --> M end E --> I H --> I ``` ### How RAG Search Works When you index files for search: * The XWing key exchange establishes a shared secret with the enclave * The SDK encrypts the file key (DEK) with the shared secret * The SDK encrypts the RAG key with the shared secret * The SDK sends both encrypted keys to the enclave When you search: * The SDK encrypts your query on your device * The SDK establishes a new XWing key exchange * The SDK sends the encrypted query to the enclave Inside the secure enclave: * The shared secret decrypts the RAG key and the file keys * The enclave decrypts the applicable documents with their file keys * The enclave searches the documents in isolation * The enclave encrypts the results before the results leave The enclave sends the encrypted results back to your device. Your device decrypts the results locally The Prem API Gateway never sees your search queries or your document contents. All processing occurs in the isolated Prem API Enclave. ## Secure Key Exchange For chat completions and tool operations, the SDK establishes secure communication with the **XWing key exchange**: ```mermaid theme={"system"} sequenceDiagram participant Client as Your Device participant Enclave as Prem API Enclave participant Proxy as Prem API Gateway Note over Client,Enclave: 1. Key Exchange Client->>Enclave: Request public key Enclave-->>Client: Public key Note over Client: 2. Generate Session Key Client->>Client: Create shared secret using XWing Note over Client: 3. Encrypt & Send Client->>Client: Encrypt request Client->>Proxy: Send encrypted data Note over Proxy: Only sees encrypted bytes Proxy->>Enclave: Forward encrypted payload Note over Enclave: 4. Process Securely Enclave->>Enclave: Decrypt and process Enclave->>Enclave: Encrypt response Enclave-->>Proxy: Encrypted response Proxy-->>Client: Forward encrypted response Client->>Client: Decrypt response ``` ## Best Practices Store your master key (KEK) securely and keep backups. The KEK is critical. Make backups of your keys in multiple secure locations. If you lose your master key, you lose your data permanently. Do not share your encryption keys. Do not transmit them in plaintext. The system verifies data integrity automatically. Examine all authentication errors. # Errors Source: https://docs.prem.io/errors Learn how API errors work, what status codes mean, and how to handle them properly. Our API returns consistent, structured errors. These errors help you debug and recover from problems in development and in production. ## Error types & status codes The API uses standard HTTP status codes to show the success or failure of a request. Errors always return: * An applicable HTTP status code (`4xx` or `5xx`) * A JSON response body that contains: * `status`: the HTTP status code * `error`: a human-readable error message * `support_id`: a unique reference ID that you can give to support for debugging Contact us in the week after the error occurs. We keep the `support_id` fields in our systems only for **up to a week**. After that time, they can be lost. Example error response: ```json theme={"system"} { "status": 403, "error": "Access denied. You do not have permission to perform this action.", "support_id": "support_018ecfc3-bfa6-7b95-a29c-bd8fc3f5b2c6" } ``` The `support_id` is a traceable identifier in the format `support_{uuidv7}`. The API includes it in every response, successful or not. If you find a problem, give this ID to our support team. This helps us investigate quickly and accurately. ## Common error codes | Code | Description | | ----- | -------------------------------------------------------------------------------------------------------- | | `400` | **Bad Request**: The request was malformed or contained invalid parameters. | | `401` | **Unauthorized**: Authentication failed. Your API key may be missing or incorrect. | | `403` | **Forbidden**: You are authenticated, but you do not have permission for the requested action. | | `404` | **Not Found**: The requested resource does not exist. | | `406` | **Not Acceptable**: The API does not support the requested response format (JSON is required). | | `429` | **Too Many Requests**: You exceeded your request quota. Retry after the time in the `Retry-After` header | | `500` | **Internal Server Error**: An unexpected error occurred on our side. These are rare. | | `503` | **Service Unavailable**: The system is temporarily offline (e.g., during maintenance). | ## Rate limits If your application sends too many requests in a short period, you can receive a 429 Too Many Requests error. These sources can apply a rate limit: * The infrastructure of Prem API * Cloudflare, our edge network These responses always include: * The HTTP status code 429 * A JSON response with status, error, and support\_id * An optional Retry-After header or field that shows when you can try again Implement retry logic. Obey this header to prevent repeated failures. Not every `429` follows this shape. Rate limiting applied further upstream (for example, at the model backend) can return a `429` status with a generic body and no `Retry-After` header. Branch on the HTTP status code, not the body, and fall back to your own exponential backoff when `Retry-After` is missing. See [Rate limits](/rate-limits#non-standard-429-responses). Read the rate limit specifications, how they operate, and how to increase them. ## Best Practices * Always check the status and error fields in responses, also for non-200 statuses. * Log the `support_id` for every request. Show it in error reports. This helps us help you faster. * Handle `5xx` errors safely with retries and exponential backoff. * Check authentication for `401` and `403` errors. Make sure that your API key is valid and has the required scopes. * Wait before you retry after `429` rate limit errors. Do not send requests continuously to the server. ## Tips to debug errors * Use tools such as curl, Postman, or HTTP clients with logs to examine the full request and response cycles. * Make sure that the request goes to the correct endpoint with the correct HTTP method (`GET`, `POST`, etc.). * Make sure that your request body matches the expected structure and content types (e.g., `application/json`). * Refer to the [Authentication](/authentication) and [API Keys](/api-keys) pages for details about access. If the problem continues, contact [support@premai.io](mailto:support@premai.io). Include the `support_id` from the error response. This helps us solve the problem quickly. # Glossary Source: https://docs.prem.io/glossary Definitions of the terms that the Prem API documentation uses. This page defines the terms that the Prem API documentation uses. Each page uses these terms with these exact meanings. ## Platform terms * **Prem API**: The end-to-end encrypted, OpenAI-compatible API for confidential AI inference. * **Prem API Gateway**: The service that receives your encrypted payloads. It handles authentication, billing, and routing. It cannot read your data. * **Enclave**: The sealed environment that decrypts your request, runs the AI model, and encrypts the response. The enclave runs inside a Trusted Execution Environment. * **Confidential Proxy**: A local proxy from the SDK. It exposes OpenAI-compatible and Anthropic-compatible routes. It encrypts and decrypts all traffic for you. See [Confidential Proxy](/confidential-proxy). * **RVENC**: Raw volatile encrypted. The name of the encrypted inference endpoints, for example `/rvenc/chat/completions`. * **Model router**: The component inside the sealed environment that sends each request to the correct AI model. ## Hardware and attestation terms * **TEE (Trusted Execution Environment)**: A hardware-isolated environment. The processor encrypts the memory of the environment and blocks access from the outside. See [Security Model](/security-model). * **CVM (Confidential Virtual Machine)**: A virtual machine with hardware-encrypted memory. Prem API uses AMD SEV-SNP and Intel TDX technology for CVMs. * **Attestation evidence**: A hardware-signed report or token that carries measurements and security claims. A verifier authenticates the evidence and applies a policy before accepting the runtime. See [Attestation](/attestation). * **Nonce**: A random challenge value that you send with an attestation request. The report contains your nonce. This proves that the report is fresh and not a replay. * **TCB (Trusted Computing Base)**: The firmware and software that the attestation report measures. The TCB version shows the firmware and microcode versions, so you can confirm the security patch levels. ## Key and encryption terms * **KEK (Key Encryption Key)**: Your master key. The current SDK expects 32 bytes encoded as 64 hexadecimal characters. You generate and keep it. It wraps other keys. See [Encryption](/encryption). * **DEK (Data Encryption Key)**: A unique key for one file (32 bytes). Each file gets its own DEK for isolation. * **DEK store**: The local store that holds your wrapped DEKs. Make a backup of it. If you lose it, you lose access to your uploaded files. * **RAG DEK**: A persistent key (32 bytes) for encrypted document search operations. * **KID (Key Identifier)**: An identifier that the system derives from your KEK. It identifies your keys on the server and does not show the KEK. * **XWing**: The hybrid key exchange that Prem API uses. It combines ML-KEM768 and X25519. * **ML-KEM768 (Kyber)**: A NIST-standardized quantum-resistant key encapsulation algorithm. * **X25519**: A proven elliptic curve key exchange algorithm. ## Account and API terms * **API key**: Your authentication credential. Prem issues API keys at the organization level. You can limit an API key with scopes and IP restrictions. See [API Keys](/api-keys). * **Scope**: A permission on an API key. A scope defines which parts of the API the key can access, for example `chats.completion`. * **Organization**: The account level that owns API keys, rate limits, and billing. * **Tier**: The usage level of your organization (`BASE`, `TIER_1`, `TIER_2`, `TIER_3`). The tier controls your rate limits. See [Rate limits](/rate-limits). * **Idempotency key**: A header value that makes a retry safe. The API executes a request with a known key only once in a 24-hour period. If a term is missing from this page, contact us at [support@premai.io](mailto:support@premai.io). # Anthropic-compatible clients Source: https://docs.prem.io/guides/anthropic-compatible-clients Connect a Messages API client to Prem API through the Confidential Proxy. Does your app already talk to the Anthropic Messages API? Then you can point it at Prem instead. You change one setting: the base URL. Prem runs the model inside a secure enclave. Your prompts stay private on the way there. You connect through a small helper on your machine: the **[Confidential Proxy](/confidential-proxy)**. "Anthropic-compatible" describes the shape of the requests. It does not mean Anthropic hosts the request. It does not mean a Claude model runs behind it. ## What the proxy does * Accepts the client's Anthropic Messages request on your machine. * Translates and encrypts the request before network egress. * Sends ciphertext and required metadata through the Prem API Gateway. * Decrypts the enclave response locally and returns Anthropic message objects or Server-Sent Events (SSE). ## How the connection works ```mermaid theme={"system"} flowchart TB subgraph Local["Your machine: plaintext is available"] C["Anthropic-compatible client"] -->|"Messages request"| A["Anthropic adapter"] A -->|"Translate request"| P["Confidential Proxy"] end P -->|"Encrypt before network egress"| G["Prem API Gateway
ciphertext and metadata"] G -->|"Route encrypted payload"| E["Prem API Enclave
decrypt, infer, encrypt"] E -.->|"Encrypted response"| P P -.->|"Messages response and SSE events"| C ``` Your app and the Confidential Proxy stay on your machine. The proxy translates your Messages request into Prem's format, then seals it. The Prem API Gateway sees only the sealed message. The Prem API Enclave opens it inside a Trusted Execution Environment (TEE). The proxy does two translations for you: 1. It turns your Messages request into Prem's internal chat request. 2. It turns the reply back into Anthropic message objects or Server-Sent Events (SSE). ## What you can connect You can connect any client that lets you set a custom base URL. The proxy gives your app these routes: | Method | Route | What it does | | ------ | --------------------------- | --------------------------------------------- | | `POST` | `/v1/messages` | Creates a message, with or without streaming. | | `POST` | `/v1/messages/count_tokens` | Returns an estimated input token count. | | `GET` | `/v1/models` | Lists the models your account can use. | | `GET` | `/v1/models/:model_id` | Gets one model. | Send the Prem API key with `x-api-key` or with `Authorization: Bearer`. The proxy uses that value as your Prem API key. ## Before you start Get these four things ready: * **An app** that lets you set a custom Anthropic base URL. * **A Prem API key.** This proves who you are. See [API Keys](/api-keys). * **A client Key Encryption Key (KEK).** This is a master key that protects your other keys. * **A model** that your Prem account can use. See [Models & Pricing](/billing/models-and-pricing). The API key and the KEK are two different secrets. The API key handles sign-in, limits, and billing. The KEK protects your encryption keys. Keep both safe. ## 1. Set your secrets Open a terminal. Set these values in the terminal that runs the proxy: ```bash theme={"system"} export PREM_API_KEY="your-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Do you not have a KEK yet? Make one, one time only: ```bash theme={"system"} openssl rand -hex 32 ``` Store the KEK in a secret manager. Keep a backup. Use the **same** KEK every time. Do not make a new one each time you start the proxy. `PROXY_URL` and `ENCLAVE_URL` show the current default endpoints. Check the [Prem dashboard](https://dashboard.prem.io) if the endpoints change. ## 2. Start the Confidential Proxy Start the proxy in Anthropic mode. Keep this terminal open while you work: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 127.0.0.1 \ --port 8787 \ --compat anthropic \ --kek "$CLIENT_KEK" ``` The proxy now listens at `http://127.0.0.1:8787`. Your app adds `/v1/messages` to that base URL. Do you need one proxy for both Anthropic and OpenAI apps? Start it in `both` mode: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 127.0.0.1 \ --port 8787 \ --compat both \ --kek "$CLIENT_KEK" ``` In `both` mode, the Anthropic base URL is `http://127.0.0.1:8787/anthropic`. Your app then adds `/v1/messages`. ## 3. Point your app at the proxy List available models: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/models \ -H "x-api-key: $PREM_API_KEY" \ -H "anthropic-version: 2023-06-01" ``` Use a returned model ID in the Messages request. The Anthropic adapter returns the requested alias in its `model` field. It does not currently expose the backend-resolved model ID or an assurance profile. See [Platform Status](/platform-status). ## 4. Test a message Send a non-streaming request: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/messages \ -H "x-api-key: $PREM_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.2", "max_tokens": 128, "messages": [{"role": "user", "content": "Reply with OK."}] }' ``` Test streaming after the first request succeeds: ```bash theme={"system"} curl -N http://127.0.0.1:8787/v1/messages \ -H "Authorization: Bearer $PREM_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.2", "max_tokens": 128, "stream": true, "messages": [{"role": "user", "content": "Count to three."}] }' ``` The stream uses Anthropic event names. These include `message_start`, content block events, `message_delta`, and `message_stop`. ## 5. Configure a client Set the client's API key and base URL. This example uses the Anthropic Node.js SDK: ```typescript theme={"system"} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ apiKey: process.env.PREM_API_KEY, baseURL: "http://127.0.0.1:8787", }); const response = await client.messages.create({ model: "glm-5.2", max_tokens: 128, messages: [{ role: "user", content: "Reply with OK." }], }); console.log(response.content); ``` Change `glm-5.2` to a model your account can use. See your model IDs on the [Prem dashboard](https://dashboard.prem.io). Each SDK joins the base URL and the route in its own way. Some clients want a host only. Some clients want a path that ends in `/v1`. Check your SDK. ## Send `claude` to a Prem model Some apps send the literal model name `claude`. You can map that one name to a Prem model: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 127.0.0.1 \ --port 8787 \ --compat anthropic \ --default-model glm-5.2 \ --kek "$CLIENT_KEK" ``` The proxy swaps in `glm-5.2` only when the model equals `claude`. It does not change other Claude model names. This swap changes routing only. It does not make the Prem model behave the same as a Claude model. ## What this setup protects | Component | Reads your text | Notes | | ------------------ | -------------------- | ---------------------------------------------------------- | | Your app | Yes | It creates the prompt and reads the reply on your machine. | | Anthropic adapter | Yes, on your machine | It translates requests and replies. | | Confidential Proxy | Yes, on your machine | It seals requests and opens replies. | | Prem API Gateway | No | It handles the sealed message and metadata only. | | Prem API Enclave | Yes, inside the TEE | It opens the request, runs the model, and seals the reply. | | External tools | Depends on the tool | Prem does not put them inside the enclave. | This setup protects the model path only. It does not put your app or its tools inside the enclave. ## Good to know **Tool calls run on your machine.** The model can return a `tool_use` block. Your app decides whether to run the tool. The tool runs outside the enclave. The next tool result enters the sealed path only after the proxy receives it. Apply your own access, approval, and logging rules to each tool. **Token counts are estimates.** The `count_tokens` route estimates tokens from the text. Use Prem usage records for billing and quota. **One stream at a time.** The encrypted endpoint allows one active stream for each API key. A second stream on the same key can return `429`. Follow the `Retry-After` header when the response has one. See [Agents & Automation](/agents). ## Troubleshooting Check that the app adds `/v1/messages`. In `both` mode, check the `/anthropic` prefix. List your models with `/v1/models`. Use an enabled Prem model ID, or map the `claude` name. Send the Prem API key with `x-api-key` or `Authorization: Bearer`. Never send the KEK. The count route returns an estimate. Use Prem usage records for billing and quota. The adapter does not support every Anthropic feature. For example, it ignores `top_k`. ## Frequently asked questions ### Does this run a Claude model? No. The request uses an Anthropic-compatible shape. The Prem model you choose runs the request. ### Why does the proxy translate the request? Prem's enclave uses an OpenAI-style chat format. The proxy translates your Messages request before it seals and sends it. ### Are token counts exact? No. The count route estimates tokens from the text and tool definitions. ### Are tool calls private? The model request and reply use the sealed path. Your app runs the tool outside the enclave. ### Can one proxy serve OpenAI and Anthropic apps? Yes. Use `--compat both` with the `/openai` and `/anthropic` prefixes. ## Related Review every mode, route, key, and daemon option. Apply the Anthropic surface to the Microsoft 365 add-ins. Use the Chat Completions interface through the same proxy. Review retries, concurrency, reasoning, and tool controls. Use the verified launcher for Claude Code. Review current behavior and assurance gaps. # Audio Transcription Source: https://docs.prem.io/guides/audio-transcription Transcribe audio files to text over the encrypted API, step by step. The current catalogue exposes one audio-transcription model: `deepgram/general-nova-3`. Its response uses Deepgram-style `metadata` and `results` fields. ## Basic audio transcription ```typescript theme={"system"} import { createReadStream } from "node:fs"; import { createRvencClient } from "@premai/api-sdk"; async function main() { const apiKey = process.env.PREM_API_KEY; const clientKEK = process.env.CLIENT_KEK; if (!apiKey || !clientKEK) { throw new Error("Set PREM_API_KEY and CLIENT_KEK"); } const client = await createRvencClient({ apiKey, clientKEK }); const transcription = await client.audio.transcriptions.create({ file: createReadStream("./audio.wav"), model: "deepgram/general-nova-3", smart_format: true, }); const alternative = transcription.results?.channels?.[0]?.alternatives?.[0]; console.log(alternative?.transcript ?? ""); } main().catch((error) => { console.error(error); process.exitCode = 1; }); ``` For this model, use `diarize` and `smart_format`. The SDK does not send Whisper-specific fields such as `language`, `prompt`, `response_format`, or `timestamp_granularities` when the model ID starts with `deepgram/`. ## Response shape The response contains: * `metadata.request_id`, duration, channel count, and resolved Deepgram model information; * `results.channels[].alternatives[].transcript`; * confidence and word timing data when the backend returns them. Do not read `transcription.text` for `deepgram/general-nova-3`; that field is not part of the current response. ## Proxy request Start the OpenAI-compatible proxy with an explicit KEK: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --compat openai \ --kek "$CLIENT_KEK" ``` Then send multipart audio: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/audio/transcriptions \ -H "Authorization: Bearer $PREM_API_KEY" \ -F "file=@audio.wav" \ -F "model=deepgram/general-nova-3" ``` The proxy currently returns the same Deepgram-style JSON object. It does not normalize it into OpenAI's `{ "text": "..." }` shape. ## Current limits * This route is non-streaming. * The proxy accepts files up to 25 MB. * `openai/whisper-large-v3` is not a documented runnable model for this route. * Verify a production audio format with your own representative fixture before rollout. # Audio Translation Source: https://docs.prem.io/guides/audio-translation Check whether an audio-translation model is enabled before implementing the route. No enabled audio-translation model is currently documented. This page intentionally does not present a runnable model ID. `@premai/api-sdk` `1.0.59` and the OpenAI-compatible proxy contain an audio-translation method and `/v1/audio/translations` route. A route existing in the package is not sufficient to make a translation example operational: the account must also receive an enabled translation-capable model from the model catalogue. The August 4 catalogue did not return `openai/whisper-large-v3`. Do not use it as a runnable example. ## Check availability List the live models and filter by type before implementing translation: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/models \ -H "Authorization: Bearer $PREM_API_KEY" ``` A runnable translation example requires an enabled audio-translation model, a documented response contract, and a successful authenticated test with a representative audio file. No model currently meets those conditions in this guide. For the currently enabled transcription path, see [Audio Transcription](/guides/audio-transcription). # Chat Completion Source: https://docs.prem.io/guides/chat-completion Create encrypted chat completions with text, vision, and streaming, step by step. ## Text Completion Create a simple chat completion with text: ```typescript theme={"system"} import createRvencClient from "@premai/api-sdk"; const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK }); const response = await client.chat.completions.create({ model: "glm-5.2", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "Explain quantum computing in simple terms" } ], }); console.log(response.choices[0].message.content); ``` ## Image Analysis Analyze an image and get a detailed description: ```typescript theme={"system"} const response = await client.chat.completions.create({ model: "qwen36-27b", messages: [{ role: "user", content: [ { type: "text", text: "Describe this image in detail" }, { type: "image_url", image_url: { url: "https://docs.prem.io/docs/images/examples/basic.jpg", }, }, ], }], }); console.log(response.choices[0].message.content); ``` ## Document & Chart Intelligence with JSON Output Extract structured data from charts, invoices, or reports: ```typescript theme={"system"} const response = await client.chat.completions.create({ model: "qwen36-27b", messages: [{ role: "user", content: [ { type: "text", text: `Analyze this sales chart and return the data in JSON format: { "chart_type": "...", "key_findings": ["...", "..."], "data_points": [{"label": "...", "value": "..."}], "trends": "..." }` }, { type: "image_url", image_url: { url: "https://docs.prem.io/docs/images/examples/sales-chart.png", }, }, ], }], response_format: { type: "json_object" }, }); const data = JSON.parse(response.choices[0].message.content || "{}"); console.log(data); ``` ## Receipt & Invoice Processing Extract structured financial information from receipts: ```typescript theme={"system"} const response = await client.chat.completions.create({ model: "qwen36-27b", messages: [{ role: "user", content: [ { type: "text", text: `Extract all information from this receipt and format as JSON: { "merchant_name": "...", "date": "YYYY-MM-DD", "items": [{"name": "...", "quantity": 1, "price": 0.00}], "subtotal": 0.00, "tax": 0.00, "total": 0.00, "payment_method": "..." }` }, { type: "image_url", image_url: { url: "https://docs.prem.io/docs/images/examples/invoice.png" }, }, ], }], response_format: { type: "json_object" }, }); const data = JSON.parse(response.choices[0].message.content || "{}"); console.log(data); ``` ## Product Catalog Automation Generate structured product information from images: ```typescript theme={"system"} const response = await client.chat.completions.create({ model: "qwen36-27b", messages: [{ role: "user", content: [ { type: "text", text: `Analyze this product image and generate catalog data in JSON: { "product_category": "...", "attributes": {"color": "...", "material": "...", "style": "..."}, "target_audience": "...", "suggested_tags": ["...", "..."], "description": "..." }` }, { type: "image_url", image_url: { url: "https://docs.prem.io/docs/images/examples/tshirt.png" }, }, ], }], response_format: { type: "json_object" }, }); const data = JSON.parse(response.choices[0].message.content || "{}"); console.log(data); ``` ## Image-Based Question Answering Ask specific questions about an image: ```typescript theme={"system"} import fs from "fs"; const imagePath = "./scene.jpg"; const base64Image = fs.readFileSync(imagePath, "base64"); const response = await client.chat.completions.create({ model: "qwen36-27b", messages: [{ role: "user", content: [ { type: "text", text: "How many people are in this image and what are they doing?" }, { type: "image_url", image_url: { url: `data:image/jpeg;base64,${base64Image}` }, }, ], }], }); console.log(response.choices[0].message.content); ``` ## Multi-Image Comparison Analysis Compare multiple images for quality control or change detection: ```typescript theme={"system"} const response = await client.chat.completions.create({ model: "qwen36-27b", messages: [{ role: "user", content: [ { type: "text", text: "Compare these before/after images and identify all changes." }, { type: "image_url", image_url: { url: "https://docs.prem.io/docs/images/examples/before.png" }, }, { type: "image_url", image_url: { url: "https://docs.prem.io/docs/images/examples/after.png" }, }, ], }], }); console.log(response.choices[0].message.content); ``` ## Streaming Vision Analysis Stream the response to make creative content in real time: ```typescript theme={"system"} const stream = await client.chat.completions.create({ model: "qwen36-27b", messages: [{ role: "user", content: [ { type: "text", text: "Create an engaging social media caption for this image" }, { type: "image_url", image_url: { url: "https://example.com/image.jpg" }, }, ], }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } ``` # Claude Code Source: https://docs.prem.io/guides/claude-code Launch Claude Code through Prem's Confidential Proxy with the bundled confidential-claude wrapper. The `@premai/api-sdk` package includes `confidential-claude`, a launcher that starts the Confidential Proxy in Anthropic mode and then runs the installed Claude Code CLI against it. This protects supported model traffic after the local proxy encrypts it. Claude Code, repository files, shell commands, MCP servers, hooks, and tool results remain on your machine or in their own external systems. Use `confidential-claude` to connect Claude Code through the local Confidential Proxy. [Go to the confidential setup](#before-you-start). Beta Router is not available for Claude Code. Claude Code requires the Anthropic Messages API, while Router supports OpenAI-compatible Chat Completions. Use [OpenCode](/guides/opencode), [OpenClaw](/guides/openclaw), [Hermes Agent](/guides/hermes), or [Goose](/guides/goose) for a Router coding workflow. ## How the launcher works ```mermaid theme={"system"} flowchart TB subgraph Local["Your machine: plaintext is available"] C["Claude Code"] -->|"Anthropic Messages API"| P["Confidential Proxy
127.0.0.1:8787"] T["Local tools and MCP servers"] <--> C end P -->|"Encrypt before network egress"| G["Prem API Gateway
ciphertext and metadata"] G -->|"Encrypted request"| E["Prem confidential runtime
decrypt, infer, encrypt"] E -.->|"Encrypted response"| P P -.->|"Anthropic SSE or message"| C ``` The launcher performs these steps on every run: 1. Requires an interactive terminal (TTY). 2. Reads the endpoint, key, and KEK configuration. 3. Starts or reuses the proxy on port `8787` in Anthropic mode with attestation enabled. 4. Displays the list of your enabled models. 5. Sets Claude Code's Anthropic base URL and model environment variables. 6. Forwards every remaining argument to the installed `claude` command. ## Before you start You need: * Claude Code installed and available as `claude` on `PATH`. * A Prem API key. * A 32-byte KEK encoded as 64 hexadecimal characters. * A terminal with interactive input. * `@premai/api-sdk`. Check the installed Claude Code version: ```bash theme={"system"} claude --version ``` ## 1. Set the launcher values ```bash theme={"system"} export API_KEY="your-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Generate the KEK once if you do not have one: ```bash theme={"system"} openssl rand -hex 32 ``` `confidential-claude` reads `API_KEY`, not `PREM_API_KEY`. Supplying it through the environment also prevents the launcher from prompting for and saving the API key in its application-data `.env` file. The launcher still saves the selected model ID there. ## 2. Run the first request Use `--print` for a bounded first test: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-claude \ --print "Reply with exactly OK. Do not use tools." ``` Choose an enabled model in the picker with the arrow keys or `j` and `k`, then press Enter. `--print` still requires a TTY because the launcher always displays the model list before it starts Claude Code. ## 3. Start an interactive session ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-claude ``` Claude Code uses the Anthropic-compatible Messages route and receives Server-Sent Events while it works. ## Model selection The picker calls the model-list endpoint on every run. This guide documents these chat models: | Picker ID | Display name | Current catalogue behavior | | ------------ | ------------ | -------------------------------------------------- | | `qwen36-27b` | Qwen 3.6 | Text, image, and video input metadata | | `glm-5.2` | GLM 5.2 | Text chat alias that resolves to `zai-org/GLM-5.2` | The Anthropic adapter returns the requested alias in its message `model` field. It does not expose the backend-resolved model ID or an assurance profile. Do not infer an assurance class from the picker label. See [Platform Status](/platform-status). ## Stop the local proxy The launcher starts a detached proxy and does not stop it when Claude Code exits. Stop it explicitly when you finish: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy stop ``` Check the status before another run when port `8787` appears occupied: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy status ``` ## Plaintext and tool boundary | Component | Plaintext access | Notes | | ------------------------------ | --------------------------------- | ------------------------------------------------------------------ | | Claude Code | Yes | Reads prompts, selected repository files, and tool results locally | | Confidential Proxy | Yes, locally | Translates, encrypts, and decrypts model traffic | | Prem API Gateway | No content access | Receives ciphertext, authentication, and routing metadata | | Prem confidential runtime | Yes, inside the protected runtime | Runs the selected model and encrypts the response | | Shell, hooks, MCP, and plugins | Depends on the tool | Outside the Prem inference boundary | Review Claude Code permissions, hooks, MCP servers, and egress separately. A confidential model route does not make local tool execution confidential. ## Troubleshooting Run the command directly in an interactive terminal. Piping input or starting it from a non-interactive CI process does not provide the interactive terminal that model selection needs. Install Claude Code and confirm that `claude --version` succeeds in the same shell. Check `API_KEY`, `PROXY_URL`, `ENCLAVE_URL`, network access, and proxy logs. Confirm that the key can list models. Run `confidential-proxy status`, then stop the managed proxy. The launcher refuses to take over an unknown process on that port. Do not add `--no-attest` to work around the failure. Capture the error and verify the current Reticle boundary in [Attestation](/attestation). The wrapper supplies an Anthropic-compatible API shape. It does not turn a Prem model into an Anthropic-hosted Claude model. ## Frequently asked questions ### Does `confidential-claude` install Claude Code? No. It checks for an existing `claude` command and exits if Claude Code is not installed. ### Does it use my Anthropic API key? No. `API_KEY` is the Prem API key. The launcher removes `ANTHROPIC_API_KEY` and passes the Prem key to Claude Code as the gateway authentication token. ### Does it remember the model? It writes the selected model to its application-data `.env` file, but the current launcher still shows the model list on each run. ### Can I pass normal Claude Code arguments? Yes. The launcher forwards its arguments to `claude`, including `--print`. ### Is the full coding session inside an enclave? No. Only supported inference traffic uses the encrypted Prem path. Files and tools remain in their own local or external trust boundaries. ## Related Review the Messages translation and compatibility limits. Review proxy modes, routes, keys, and daemon controls. Review current behavior and assurance gaps. Review the protected and unprotected parts of the data flow. # Claude for Microsoft 365 Source: https://docs.prem.io/guides/claude-microsoft-365 Connect the Claude add-ins for Excel, PowerPoint, Word, and Outlook to Prem API. The Claude add-ins for Microsoft 365 can send inference to Prem. Prem runs the model inside a confidential enclave. The add-ins keep their normal look and workflow. Your organization controls the connection. This guide helps you set up that connection. It uses the **LLM gateway** path from Anthropic. Prem provides the gateway software: the [Confidential Proxy](/confidential-proxy). A Microsoft 365 tenant has not been tested end to end with this setup. Complete the acceptance tests before production rollout. Anthropic documents four connection paths for the Office add-ins: LLM gateway, Bedrock direct, Vertex AI direct, and Foundry direct. This guide covers the **LLM gateway** path only. Read the [Anthropic third-party platforms guide](https://claude.com/docs/office-agents/third-party-platforms) first. It is the source of truth for tenant setup, network allowlists, and feature support. ## Who this guide is for Two roles complete this setup: * **An IT administrator** deploys the add-in and runs the gateway. Sections 1 to 8 are for this role. * **An end user** connects the add-in in their Office application. The [End-user connection](#end-user-connection) section is for this role. You can do both roles yourself for a small pilot. ## What you set up The add-in talks to a gateway that you run. The gateway is the Prem Confidential Proxy. The proxy encrypts each request before it leaves your network. The Prem enclave decrypts the request, runs the model, and encrypts the response. ```mermaid theme={"system"} flowchart TB subgraph M365["Microsoft 365 and user device"] D["Workbook, presentation, document, or email"] --> A["Claude add-in task pane
plaintext"] end A -->|"HTTPS, Anthropic Messages API"| P["Your gateway host
Confidential Proxy"] P -->|"Encrypt before Prem network egress"| G["Prem API Gateway
ciphertext and metadata"] G -->|"Encrypted request"| E["Prem API Enclave
decrypt, infer, encrypt"] E -.->|"Encrypted response"| P P -.->|"Anthropic-style response"| A A -.->|"Office.js action"| D ``` The add-in and the proxy host read plaintext. The Prem API Gateway sees only ciphertext. The Prem Enclave reads plaintext inside its Trusted Execution Environment (TEE). ## What this integration does Users open the Claude task pane in these applications: * Excel * PowerPoint * Word * Outlook The add-in sends its model requests to your Prem gateway. Prem runs the model with confidential inference. ## What this integration does not do This setup does **not** do these things: * It does not set up Microsoft Copilot. * It does not protect the full Microsoft 365 workflow. It protects the model request only. * It does not make a Prem model behave the same as a Claude model. * It does not protect Microsoft Graph calls, connectors, or local add-in storage. * It does not enable work across applications. Anthropic lists this feature as unavailable through third-party platforms. Check the current [feature support table](https://claude.com/docs/office-agents/third-party-platforms) before each rollout. The list changes. ## Before you start Get these items ready: * Microsoft 365 with Microsoft Entra ID for admin consent. * Claude for Excel, PowerPoint, Word, or Outlook, installed from Microsoft AppSource or by admin deployment. * A gateway hostname that every approved device can reach. * A TLS certificate that those devices trust. For a local pilot on macOS, you create one with [mkcert](https://github.com/FiloSottile/mkcert). * [Docker Desktop](https://www.docker.com/products/docker-desktop/) for a local pilot on macOS, or Node.js for a server. * A dedicated Prem API key. * A 32-byte KEK encoded as 64 hexadecimal characters. * The current Prem gateway and enclave endpoints. * A Prem model that supports your workflow. For Outlook, a Global Administrator grants Microsoft Graph consent one time. The Graph token stays in the user's Outlook client. It never reaches your gateway or Prem. See the [Anthropic Outlook guide](https://claude.com/docs/office-agents/third-party-platforms) for the consent step. ## 1. Choose the gateway hostname The add-in cannot use `localhost` or `127.0.0.1` on macOS. So the gateway needs a real hostname. * Choose a hostname, for example `prem-office-gateway.example.com`. * Point it at the gateway host's network IP address, not `127.0.0.1`. * Add the hostname to your local DNS, or to the `/etc/hosts` file on each approved device. * Confirm that every approved device can resolve the hostname. Use the same hostname everywhere. It must match the certificate and the URL that users enter. ## 2. Create a trusted certificate The task pane runs in a browser. It requires an HTTPS gateway with a trusted certificate. For a local pilot on macOS, create the certificate with mkcert: ```bash theme={"system"} brew install mkcert mkcert -install mkcert prem-office-gateway.example.com ``` `mkcert -install` adds a local certificate authority to the Mac's trust store. `mkcert` then writes two files in the current folder: ```text theme={"system"} prem-office-gateway.example.com.pem # the certificate prem-office-gateway.example.com-key.pem # the private key ``` To trust the certificate on other devices, install the mkcert root certificate on each one. Find it with `mkcert -CAROOT`. For a wider rollout, use an organization-issued or public certificate instead. Keep the private key safe. Do not commit it to a repository. ## 3. Set the gateway secrets Set these values on the gateway host: ```bash theme={"system"} export PREM_API_KEY="your-dedicated-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Store the API key, KEK, certificate key, and endpoints in a secret manager. Do not commit them to a repository. ## 4. Start the gateway The gateway software is the `confidential-proxy` command in the [`@premai/api-sdk`](https://www.npmjs.com/package/@premai/api-sdk) npm package. The package is an end-to-end encrypted client that speaks both the OpenAI and the Anthropic API formats. **Local pilot on macOS (Docker):** run the proxy in a container. Run this command from the folder that holds your certificate files: ```bash theme={"system"} docker run --rm -p 8787:8787 \ -e PREM_API_KEY -e CLIENT_KEK -e PROXY_URL -e ENCLAVE_URL \ -v "$(pwd):/tls:ro" \ node:22-alpine \ npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 0.0.0.0 --port 8787 \ --compat anthropic \ --kek "$CLIENT_KEK" \ --default-model glm-5.2 \ --cors-origin https://pivot.claude.ai \ --tls \ --tls-cert /tls/prem-office-gateway.example.com.pem \ --tls-key /tls/prem-office-gateway.example.com-key.pem ``` **Server (Node.js):** run the proxy directly with npx: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 0.0.0.0 --port 8787 \ --compat anthropic \ --kek "$CLIENT_KEK" \ --default-model glm-5.2 \ --cors-origin https://pivot.claude.ai \ --tls \ --tls-cert /path/to/cert.pem \ --tls-key /path/to/key.pem ``` The proxy now serves the add-in at `https://prem-office-gateway.example.com:8787`. The options mean: * `--compat anthropic` makes the proxy speak the Anthropic Messages API. The add-in uses this format. * `--default-model` is built for the Claude for Microsoft 365 integration. It replaces the literal `claude` request value with a Prem model. Change `glm-5.2` to the Prem model you want. * `--cors-origin` lets the task pane read the responses. The task pane loads from `https://pivot.claude.ai`. Do not use `--cors-origin *`. CORS is not authentication. An exact origin limits unwanted browser access. ## 5. Restrict network access Do not expose an unauthenticated proxy to the public internet. Allow inbound traffic to port `8787` only from approved networks or your reverse proxy. Choose a topology that fits the rollout: | Topology | Use it for | Required controls | | -------------------------- | ------------------------------------ | ---------------------------------------------------------- | | Dedicated LAN host | A small pilot on one managed network | Internal DNS, trusted TLS, firewall, dedicated key | | Private VM or container | A managed organization deployment | Private routing, health checks, key storage, updates | | Internet-reachable gateway | Users without private network access | Reverse proxy, strict authentication, firewall, monitoring | For an internet-reachable deployment, put an authenticated gateway in front of the proxy. Apply request limits, authentication, and alerts there. ## 6. Deploy the add-in Anthropic provides a setup wizard for tenant deployment. The wizard generates the add-in manifest and handles admin consent. Install the plugin in your shell: ```bash theme={"system"} claude plugin marketplace add anthropics/financial-services claude plugin install claude-for-msft-365-install@claude-for-financial-services ``` Run the wizard inside Claude: ```text theme={"system"} /claude-for-msft-365-install:setup ``` Choose the **LLM gateway** path when the wizard asks. Give it these values: * **Gateway URL:** the base URL of your proxy, for example `https://prem-office-gateway.example.com:8787`. * **Gateway token:** the dedicated Prem API key. The wizard writes a `manifest.xml` file. For Outlook, it also writes `manifest-outlook.xml`. Upload each manifest in the Microsoft 365 Admin Center. Open **Settings → Integrated apps → Upload custom apps**. Assign the add-in to a pilot group first. Widen the assignment after the pilot passes. By default, the add-in sends the token in the `x-api-key` header. To use `Authorization: Bearer` instead, set `gateway_auth_header: authorization` in the manifest. See the [Anthropic gateway requirements](https://claude.com/docs/office-agents/third-party-platforms) for all manifest options. ## End-user connection Give these steps to each user. They connect the add-in one time. 1. Open Excel, PowerPoint, Word, or Outlook. 2. Launch the Claude add-in. 3. On the sign-in screen, select **Cloud provider or gateway**. 4. Select **Gateway**. 5. Enter the gateway URL, for example `https://prem-office-gateway.example.com:8787`. 6. Enter the Prem API key as the token. 7. Select **Connect**. Enter the base URL only. Do not add `/v1/messages` to the URL. The add-in stores the token in the browser's local storage, inside its sandboxed frame. To change the token later, open **Settings** in the add-in sidebar, enter the new value, and test the connection. ## Validate the rollout Test each approved application on its own. Use non-sensitive test files first. 1. Open a small range, slide, paragraph, or email. 2. Ask for a short model response. 3. Apply one reversible edit through the add-in. 4. Confirm that streaming finishes. 5. Confirm that the Prem usage record appears. 6. Confirm that an invalid token returns `401`. 7. Confirm that an unapproved user or network cannot reach the gateway. 8. Confirm that a certificate error blocks the connection. Do not widen the pilot until every test passes. ## Responsibility boundaries | Layer | Owner | Responsibility | | --------------------- | ---------------------------------- | ------------------------------------------------------- | | Add-in deployment | Microsoft 365 administrator | Assign add-ins, users, groups, and permissions | | Identity and consent | Microsoft Entra ID administrator | Approve sign-in and Microsoft Graph permissions | | Office content access | Add-in and Microsoft 365 | Read or change selected content | | Gateway host | Your organization | Run TLS, CORS, proxy, firewall, keys, and monitoring | | Confidential Proxy | Your organization and Prem package | Translate, encrypt, decrypt, and return traffic | | Prem API Gateway | Prem | Authenticate, limit, bill, and route encrypted payloads | | Prem API Enclave | Prem | Decrypt, run inference, and encrypt the response | ## Security boundary | Component | Reads plaintext | Note | | ----------------------------- | ---------------------- | ------------------------------------------------------- | | Microsoft 365 application | Yes | Holds the source document or message | | Claude task pane | Yes | Reads selected content and stores the token locally | | Microsoft Entra ID and Graph | Depends on tenant | Outside the Prem inference boundary | | Confidential Proxy host | Yes | Translates, encrypts, decrypts, and holds local secrets | | Prem API Gateway | No | Processes ciphertext, authentication, and metadata | | Prem API Enclave | Yes, inside the TEE | Runs the selected Prem model | | Connectors and external tools | Depends on the feature | Need a separate data-flow review | Anthropic states that prompts and responses go only to your gateway. The add-in loads its interface and telemetry from Anthropic and Microsoft domains. That traffic does not carry prompts or responses. The add-in stores its token in browser `localStorage`, inside the sandboxed task pane. The current Prem proxy uses that token as the Prem API key. Treat each device and browser profile as a key-bearing asset. For short-lived user tokens, run a separate authenticated gateway in front of the proxy. ## Operational controls Apply these controls for a managed rollout: * Pin the `@premai/api-sdk` package version. * Run the proxy as a supervised service. * Monitor certificate expiry and endpoint health. * Rotate the Prem API key and KEK under a written procedure. * Turn off debug logs in production unless support needs them. * Keep document content out of support bundles. * Record add-in assignments, gateway changes, and key rotations. * Test rollback before each package or manifest update. ## Troubleshooting Check DNS, firewall rules, gateway health, and TLS trust. Confirm the base URL. Do not include `/v1/messages`. Confirm the response includes the allowed origin on `OPTIONS`, `GET`, `POST`, and error responses. Test `GET /v1/models` with the same token. Confirm the Prem API key can list an enabled model. Send `claude` as the model, or send a supported Prem model ID. The default substitution matches only `claude`. Update the token in the add-in Settings. Test the connection again. Confirm that firewalls and reverse proxies do not buffer Server-Sent Events. Test the direct gateway path. ## Frequently asked questions ### Is this Microsoft Copilot? No. This connects Anthropic's Claude add-ins for Microsoft 365 to the Prem gateway. ### Does Prem run a Claude model in this setup? Not always. The add-in uses the Anthropic API format. The proxy maps the `claude` placeholder to a Prem model. ### Why can the add-in not use localhost on macOS? The add-in runs in a sandboxed browser frame. It cannot use the loopback path. Use a reachable gateway host with trusted TLS. ### Does CORS protect the API key? No. CORS controls which browser origin can read responses. Authentication, TLS, device controls, and firewall rules protect access. ### Where does the add-in store the token? The add-in stores the token in browser `localStorage`, inside its sandboxed task pane. Treat the device and browser profile as key-bearing assets. ### Does this protect the full document lifecycle? No. It protects the model request after the proxy encrypts it. Microsoft 365 and local add-in processing stay separate. ### Does work across Microsoft 365 applications work through this gateway? No. Anthropic lists this feature as unavailable through third-party platforms. Check the limitation before each rollout. ## Related Read Anthropic's source guide for tenant setup and feature support. Understand the Messages adapter and its compatibility limits. Review every proxy option and daemon control. Review the protected and unprotected parts of the data path. Complete key, attestation, reliability, and support checks. Review current behavior and the untested tenant boundary. # Codex compatibility Source: https://docs.prem.io/guides/codex-cli Understand why Codex cannot currently connect to Prem API. Codex is not compatible with Prem API today. Codex custom providers use the OpenAI Responses API. Prem API does not expose `/v1/responses`, so changing `base_url` is not enough. ## Use a supported coding harness * Use [OpenCode](/guides/opencode) for an OpenAI-compatible coding agent. * Use [Claude Code](/guides/claude-code) through Prem's Anthropic-compatible launcher. Codex support requires a verified Responses API adapter. See the [Codex configuration reference](https://developers.openai.com/codex/config-reference#configtoml) for its current provider protocol. # Cursor compatibility Source: https://docs.prem.io/guides/cursor Understand why Cursor's custom OpenAI base URL is not currently a supported confidential Prem integration. Cursor exposes an **Override OpenAI Base URL** control for OpenAI API requests. That control is not enough to create the same confidential path as OpenCode, Goose, or another local client. Cursor is not currently supported as a Prem confidential integration. Cursor's documentation states that requests pass through Cursor's servers for final prompt building and that the provider API key is sent through those servers with each request. ## Compatibility verdict | Requirement | Current result | | -------------------------------------- | ------------------------------------------------------ | | Custom OpenAI base URL control | Available in Cursor Settings under Models and API Keys | | Standard chat models with a custom key | Documented by Cursor | | Direct local call to `127.0.0.1:8787` | Not documented or verified | | Prompt hidden from Cursor's service | No | | Client-to-Prem-enclave confidentiality | Not established | | End-to-end Prem test | Not completed | The [Cursor API key documentation](https://cursor.com/help/models-and-usage/api-keys) says that custom keys work only with chat models. It also says that all requests are routed through Cursor's servers for final prompt building. The key is transmitted to Cursor's backend for each request. ## Why the base URL is insufficient ```mermaid theme={"system"} flowchart TB subgraph Device["Your device"] C["Cursor
prompt, files, and tool context"] L["Local Confidential Proxy
127.0.0.1:8787"] end C -->|"Prompt and provider key"| S["Cursor services
final prompt building"] S -.->|"Custom OpenAI endpoint, if reachable"| R["Remote endpoint or deployed proxy"] L -.->|"Not a documented direct path"| S R -->|"Encrypted only after the proxy receives plaintext"| G["Prem API Gateway"] G --> E["Prem confidential runtime"] ``` The confidentiality problem occurs before Prem receives the request. Cursor's service can process the prompt while it builds the final request. A Prem proxy deployed after that service can protect the later Prem inference path, but it cannot make the earlier Cursor processing confidential. A loopback URL introduces a second problem. `127.0.0.1` refers to the host making the connection. Cursor does not document its custom provider flow as a direct local request from the editor to that address. Do not assume that the local proxy is reachable from Cursor's backend. ## What the current controls establish Cursor can use custom provider credentials for supported chat models. The installed Cursor settings also expose a base URL override for OpenAI requests. These facts do **not** establish any of the following: * that Cursor sends the final request directly from the device to the local Confidential Proxy; * that Cursor cannot read the prompt, file context, tool output, or provider key; * that Cursor uses only `/v1/chat/completions` for every agent feature; * that Cursor Tab, Cloud Agents, Composer, or other Cursor-hosted features use the custom endpoint; * that an agent turn, stream, and tool call work end to end through Prem. ## Use a supported local harness Use a client that sends Chat Completions directly to the local proxy when the client-to-enclave boundary is required: * [OpenCode](/guides/opencode) for a terminal coding agent. * [Goose](/guides/goose) for a local agent with built-in and MCP tools. * [OpenClaw](/guides/openclaw) or [Hermes Agent](/guides/hermes) for broader local automation. * [Claude Code](/guides/claude-code) through Prem's bundled Anthropic-compatible launcher. The Codex extension can run inside Cursor as an editor extension, but that is a separate client and provider path. See [Codex CLI compatibility](/guides/codex-cli) before assuming it can use Prem. ## Requirements for confidential support A confidential Cursor integration would require all of these conditions: 1. Cursor documents a direct client-side provider path or a local extension path that does not send plaintext to Cursor's service. 2. The path can target `http://127.0.0.1:8787/v1` and uses `/v1/chat/completions`. 3. A bounded text request reaches the local Confidential Proxy. 4. Streaming and tool calls complete without direct-provider fallback. 5. Network inspection confirms that prompt content and the Prem API key do not transit Cursor's service. 6. Cursor-hosted features that bypass the custom provider are clearly excluded from the guide. ## Frequently asked questions ### Does Cursor allow an OpenAI base URL override? Yes. The current Models settings expose that control. It does not establish a direct or confidential local path. ### Can I point the override at the Prem proxy anyway? No, not as a confidential deployment. Cursor's documented backend processing keeps Cursor in the plaintext trust boundary, and a loopback proxy is not a documented backend-reachable endpoint. ### Would a publicly reachable proxy solve this? It may solve network reachability, but it does not remove Cursor from the plaintext path. You would also need TLS, strict network access, and controlled secret handling on the deployed proxy. ### Does Prem protect Cursor tools and files? No. Even in a future model integration, Cursor, editor files, terminal commands, extensions, and remote tools remain outside the Prem confidential runtime. ## Related Read Cursor's current provider-key and backend-routing boundary. Use a tested local coding-agent path through Prem. Review supported routes, modes, and key handling. Review the protected and unprotected parts of the request path. # Goose Source: https://docs.prem.io/guides/goose Connect Goose to Prem API through the local Confidential Proxy. Goose has a built-in OpenAI provider that accepts a custom host and Chat Completions path. Point it at Prem's local Confidential Proxy and select a Prem model ID. Goose and its extensions run on your machine. Prem protects supported model traffic after the local proxy encrypts it. File, shell, browser, MCP, and extension execution remain outside the Prem confidential runtime. Use the local Confidential Proxy for sensitive model traffic. [Go to the confidential setup](#confidential-api). Beta Use Router for its broader model catalog with non-sensitive data. [Go to the Router setup](#router). ## Confidential API ## How the connection works ```mermaid theme={"system"} flowchart TB subgraph Local["Your machine: plaintext is available"] O["Goose"] -->|"OpenAI Chat Completions"| P["Confidential Proxy
127.0.0.1:8787/v1"] T["Built-in tools and MCP extensions"] <--> O end P -->|"Encrypt before network egress"| G["Prem API Gateway
ciphertext and metadata"] G -->|"Encrypted request"| E["Prem confidential runtime
decrypt, infer, encrypt"] E -.->|"Encrypted response"| P P -.->|"OpenAI-compatible response"| O ``` ## Before you start You need: * Goose installed. See the [official installation guide](https://goose-docs.ai/docs/getting-started/installation/). * A Prem API key. See [API Keys](/api-keys). * A 32-byte client KEK encoded as 64 hexadecimal characters. * The current Prem gateway and enclave endpoints. * A chat model returned by your Prem model-list request. Check the installed version: ```bash theme={"system"} goose --version ``` The commands below have been exercised with Goose `1.45.0` using a normal response and a local shell-tool call. ## 1. Set your secrets ```bash theme={"system"} export PREM_API_KEY="your-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Generate the KEK once if you do not have one: ```bash theme={"system"} openssl rand -hex 32 ``` Store both secrets outside the repository. Do not place the API key in Goose's `config.yaml`. ## 2. Start the Confidential Proxy ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 127.0.0.1 \ --port 8787 \ --compat openai \ --kek "$CLIENT_KEK" ``` Do not use `--no-attest`. Keep attestation enabled for confidential inference. ## 3. Select an enabled model ```bash theme={"system"} curl http://127.0.0.1:8787/v1/models \ -H "Authorization: Bearer $PREM_API_KEY" ``` Use a returned `id`. This example uses `qwen36-27b`: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/chat/completions \ -H "Authorization: Bearer $PREM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen36-27b", "messages": [{"role": "user", "content": "Reply with OK."}], "stream": false }' ``` ## 4. Configure Goose Set Goose's OpenAI provider variables in the shell that starts it: ```bash theme={"system"} export GOOSE_PROVIDER="openai" export GOOSE_MODEL="qwen36-27b" export OPENAI_API_KEY="$PREM_API_KEY" export OPENAI_HOST="http://127.0.0.1:8787" export OPENAI_BASE_PATH="v1/chat/completions" ``` `OPENAI_HOST` contains the origin. `OPENAI_BASE_PATH` contains the Chat Completions path without a leading slash. A wrong path usually returns `404`. Goose documents this pattern under [custom OpenAI endpoints](https://goose-docs.ai/docs/getting-started/providers/#using-custom-openai-endpoints). The API key remains in the process environment instead of `~/.config/goose/config.yaml`. ## 5. Run Goose Run one chat-only request first: ```bash theme={"system"} GOOSE_MODE=chat goose run \ --no-session \ --text "Reply with exactly OK. Do not use tools." ``` Then test a tool-capable session in a controlled workspace: ```bash theme={"system"} GOOSE_MODE=approve goose run \ --with-builtin developer \ --text "Read README.md and summarize its first heading." ``` Use `approve` while evaluating commands. Goose can stream model output through the Chat Completions connection. A model-produced tool call returns through the encrypted model channel; Goose executes the tool locally. ## Tool and extension boundary | Component | Plaintext access | Notes | | ------------------------- | --------------------------------- | ---------------------------------------------------------------- | | Goose | Yes | Reads prompts, workspace files, instructions, and tool results | | Confidential Proxy | Yes, locally | Encrypts model requests and decrypts responses | | Prem API Gateway | No content access | Receives ciphertext and operational metadata | | Prem confidential runtime | Yes, inside the protected runtime | Runs inference and encrypts the result | | Built-in or MCP extension | Depends on the tool | Uses its own permissions, credentials, storage, and network path | Limit enabled extensions, review permission mode, and run Goose from the narrowest required workspace. ## Agent limits The encrypted chat endpoint permits one active stream for each API key. Concurrent Goose sessions, recipes, or subagents using the same key can receive `429` responses. Start with one session. Serialize model turns or use a separate Prem API key per concurrent worker. Do not switch to another provider if every model call must use Prem. ## Troubleshooting Set `OPENAI_HOST` to `http://127.0.0.1:8787` and `OPENAI_BASE_PATH` to `v1/chat/completions`. Do not put `/v1` in both values. Export `OPENAI_API_KEY="$PREM_API_KEY"` in the same process that starts Goose. Goose does not read provider API keys from `config.yaml`. Call `/v1/models`, then set `GOOSE_MODEL` to a returned ID. Check `GOOSE_MODE`, the enabled extension, and its permissions. Use `approve` during setup instead of bypassing tool confirmation. Wait for the active stream to finish. Reduce concurrent sessions, recipes, and subagents or use separate API keys. ## Frequently asked questions ### Does Goose need a custom provider plugin? No. Its built-in OpenAI provider supports a custom host and Chat Completions path. ### Is Goose itself inside the Prem enclave? No. Goose runs locally. Only supported model traffic uses the encrypted Prem path. ### Are extension calls confidential? Not automatically. The model call is protected through Prem, but each local or remote extension has its own trust boundary. ### Can I persist these settings? You can persist non-secret provider values in your shell or Goose configuration. Keep `OPENAI_API_KEY` in a secret manager or injected environment variable. ## Router Beta Router is not confidential. Use this path only for non-sensitive prompts, workspace context, and extension results. Goose uses Router through its built-in OpenAI provider and a separate API key. Confirm that `kimi-k3` appears in `GET /v1/models` for this key. Otherwise, use an exact returned model ID. See [Router models](/router/models). ```bash theme={"system"} export PREM_ROUTER_API_KEY="your-router-api-key" export GOOSE_PROVIDER="openai" export GOOSE_MODEL="kimi-k3" export OPENAI_API_KEY="$PREM_ROUTER_API_KEY" export OPENAI_HOST="https://router.prem.io" export OPENAI_BASE_PATH="v1/chat/completions" ``` Run a bounded request before enabling tools: ```bash theme={"system"} GOOSE_MODE=chat goose run \ --no-session \ --text "Reply with exactly: router ok" ``` This provider does not use the local proxy or KEK. See the [Router Goose configuration](/router/integrations#goose) and [Router models](/router/models). ## Related Review proxy modes, routes, keys, and daemon controls. Understand the protocol used by Goose. Review concurrency, retries, and unattended operation. Review the protected and unprotected parts of the flow. # Hermes Agent Source: https://docs.prem.io/guides/hermes Connect Hermes Agent to Prem API through the local Confidential Proxy. Hermes Agent supports named custom providers that use the OpenAI Chat Completions protocol. Point one of those providers at Prem's local Confidential Proxy. Do not configure Prem under Hermes' `openai-api` provider. That provider can select the OpenAI Responses API, which the Confidential Proxy does not expose. Use a named custom provider with `transport: chat_completions`. Use the local Confidential Proxy for sensitive model traffic. [Go to the confidential setup](#confidential-api). Beta Use Router for its broader model catalog with non-sensitive data. [Go to the Router setup](#router). ## Confidential API ## How the connection works ```mermaid theme={"system"} flowchart TB subgraph Local["Your machine: plaintext is available"] H["Hermes Agent"] -->|"Chat Completions"| P["Confidential Proxy
127.0.0.1:8787/v1"] T["Terminal, files, skills, and tools"] <--> H end P -->|"Encrypt before network egress"| G["Prem API Gateway
ciphertext and metadata"] G -->|"Encrypted request"| E["Prem confidential runtime
decrypt, infer, encrypt"] E -.->|"Encrypted response"| P P -.->|"OpenAI-compatible response"| H ``` ## Before you start You need: * Hermes Agent installed. See the [Hermes Agent repository](https://github.com/NousResearch/hermes-agent). * A Prem API key. See [API Keys](/api-keys). * A 32-byte client KEK encoded as 64 hexadecimal characters. * The current Prem gateway and enclave endpoints. * A chat model returned by the Prem model-list endpoint. Check the installed version: ```bash theme={"system"} hermes --version ``` The configuration below has been exercised with Hermes Agent `0.20.0` using a one-shot response and a local file-reading tool call. ## 1. Set your secrets ```bash theme={"system"} export PREM_API_KEY="your-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Generate the KEK once if you do not have one: ```bash theme={"system"} openssl rand -hex 32 ``` Keep `PREM_API_KEY` out of `config.yaml`. Hermes resolves it from the environment variable named by `key_env`. ## 2. Start the Confidential Proxy ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 127.0.0.1 \ --port 8787 \ --compat openai \ --kek "$CLIENT_KEK" ``` Do not use `--no-attest`. Keep attestation enabled for confidential inference. ## 3. Select an enabled model ```bash theme={"system"} curl http://127.0.0.1:8787/v1/models \ -H "Authorization: Bearer $PREM_API_KEY" ``` Use a returned `id`. This example uses `qwen36-27b`: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/chat/completions \ -H "Authorization: Bearer $PREM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen36-27b", "messages": [{"role": "user", "content": "Reply with OK."}], "stream": false }' ``` Do not continue until this request succeeds. ## 4. Configure Hermes Add this provider to `~/.hermes/config.yaml`: ```yaml theme={"system"} providers: prem-confidential: api: http://127.0.0.1:8787/v1 key_env: PREM_API_KEY transport: chat_completions default_model: qwen36-27b models: qwen36-27b: context_length: 131072 model: default: qwen36-27b provider: custom:prem-confidential context_length: 131072 ``` Replace the model ID and context length when you choose another enabled model. Keep `transport: chat_completions` explicit. The keyed `providers` format is Hermes' current custom-provider schema. See the [Hermes custom-provider documentation](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/integrations/providers.md#named-custom-providers). Check the configuration: ```bash theme={"system"} hermes config check ``` ## 5. Run Hermes Start with a bounded one-shot request: ```bash theme={"system"} hermes --oneshot "Reply with exactly OK. Do not use tools." ``` Then start an interactive session: ```bash theme={"system"} hermes chat ``` Hermes can stream responses and perform tool calls through this provider. The model request and tool-call response use the encrypted model channel. Hermes runs the selected tool on your machine, then sends any resulting context in a later encrypted model request. ## Tool, memory, and gateway boundary | Component | Plaintext access | Notes | | -------------------------------------------------- | --------------------------------- | -------------------------------------------------------------- | | Hermes Agent | Yes | Reads prompts, memory, rules, workspace data, and tool results | | Confidential Proxy | Yes, locally | Encrypts model requests and decrypts responses | | Prem API Gateway | No content access | Receives ciphertext and operational metadata | | Prem confidential runtime | Yes, inside the protected runtime | Runs inference and encrypts the result | | Shell, skills, messaging gateways, and MCP servers | Depends on the integration | Remain outside the Prem confidential boundary | Hermes can connect to messaging channels and run scheduled work. Review sender allowlists, approval rules, toolsets, hooks, and outbound network access before enabling those surfaces. ## Agent limits The encrypted chat endpoint permits one active stream for each API key. Parallel Hermes sessions, delegation, fallback attempts, or messaging workers that share one key can receive `429` responses. Begin with one session and no delegation. Serialize turns or use a separate Prem API key per concurrent worker. Do not configure a non-Prem fallback if every model turn must use Prem's encrypted path. ## Troubleshooting The session is using the wrong provider. Set `model.provider` to `custom:prem-confidential` and keep `transport: chat_completions` in the named provider. Export `PREM_API_KEY` in the process that starts Hermes. Confirm that `key_env` has the exact same name. Call `/v1/models`. Use a returned ID in `default_model`, `providers.prem-confidential.models`, and `model.default`. Limit Hermes toolsets, workspace access, approval settings, hooks, and MCP servers. Model-path confidentiality does not restrict local tool permissions. Wait for the active stream to finish. Reduce delegation and parallel workers or use distinct API keys. ## Frequently asked questions ### Is Hermes itself running inside the enclave? No. Hermes runs locally. The Prem confidential boundary starts after the local proxy encrypts a supported model request. ### Why not use `OPENAI_BASE_URL` with the built-in OpenAI provider? That route can use `/v1/responses`. The named custom provider fixes the protocol to `/v1/chat/completions`, which the Confidential Proxy supports. ### Can Hermes tools use the encrypted connection? The model can request a tool through the encrypted model channel. Hermes executes the tool outside the enclave. A tool's own network traffic does not pass through Prem unless you configure that tool separately. ### Can Hermes use fallback providers? Yes, but a non-Prem fallback leaves the Prem inference path. Omit those fallbacks when the encrypted route is a requirement. ## Router Beta Router is not confidential. Use this path only for non-sensitive prompts, memory, workspace context, and tool results. Hermes uses Router through a second named provider. ```bash theme={"system"} export PREM_ROUTER_API_KEY="your-router-api-key" ``` Confirm that `kimi-k3` appears in `GET /v1/models` for this key. Otherwise, use an exact returned model ID. See [Router models](/router/models). Use the `prem-router` configuration in the [Router Hermes configuration](/router/integrations#hermes-agent): | Field | Value | | ---------------- | --------------------------- | | `api` | `https://router.prem.io/v1` | | `key_env` | `PREM_ROUTER_API_KEY` | | `transport` | `chat_completions` | | `default_model` | `kimi-k3` | | `model.provider` | `custom:prem-router` | Run `hermes config check` after switching. Do not reuse `PREM_API_KEY` or the client KEK for Router. ## Related Review proxy modes, routes, keys, and daemon controls. Understand the protocol used by Hermes. Review concurrency, retries, and unattended operation. Review the protected and unprotected parts of the flow. # Loupe Source: https://docs.prem.io/guides/loupe Understand the current compatibility boundary between Loupe, Claude Code, and Prem API. [Loupe](https://loupe.build/) is a macOS application that creates AI workers with a browser, terminal, and Claude Code chat. Loupe is not a separate OpenAI- or Anthropic-compatible model client. Loupe is not currently supported as a Prem integration. Its public documentation does not expose a provider, base URL, or launch-environment setting for embedded Claude Code workers, so the worker cannot be connected to the local Confidential Proxy through a documented configuration. ## What Loupe adds Loupe combines three local surfaces for each worker: * a Claude Code chat; * a terminal; * a browser with Chrome DevTools MCP access. It can pass console and terminal errors to its Claude Code worker. This makes Loupe an orchestration and user-interface layer around Claude Code, not another inference protocol. ```mermaid theme={"system"} flowchart TB subgraph Loupe["Loupe on macOS"] W["Worker"] B["Browser and DevTools MCP"] T["Terminal"] C["Embedded Claude Code"] B --> W T --> W W --> C end C -.->|"Requires a configurable Anthropic endpoint"| P["Confidential Proxy
127.0.0.1:8787/v1"] P -->|"Encrypted model request"| G["Prem API Gateway"] G --> E["Prem confidential runtime"] ``` The dotted connection is conditional. It exists only when Loupe passes Prem's Claude Code endpoint and authentication settings to the embedded process. ## Before you start Separate two questions: 1. **Does Claude Code work through Prem?** Yes. Use the tested [Claude Code guide](/guides/claude-code). 2. **Does Loupe expose the settings needed to make its embedded Claude Code use Prem?** This is not documented by Loupe today. A direct Loupe integration requires all of the following: * a Loupe build that exposes the embedded Claude Code launch environment or command; * a way to set the Anthropic base URL, authentication token, and model for that worker; * a Prem API key and 64-character hexadecimal client KEK; * a successful model-list request, non-streaming turn, streaming turn, and tool call; * confirmation that the worker did not fall back to Anthropic or another provider. ## Use Claude Code through Prem today Run Claude Code outside Loupe with Prem's launcher: ```bash theme={"system"} export API_KEY="your-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" npx -p @premai/api-sdk@1.0.59 confidential-claude ``` This starts the local Confidential Proxy, selects an enabled Prem model, and launches Claude Code against the Anthropic-compatible endpoint. It does not reconfigure Loupe's embedded worker. ## Validate a future Loupe integration When Loupe exposes worker configuration, test it in this order: 1. Start the Confidential Proxy in Anthropic mode with attestation enabled. 2. Select a model returned by `/v1/models`. 3. Configure the embedded Claude Code process to use the local proxy and Prem API key. 4. Send a bounded prompt and confirm a request reaches the local proxy. 5. Confirm the response streams through the Anthropic Server-Sent Events route. 6. Run one file-reading tool call and confirm the tool executes locally. 7. Block the direct Anthropic endpoint during the test and confirm Loupe does not fall back. 8. Restart Loupe and confirm the provider configuration remains effective. The model name shown in the Loupe interface does not prove how the request was routed. Confirm that the request reaches the local proxy and that no direct provider connection occurs. ## Security boundary | Component | Plaintext access | Notes | | ------------------------- | --------------------------------- | ---------------------------------------------------------------- | | Loupe | Yes | Coordinates workers, browser state, terminals, and error context | | Embedded Claude Code | Yes | Reads prompts, files, browser context, and tool results | | DevTools MCP and terminal | Yes, for the data they handle | Remain outside the Prem confidential runtime | | Confidential Proxy | Yes, locally | Encrypts configured model requests and decrypts responses | | Prem API Gateway | No content access | Receives ciphertext and operational metadata | | Prem confidential runtime | Yes, inside the protected runtime | Runs inference and encrypts the response | Even after model routing works, Loupe's browser, terminal, DevTools MCP, worker state, and local logs remain separate security surfaces. ## Troubleshooting The direct integration is not available through the documented product surface. Use Claude Code through `confidential-claude` outside Loupe and request an explicit worker-provider setting from Loupe. Stop the test. The embedded Claude Code process did not receive or retain the Prem endpoint configuration. Do not treat the session as protected by Prem. Confirm that Loupe launches the worker with the required environment. A proxy running on `127.0.0.1:8787` is not sufficient by itself. Tool execution proves only the local Loupe workflow. Confirm model traffic independently at the Confidential Proxy. ## Frequently asked questions ### Is Loupe a model provider? No. Loupe describes each worker as a browser, terminal, and Claude Code chat. ### Does the Claude Code integration prove that Loupe works with Prem? No. It proves the underlying Claude Code path. Loupe must still expose or inherit the same provider configuration for its embedded worker. ### Can Prem protect Loupe's browser or terminal traffic? No. Prem's model path does not place browser, terminal, DevTools MCP, or local worker state inside the confidential runtime. ### What would make Loupe compatible? A documented worker configuration surface and an end-to-end acceptance test that proves model traffic reaches the local proxy without direct-provider fallback. ## Related Use the tested Claude Code path through Prem. Review the Messages protocol and compatibility limits. Review proxy modes, routes, and keys. Review what the confidential inference path protects. # OpenAI-compatible clients Source: https://docs.prem.io/guides/openai-compatible-clients Connect an OpenAI-compatible SDK or application to Prem API through the Confidential Proxy. Does your app already talk to the OpenAI API? Then you can point it at Prem instead. You change one setting: the base URL. Prem runs the model inside a secure enclave. Your prompts stay private on the way there. You connect through a small helper on your machine: the **[Confidential Proxy](/confidential-proxy)**. "OpenAI-compatible" describes the shape of the requests. It does not mean the requests go to OpenAI. It does not mean an OpenAI model runs behind them. Use the local Confidential Proxy for sensitive model traffic. [Go to the confidential setup](#confidential-api). Beta Use Router for its broader model catalog with non-sensitive data. [Go to the Router setup](#router). ## Confidential API ## What the proxy does * Accepts the client's OpenAI-compatible request on your machine. * Encrypts request content before network egress. * Sends ciphertext and required metadata through the Prem API Gateway. * Decrypts the enclave response locally and returns the supported OpenAI-compatible response shape. ## How the connection works ```mermaid theme={"system"} flowchart TB subgraph Local["Your machine: plaintext is available"] C["OpenAI-compatible client"] -->|"Chat Completions request"| P["Confidential Proxy
127.0.0.1:8787/v1"] end P -->|"Encrypt before network egress"| G["Prem API Gateway
ciphertext and metadata"] G -->|"Route encrypted payload"| E["Prem API Enclave
decrypt, infer, encrypt"] E -.->|"Encrypted response"| P P -.->|"OpenAI-compatible response"| C ``` Your app and the Confidential Proxy stay on your machine. The Prem API Gateway sees only the sealed message. The Prem API Enclave opens it inside a Trusted Execution Environment (TEE). ## What you can connect You can connect any client that lets you set a custom base URL. For example: * OpenAI SDKs for Node.js, Python, and other languages. * Agent frameworks that support a custom OpenAI provider. * Internal apps that call `/v1/chat/completions`. * Audio apps that use the transcription or translation routes. The proxy gives your app the routes below. It does not add every OpenAI feature. | Method | Route | What it does | | ---------------- | ----------------------------------------- | ------------------------------------------------------- | | API shape | Sends OpenAI-style JSON or multipart data | Accepts the supported OpenAI routes | | Authentication | Sends `Authorization: Bearer ` | Uses the value as the Prem API key | | Model | Sends a Prem model ID | Sends that model ID to Prem API | | Request content | Plaintext inside the client process | Encrypts content before network egress | | Response content | Receives OpenAI-style output | Decrypts and returns the response locally | | Billing | Reads usage fields when available | Prem bills the organization associated with the API key | The client does not gain every OpenAI API feature. It gains the routes that the Confidential Proxy implements. ## Supported routes The inspected package implements these OpenAI-compatible routes: | Method | Route | Purpose | | ------ | -------------------------- | ----------------------------------------------------------------------- | | `GET` | `/v1/models` | List enabled Prem models | | `POST` | `/v1/chat/completions` | Run chat completion requests, including streaming | | `POST` | `/v1/audio/transcriptions` | Transcribe supported audio inputs | | `POST` | `/v1/audio/translations` | Translation route; no enabled translation model is currently documented | The current Deepgram transcription response is not normalized to OpenAI's `{ "text": "..." }` shape. It returns Deepgram-style `metadata` and `results` fields. Do not expect the Responses, Assistants, Realtime, Batches, Embeddings, or Files APIs. Check the Prem API reference before you use another route. ## Before you start Get these four things ready: * **An app** that lets you set a custom OpenAI base URL. * **A Prem API key.** This proves who you are. See [API Keys](/api-keys). * **A client Key Encryption Key (KEK).** This is a master key that protects your other keys. * **A model** that your Prem account can use. See [Models & Pricing](/billing/models-and-pricing). The API key and the KEK are two different secrets. The API key handles sign-in, limits, and billing. The KEK protects your encryption keys. Keep both safe. ## 1. Set your secrets Open a terminal. Set these values in the terminal that runs the proxy: ```bash theme={"system"} export PREM_API_KEY="your-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Do you not have a KEK yet? Make one, one time only: ```bash theme={"system"} openssl rand -hex 32 ``` Store the KEK in a secret manager. Keep a backup. Use the **same** KEK every time. Do not make a new one each time you start the proxy. `PROXY_URL` and `ENCLAVE_URL` show the current default endpoints. Check the [Prem dashboard](https://dashboard.prem.io) if the endpoints change. ## 2. Start the Confidential Proxy Start the proxy in OpenAI mode. Keep this terminal open while you work: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 127.0.0.1 \ --port 8787 \ --compat openai \ --kek "$CLIENT_KEK" ``` The proxy now listens at `http://127.0.0.1:8787/v1`. Your app sends its requests there. Do you need one proxy for both OpenAI and Anthropic apps? Start it in `both` mode: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 127.0.0.1 \ --port 8787 \ --compat both \ --kek "$CLIENT_KEK" ``` In `both` mode, the OpenAI base URL is `http://127.0.0.1:8787/openai/v1`. ## 3. Point your app at the proxy List the models that your Prem API key can use: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/models \ -H "Authorization: Bearer $PREM_API_KEY" ``` Use the returned `id` value in later requests. Do not substitute an OpenAI model name unless Prem lists that exact ID. The model list currently does not include a provider-backed or Reticle-verifiable assurance label. Do not infer one from the alias. See [Platform Status](/platform-status) for the current contract gap. ## 4. Test chat completions Send a small non-streaming request: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/chat/completions \ -H "Authorization: Bearer $PREM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.2", "messages": [{"role": "user", "content": "Reply with OK."}], "reasoning_effort": "none", "max_completion_tokens": 128, "stream": false }' ``` Replace `glm-5.2` when your account uses another model. Inspect the response `model` field as well as the requested alias. A request for `glm-5.2` currently returns the resolved ID `zai-org/GLM-5.2`. Do not continue if this request fails. See [Errors](/errors) and [Rate limits](/rate-limits). ## 5. Configure the application Change only the API key and base URL when the application already uses Chat Completions. ```typescript Node.js theme={"system"} import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.PREM_API_KEY, baseURL: "http://127.0.0.1:8787/v1", }); const response = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Reply with OK." }], reasoning_effort: "none", max_completion_tokens: 128, }); console.log(response.choices[0]?.message?.content); ``` ```python Python theme={"system"} import os from openai import OpenAI client = OpenAI( api_key=os.environ["PREM_API_KEY"], base_url="http://127.0.0.1:8787/v1", ) response = client.chat.completions.create( model="glm-5.2", messages=[{"role": "user", "content": "Reply with OK."}], reasoning_effort="none", max_completion_tokens=128, ) print(response.choices[0].message.content) ``` Change `glm-5.2` to a model your account can use. See your model IDs on the [Prem dashboard](https://dashboard.prem.io). Other clients name the base URL setting in different ways. Look for `baseURL`, `base_url`, endpoint, host, or provider URL. ## What this setup protects | Component | Reads your text | Notes | | --------------------- | -------------------- | ---------------------------------------------------------- | | Your app | Yes | It creates the prompt and reads the reply on your machine. | | Confidential Proxy | Yes, on your machine | It seals requests and opens replies. | | Prem API Gateway | No | It handles the sealed message and metadata only. | | Prem API Enclave | Yes, inside the TEE | It opens the request, runs the model, and seals the reply. | | App tools and plugins | Depends on the tool | Prem does not put them inside the enclave. | This setup protects the model path only. It does not put your app, its tools, or its plugins inside the enclave. ## Good to know **Use a Prem model ID.** Compatibility does not give you OpenAI-hosted models. Use a model that `/v1/models` returns. **One stream at a time.** The encrypted endpoint allows one active stream for each API key. A second stream on the same key can return `429`. Follow the `Retry-After` header when the response has one. See [Agents & Automation](/agents). **Check the finish reason.** Read `finish_reason` on each response. Do not treat a cut-off reply as a complete one. ## Troubleshooting Check the base URL setting. Restart the app after you change it. List your models with `/v1/models`. Use an enabled Prem model ID from that list. Set the app to use Chat Completions. The proxy does not expose the Responses API. Check `finish_reason`, timeout settings, proxy logs, and rate limits. Do not treat partial content as complete. Wait for the active request to finish. Follow `Retry-After` when the response has it. ## Frequently asked questions ### Does "OpenAI-compatible" mean OpenAI processes the request? No. The term describes the HTTP interface only. The request goes through Prem and uses the Prem model you choose. ### Can I use an OpenAI model name? Only when `/v1/models` returns that exact ID. Compatibility does not give you OpenAI-hosted models. ### Can I use the Responses API? No. Set the client to use `/v1/chat/completions`. ### Which key goes in the Authorization header? The Prem API key. The KEK is a separate local secret. Never send the KEK. ### Does changing the base URL protect my local tools? No. It protects the model path after the proxy seals the request. Your local tools keep their own trust boundaries. ## Router Beta Router is not confidential. Do not send secrets, personal data, regulated data, or other sensitive data through this path. An OpenAI-compatible Chat Completions client can call Router directly: | Setting | Confidential API | Router | | ------------- | ----------------------------------- | ----------------------------------- | | Base URL | `http://127.0.0.1:8787/v1` | `https://router.prem.io/v1` | | API key | `PREM_API_KEY` | `PREM_ROUTER_API_KEY` | | Client KEK | Required by the local proxy | Not used | | Confidential | Yes, within the documented boundary | No | | Model catalog | Confidential catalog | Broader current Router chat catalog | Confirm that `kimi-k3` appears in `GET /v1/models` for this key. Otherwise, use an exact returned model ID. See [Router models](/router/models). ```typescript theme={"system"} const routerClient = new OpenAI({ apiKey: process.env.PREM_ROUTER_API_KEY, baseURL: "https://router.prem.io/v1", }); const response = await routerClient.chat.completions.create({ model: "kimi-k3", messages: [{ role: "user", content: "Reply with router ok." }], }); ``` Use a separate Router key and an exact ID returned for that key. See [Router models](/router/models). ## Related Review every mode, route, key, and daemon option. Configure OpenCode as a specific OpenAI-compatible client. Configure a custom Chat Completions provider. Use a named provider with the Chat Completions transport. Point Goose's built-in OpenAI provider at the proxy. Use the Messages API shape through the same proxy. Handle concurrency, retries, reasoning, and unattended operation. Review current behavior and assurance gaps. # OpenClaw Source: https://docs.prem.io/guides/openclaw Connect OpenClaw to Prem API through the local Confidential Proxy. OpenClaw can use Prem API as a custom OpenAI-compatible provider. OpenClaw sends Chat Completions requests to the local Confidential Proxy, which encrypts them before network egress. OpenClaw, its workspace, channels, local tools, plugins, and MCP servers remain outside the Prem confidential runtime. This setup protects supported model traffic, not the complete agent. Use the local Confidential Proxy for sensitive model traffic. [Go to the confidential setup](#confidential-api). Beta Use Router for its broader model catalog with non-sensitive data. [Go to the Router setup](#router). ## Confidential API ## How the connection works ```mermaid theme={"system"} flowchart TB subgraph Local["Your machine: plaintext is available"] O["OpenClaw"] -->|"OpenAI Chat Completions"| P["Confidential Proxy
127.0.0.1:8787/v1"] T["Workspace, tools, channels, and MCP"] <--> O end P -->|"Encrypt before network egress"| G["Prem API Gateway
ciphertext and metadata"] G -->|"Encrypted request"| E["Prem confidential runtime
decrypt, infer, encrypt"] E -.->|"Encrypted response"| P P -.->|"OpenAI-compatible response"| O ``` ## Before you start You need: * OpenClaw installed. See the [OpenClaw setup guide](https://docs.openclaw.ai/start/setup). * A Prem API key. See [API Keys](/api-keys). * A 32-byte client KEK encoded as 64 hexadecimal characters. * The current Prem gateway and enclave endpoints. * A chat model returned by your Prem model-list request. Check the installed version: ```bash theme={"system"} openclaw --version ``` This configuration has been exercised with OpenClaw `2026.7.1-2` using a normal agent turn and a local file-reading tool call. ## 1. Set your secrets ```bash theme={"system"} export PREM_API_KEY="your-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Generate the KEK once if you do not have one: ```bash theme={"system"} openssl rand -hex 32 ``` Store the KEK in a secret manager. Do not place either secret in `openclaw.json` as a literal value. ## 2. Start the Confidential Proxy ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 127.0.0.1 \ --port 8787 \ --compat openai \ --kek "$CLIENT_KEK" ``` Do not use `--no-attest`. Keep attestation enabled for confidential inference. ## 3. Select an enabled model ```bash theme={"system"} curl http://127.0.0.1:8787/v1/models \ -H "Authorization: Bearer $PREM_API_KEY" ``` Use an `id` returned by this request. The example below uses `qwen36-27b`. Test it before starting OpenClaw: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/chat/completions \ -H "Authorization: Bearer $PREM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen36-27b", "messages": [{"role": "user", "content": "Reply with OK."}], "stream": false }' ``` ## 4. Configure OpenClaw OpenClaw reads JSON5 from `~/.openclaw/openclaw.json`. Add a custom provider: ```json theme={"system"} { "models": { "mode": "merge", "providers": { "prem-confidential": { "baseUrl": "http://127.0.0.1:8787/v1", "apiKey": "${PREM_API_KEY}", "api": "openai-completions", "timeoutSeconds": 600, "models": [ { "id": "qwen36-27b", "name": "Qwen 3.6 27B through Prem", "reasoning": true, "input": ["text"], "contextWindow": 131072, "maxTokens": 8192 } ] } } }, "agents": { "defaults": { "model": { "primary": "prem-confidential/qwen36-27b" } } } } ``` The provider must use `api: "openai-completions"`. The Confidential Proxy does not expose `/v1/responses`. OpenClaw allows the exact `baseUrl` origin in its guarded model-request path. See the [OpenClaw custom-provider reference](https://docs.openclaw.ai/gateway/config-tools#custom-providers-and-base-urls). Validate the file: ```bash theme={"system"} openclaw config validate ``` ## 5. Run one agent turn Run a bounded local turn before connecting channels or schedules: ```bash theme={"system"} openclaw agent \ --local \ --agent main \ --message "Reply with exactly OK." \ --json ``` Inspect the returned provider and model. They should identify `prem-confidential` and the model you configured. OpenClaw can stream model output through the Chat Completions connection. Tool calls return through the encrypted model channel, but OpenClaw executes each tool in its local environment. ## Tool and channel boundary | Component | Plaintext access | Notes | | ------------------------------------ | --------------------------------- | --------------------------------------------------------- | | OpenClaw | Yes | Reads prompts, channel messages, memory, and tool results | | Confidential Proxy | Yes, locally | Encrypts model requests and decrypts responses | | Prem API Gateway | No content access | Receives ciphertext and operational metadata | | Prem confidential runtime | Yes, inside the protected runtime | Runs inference and encrypts the result | | Channel, plugin, tool, or MCP server | Depends on its function | Uses its own storage, credentials, and network boundary | Review channel allowlists, tool permissions, secret storage, workspace access, and external egress separately. ## Agent limits The encrypted chat endpoint permits one active stream for each API key. Parallel OpenClaw agents using the same key can receive `429` responses. Start with one worker. Serialize model turns or assign a separate Prem API key to each concurrent worker. Review [Agents & Automation](/agents) before enabling unattended schedules or channels. ## Troubleshooting Run `openclaw config validate`. Check the field names and confirm that `api` is `openai-completions`. The provider is using the wrong adapter. Set `api` to `openai-completions` and restart the affected agent process. Confirm that the Confidential Proxy is running in the same host environment. A container or remote OpenClaw worker needs a reachable proxy address instead of its own loopback interface. Call `/v1/models` with the Prem API key and use a returned model ID in both provider locations. Wait for the current stream to finish. Reduce parallel agent turns or use separate API keys for concurrent workers. ## Frequently asked questions ### Is OpenClaw running inside the enclave? No. OpenClaw runs on your machine. Only supported model inference traffic passes through the encrypted Prem path. ### Does OpenClaw need the Prem TypeScript SDK? No. It calls the OpenAI-compatible surface exposed by the local proxy. ### Can OpenClaw use tools through this setup? Yes, when the selected model produces compatible tool calls. OpenClaw executes those tools outside the enclave. ### Can I expose the proxy to a remote OpenClaw host? Yes, but that changes the local trust boundary. Use TLS, authenticated private networking, a restrictive firewall, and a controlled host binding. Do not expose an unauthenticated plaintext listener to the public internet. ## Router Beta Router is not confidential. Use this path only for non-sensitive prompts, workspace context, and tool results. OpenClaw uses Router through a separate `openai-completions` provider. ```bash theme={"system"} export PREM_ROUTER_API_KEY="your-router-api-key" ``` Confirm that `kimi-k3` appears in `GET /v1/models` for this key. Otherwise, use an exact returned model ID. See [Router models](/router/models). Add a provider named `prem-router` with these values: | Field | Value | | -------------- | --------------------------- | | `baseUrl` | `https://router.prem.io/v1` | | `apiKey` | `${PREM_ROUTER_API_KEY}` | | `api` | `openai-completions` | | First model ID | `kimi-k3` | | Agent model | `prem-router/kimi-k3` | Use the complete JSON in the [Router OpenClaw configuration](/router/integrations#openclaw), then run `openclaw config validate`. Do not start the Confidential Proxy or pass a KEK for this provider. ## Related Review modes, routes, keys, and daemon controls. Understand the interface used by OpenClaw. Review concurrency, retries, and unattended operation. Review the protected and unprotected parts of the flow. # OpenCode Source: https://docs.prem.io/guides/opencode Connect OpenCode to Prem API through the local Confidential Proxy. OpenCode is a coding assistant that runs in your terminal. This guide connects it to Prem API. Prem runs the model inside a secure enclave. Your prompts stay private on the way there. OpenCode does not use the TypeScript SDK directly. It uses the OpenAI-compatible routes from the Confidential Proxy. The proxy is part of `@premai/api-sdk`. Use the local Confidential Proxy for sensitive prompts and source code. [Go to the confidential setup](#confidential-api). Beta Use Router for its broader model catalog with non-sensitive data. [Go to the Router setup](#router). ## Confidential API ## How the connection works ```mermaid theme={"system"} flowchart TB subgraph Local["Your machine: plaintext is available"] O["OpenCode"] -->|"OpenAI-compatible request"| P["Confidential Proxy
127.0.0.1:8787/v1"] end P -->|"Encrypt before network egress"| G["Prem API Gateway
ciphertext and metadata"] G -->|"Route the encrypted payload"| E["Prem API Enclave (TEE)
decrypt, infer, encrypt"] E -.->|"Encrypted response returns on the same path"| P P -.->|"Decrypt on your machine"| O ``` OpenCode and the Confidential Proxy stay on your machine. The Prem API Gateway sees only the sealed message. The Prem API Enclave opens it inside a Trusted Execution Environment (TEE). ## Before you start Get these four things ready: * **OpenCode**, installed. See the [OpenCode install guide](https://opencode.ai/docs/). * **A Prem API key.** This proves who you are. See [API Keys](/api-keys). * **A client Key Encryption Key (KEK).** This is a master key that protects your other keys. * **A model** that your Prem account can use. See [Models & Pricing](/billing/models-and-pricing). The API key and the KEK are two different secrets. The API key handles sign-in, limits, and billing. The KEK protects your encryption keys. Keep both safe. ## 1. Set your secrets Open a terminal. Set these values in the terminal that runs the proxy: ```bash theme={"system"} export PREM_API_KEY="your-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Do you not have a KEK yet? Make one, one time only: ```bash theme={"system"} openssl rand -hex 32 ``` Store the KEK in a secret manager. Keep a backup. Use the **same** KEK every time. Do not make a new one each time you start the proxy. `PROXY_URL` and `ENCLAVE_URL` show the current default endpoints. Check the [Prem dashboard](https://dashboard.prem.io) if the endpoints change. ## 2. Start the Confidential Proxy Start the proxy in OpenAI mode. Keep this terminal open while you work: ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --host 127.0.0.1 \ --port 8787 \ --compat openai \ --kek "$CLIENT_KEK" ``` The proxy now listens at `http://127.0.0.1:8787/v1`. OpenCode sends its requests there. ## 3. Do a proxy test Send one request before you start OpenCode: ```bash theme={"system"} curl http://127.0.0.1:8787/v1/chat/completions \ -H "Authorization: Bearer $PREM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.2", "messages": [{"role": "user", "content": "Reply with OK."}], "reasoning_effort": "none", "max_completion_tokens": 128, "stream": false }' ``` Use a different model ID if your account does not have `glm-5.2`. Inspect the response `model` field. The requested alias `glm-5.2` currently resolves to `zai-org/GLM-5.2`. The response does not include an assurance-profile field. See [Platform Status](/platform-status). Do not continue if this request does not complete. See [Errors](/errors) and [Rate limits](/rate-limits). ## 4. Configure OpenCode Create `opencode.json` in the project root: ```json theme={"system"} { "$schema": "https://opencode.ai/config.json", "model": "prem-confidential/glm-5.2", "small_model": "prem-confidential/glm-5.2", "enabled_providers": ["prem-confidential"], "share": "disabled", "provider": { "prem-confidential": { "npm": "@ai-sdk/openai-compatible", "name": "Prem API through the Confidential Proxy", "options": { "baseURL": "http://127.0.0.1:8787/v1", "apiKey": "{env:PREM_API_KEY}", "timeout": 600000, "chunkTimeout": 60000 }, "models": { "glm-5.2": { "name": "GLM 5.2" } } } }, "permission": { "edit": "ask", "bash": "ask" } } ``` Here is what each part does: | Setting | What it does | | ------------------------- | ------------------------------------------------------ | | `baseURL` | Sends OpenCode's requests to your local proxy. | | `apiKey` | Reads the Prem API key from your terminal. | | `model` and `small_model` | Uses Prem for big and small tasks. | | `enabled_providers` | Uses Prem only. No other provider. | | `share: disabled` | Does not create public chat links. | | `permission` | Asks you before it edits files or runs shell commands. | This follows the [OpenCode custom provider docs](https://opencode.ai/docs/providers/). Change `glm-5.2` to the model your account uses. Change it in both `model` and `models`. ## 5. Start OpenCode Start OpenCode from the folder that has `opencode.json`: ```bash theme={"system"} opencode ``` Send a short prompt, such as "Say hello." Watch the proxy terminal. You see the request arrive. That is it. OpenCode now talks to Prem through the sealed path. Type `/models` in OpenCode to check the model. The provider shows as **Prem API through the Confidential Proxy**. ## What this setup protects | Component | Reads your text | Notes | | --------------------------- | -------------------- | ---------------------------------------------------------------------- | | OpenCode | Yes | It reads your prompt, your code, and the tool results on your machine. | | Confidential Proxy | Yes, on your machine | It seals requests and opens replies. | | Prem API Gateway | No | It handles the sealed message and metadata only. | | Prem API Enclave | Yes, inside the TEE | It opens the request, runs the model, and seals the reply. | | External tool or MCP server | Depends on the tool | A tool reads the data that OpenCode sends to it. | This setup protects the model path only. It does not put OpenCode, your code, shell commands, plugins, MCP servers, or local files inside the enclave. ## Good to know **Tool calls run on your machine.** The model can ask OpenCode to run a tool. The model traffic uses the sealed path. But OpenCode runs the tool on your machine, outside the enclave. So keep the `edit` and `bash` permissions set to `ask`. Do not treat a private model as a private tool. **One stream at a time.** The encrypted endpoint allows one active stream for each API key. A second stream on the same key can return `429`. Start with one OpenCode session and no parallel subagents. Need parallel workers? Run them one after another, or give each worker its own API key. See [Agents & Automation](/agents). ## Troubleshooting Check that the proxy runs at `127.0.0.1:8787`. Check that `baseURL` ends with `/v1`. Check that `PREM_API_KEY` is correct. Check that the API key is active. Change `glm-5.2` to a model your account can use. Change it in `model` and in `models`. Wait for the active stream to finish. Reduce parallel work. Follow the `Retry-After` header if the response has one. Check that `enabled_providers` lists only `prem-confidential`. Restart OpenCode after you change `opencode.json`. ## Frequently asked questions ### Does OpenCode use the Prem TypeScript SDK? No. The local proxy gives OpenCode an OpenAI-compatible interface. OpenCode does not import the SDK. ### Is the API key the same as the KEK? No. The API key handles access, limits, and billing. The KEK protects your encryption keys. ### Does this protect my whole OpenCode session? No. It protects the model path. OpenCode and its local tools stay on your machine. ### Can OpenCode use tool calls? Yes, when the selected model supports tool calls. The model traffic uses the encrypted path. OpenCode runs each tool outside the enclave. ## Router Beta Router is not confidential. Do not send secrets, private source code, personal data, or regulated data through this path. Router uses a separate OpenCode provider. It does not use the Confidential Proxy, client KEK, or attestation path. ```bash theme={"system"} export PREM_ROUTER_API_KEY="your-router-api-key" ``` Confirm that `kimi-k3` appears in `GET /v1/models` for this key. Otherwise, use an exact returned model ID. See [Router models](/router/models). Add the `prem` provider from the [Router OpenCode configuration](/router/integrations#opencode). Use `https://router.prem.io/v1` as the base URL and start with `prem/kimi-k3` as the model selector. Keep the provider names separate when both paths are configured: | Path | Provider | Key | Data boundary | | ---------------- | ------------------- | --------------------- | --------------------------------- | | Confidential API | `prem-confidential` | `PREM_API_KEY` | Encrypted through the local proxy | | Router | `prem` | `PREM_ROUTER_API_KEY` | Not confidential | ## Related Learn the proxy modes, routes, keys, and daemon controls. Review concurrency, retries, reasoning, and error controls. Understand the compatibility layer that OpenCode uses. Learn what the enclave protects and what stays outside the TEE. Complete the key, reliability, capacity, and attestation checks. Review current behavior, assurance gaps, and roadmap items. # Qwen Code Source: https://docs.prem.io/guides/qwen-code Launch Qwen Code through Prem's Confidential Proxy with the bundled confidential-qwen wrapper. The `@premai/api-sdk` package contains `confidential-qwen`. This launcher starts the Confidential Proxy in Anthropic mode. Then it runs the installed Qwen Code CLI against the proxy. The local proxy encrypts supported model traffic before network egress. Qwen Code, repository files, shell commands, MCP servers, and tool results remain on your machine or in their own external systems. Use `confidential-qwen` to connect Qwen Code through the local Confidential Proxy. [Go to the confidential setup](#before-you-start). Beta Router is not available for `confidential-qwen`. The launcher drives Qwen Code over the Anthropic Messages API. Router supports only OpenAI-compatible Chat Completions. Use [OpenCode](/guides/opencode), [OpenClaw](/guides/openclaw), [Hermes Agent](/guides/hermes), or [Goose](/guides/goose) for a Router coding workflow. ## How the launcher works ```mermaid theme={"system"} flowchart TB subgraph Local["Your machine: plaintext is available"] C["Qwen Code"] -->|"Anthropic Messages API"| P["Confidential Proxy
127.0.0.1:8787"] T["Local tools and MCP servers"] <--> C end P -->|"Encrypt before network egress"| G["Prem API Gateway
ciphertext and metadata"] G -->|"Encrypted request"| E["Prem confidential runtime
decrypt, infer, encrypt"] E -.->|"Encrypted response"| P P -.->|"Anthropic SSE or message"| C ``` The launcher does these steps on every run: 1. Requires an interactive terminal (TTY). 2. Reads the endpoint, key, and KEK configuration. 3. Starts or reuses the proxy in Anthropic mode with attestation enabled. 4. Displays the list of your enabled models. 5. Prepares an isolated Qwen home and chooses the `anthropic` authentication type. 6. Sets the Qwen Code Anthropic base URL and the model environment variables. 7. Sends every remaining argument to the installed `qwen` command. Qwen Code connects to Prem through its Anthropic provider. It does not use Qwen OAuth or a DashScope key. The launcher passes `--auth-type anthropic` and removes all OAuth, OpenAI, Gemini, and Google credentials from the child environment. ## Before you start You need: * Qwen Code installed and available as `qwen` on `PATH`. * A Prem API key. * A 32-byte KEK encoded as 64 hexadecimal characters. * A terminal with interactive input. * `@premai/api-sdk`. Check the installed Qwen Code version: ```bash theme={"system"} qwen --version ``` `confidential-qwen` is not present in `@premai/api-sdk@1.0.60` or earlier. Install the package without a version pin so that npm resolves a release that publishes the binary. Then confirm with `npx -p @premai/api-sdk confidential-qwen --help`. ## 1. Set the launcher values ```bash theme={"system"} export API_KEY="your-prem-api-key" export CLIENT_KEK="your-64-character-hex-kek" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Generate the KEK once if you do not have one: ```bash theme={"system"} openssl rand -hex 32 ``` `confidential-qwen` reads `API_KEY`, not `PREM_API_KEY`. Supply it through the environment. This also prevents the launcher from prompting for the API key and saving it in the application-data `.env` file. The launcher still saves the selected model ID there. `confidential-qwen` uses its own application-data directory. The directory is separate from the one that `confidential-claude` uses. Configure each launcher once. ## 2. Run the first request Use `--prompt` for a bounded first test: ```bash theme={"system"} npx -p @premai/api-sdk confidential-qwen \ --prompt "Reply with exactly OK. Do not use tools." ``` Choose an enabled model in the picker. Use the arrow keys or `j` and `k`. Then press Enter. `--prompt` still needs a TTY. The launcher always shows the model list before it starts Qwen Code. ## 3. Start an interactive session ```bash theme={"system"} npx -p @premai/api-sdk confidential-qwen ``` Qwen Code uses the Anthropic-compatible Messages route. It receives Server-Sent Events while it works. ## Arguments the launcher owns The launcher sets the model and the authentication type itself. It removes these flags from the arguments that you pass. It removes both the `--flag value` and `--flag=value` forms: | Flag | Reason | | ------------- | --------------------------------- | | `--model` | The picker sets the model | | `-m` | Short form of `--model` | | `--auth-type` | The launcher requires `anthropic` | The launcher sends every other argument to `qwen` without changes. Choose a different model by restarting the launcher and selecting it in the picker. ## Model selection The picker calls the model-list endpoint on every run. This guide documents these chat models: | Picker ID | Display name | Current catalogue behavior | | ------------ | ------------ | -------------------------------------------------- | | `qwen36-27b` | Qwen 3.6 | Text, image, and video input metadata | | `glm-5.2` | GLM 5.2 | Text chat alias that resolves to `zai-org/GLM-5.2` | The Anthropic adapter returns the requested alias in the message `model` field. It does not show the backend-resolved model ID or an assurance profile. Do not infer an assurance class from the picker label. See [Platform Status](/platform-status). A Qwen picker label names a Prem catalogue model. It does not show that Qwen Code connects to an Alibaba-hosted Qwen endpoint. ## Isolated Qwen home The launcher does not write Prem credentials into `~/.qwen/settings.json`. It builds a private `QWEN_HOME` under its application-data directory for the session: * The launcher links your existing `~/.qwen` entries into the private home. Extensions, commands, and history remain available. * The launcher copies `settings.json`. Then the copy sets the `anthropic` authentication type. * The launcher merges your settings changes back into `~/.qwen/settings.json` when Qwen Code exits. It restores your original authentication and model keys. It removes the local endpoint and the API key from anything it writes back. * The launcher removes the private home on exit. A later run also removes directories that a previous run leaves behind after a crash. The launcher prints a warning if it cannot parse `~/.qwen/settings.json`. It leaves the file unchanged. It does not sync anything back. Set `QWEN_HOME` before you start the launcher. The launcher reads that location as your real Qwen home. It still isolates the session from it. ## Environment the launcher sets | Variable | Value | | ----------------------------- | --------------------------------------------------- | | `ANTHROPIC_BASE_URL` | The local proxy root, including the route prefix | | `ANTHROPIC_API_KEY` | Your Prem API key | | `ANTHROPIC_MODEL` | The model that you selected | | `QWEN_HOME` | The private session home | | `QWEN_STREAM_IDLE_TIMEOUT_MS` | `600000`, which permits long confidential responses | The launcher removes `ANTHROPIC_AUTH_TOKEN`, `QWEN_OAUTH`, `QWEN_MODEL`, and the `OPENAI_`, `GEMINI_`, and `GOOGLE_` credential and model variables from the child environment. A stale credential cannot redirect traffic away from the proxy. ## Stop the local proxy The launcher starts a detached proxy. The proxy continues to run when Qwen Code exits. Stop it explicitly when you finish: ```bash theme={"system"} npx -p @premai/api-sdk confidential-proxy stop ``` Check the status before another run when the default proxy port appears occupied: ```bash theme={"system"} npx -p @premai/api-sdk confidential-proxy status ``` `confidential-claude` and `confidential-qwen` share one proxy in Anthropic mode. You do not need a second proxy to run both at the same time. One `confidential-proxy stop` ends the session for both. ## Plaintext and tool boundary | Component | Plaintext access | Notes | | ---------------------------------- | --------------------------------- | ------------------------------------------------------------------ | | Qwen Code | Yes | Reads prompts, selected repository files, and tool results locally | | Confidential Proxy | Yes, locally | Translates, encrypts, and decrypts model traffic | | Prem API Gateway | No content access | Receives ciphertext, authentication, and routing metadata | | Prem confidential runtime | Yes, inside the protected runtime | Runs the selected model and encrypts the response | | Shell, MCP servers, and extensions | Depends on the tool | Outside the Prem inference boundary | Review Qwen Code permissions, MCP servers, extensions, and egress separately. A confidential model route does not protect local tool execution. ## Troubleshooting Run the command directly in an interactive terminal. Piping input or starting it from a non-interactive CI process does not supply the interactive terminal that model selection needs. Install Qwen Code and confirm that `qwen --version` succeeds in the same shell. See the [Qwen Code documentation](https://qwenlm.github.io/qwen-code-docs/en/users/overview/). Check `API_KEY`, `PROXY_URL`, `ENCLAVE_URL`, network access, and proxy logs. Make sure that the key can list models. The launcher needs an Anthropic Messages route. It refuses to use a proxy that cannot serve one. Run `confidential-proxy stop`. Then start the launcher again. Pre-start the proxy with `confidential-proxy start --compat anthropic` or `--compat both`. Run `confidential-proxy status`. Then stop the managed proxy. The launcher refuses to take over an unknown process on that port. A stale credential or authentication setting overrides the Anthropic provider. Confirm that you did not pass `--auth-type`. Check `~/.qwen/settings.json` for a pinned authentication type. The launcher still runs. But it does not sync settings back. Repair the JSON. Then start it again. Do not add `--no-attest` to work around the failure. Capture the error. Verify the current Reticle boundary in [Attestation](/attestation). ## Frequently asked questions ### Does `confidential-qwen` install Qwen Code? No. It checks for an existing `qwen` command. It exits if Qwen Code is not installed. ### Does it use my Qwen or DashScope credentials? No. `API_KEY` is the Prem API key. The launcher clears all Qwen OAuth, OpenAI, Gemini, and Google variables. It passes the Prem key to Qwen Code as the Anthropic provider token. ### Does it change my Qwen Code configuration? It runs the session against a private home. It merges your settings changes back into `~/.qwen/settings.json` on exit. It does not write the local endpoint or the API key. Your original authentication and model keys are restored. ### Can I pass normal Qwen Code arguments? Yes, except `--model`, `-m`, and `--auth-type`. The launcher sets those values and removes them. ### Can I run it beside `confidential-claude`? Yes. Both use Anthropic mode and share one proxy. ### Is the full coding session inside an enclave? No. Only supported inference traffic uses the encrypted Prem path. Files and tools remain in their own local or external trust boundaries. ## Related Review the sibling launcher that shares the same proxy mode. Review the Messages translation and compatibility limits. Review proxy modes, routes, keys, and daemon controls. Review the protected and unprotected parts of the data flow. # React Native (BareKit) Source: https://docs.prem.io/guides/react-native Run the Prem TypeScript SDK inside a react-native-bare-kit worklet. Building a mobile app? You get the same confidentiality and security as on the server. The `@premai/api-sdk` TypeScript client runs inside a [react-native-bare-kit](https://github.com/holepunchto/react-native-bare-kit) worklet. It encrypts each request on the device, before the request leaves the phone. The Prem API Gateway sees only ciphertext. The enclave opens the request inside a Trusted Execution Environment (TEE). The encryption path is identical to the [standard client](/quickstart). The worklet runs the same WASM cryptography, so the [security model](/security-model) and [attestation](/attestation) guarantees also apply on mobile. This is the same method that powers [Sotto](https://sotto.prem.io), Prem's confidential dictation app. Want a working starting point? The [`reticle-expo`](https://github.com/prem-research/reticle-expo) example app runs the worklet on iOS and Android. It verifies enclave attestation from the device, with no server-side proxy. Clone it and use it as a scaffold for your own app. ## Pass an assets directory Pass an `assets` directory to `new Worklet(...)`. bare-kit extracts the bundled assets to that directory at startup. It then rewrites the paths to real `file://` URLs. The WASM module loads from disk on the fast path. ```js theme={"system"} import { Worklet } from "react-native-bare-kit"; import * as FileSystem from "expo-file-system"; const cacheDir = new FileSystem.Directory(FileSystem.Paths.cache, "reticle-assets"); if (!cacheDir.exists) cacheDir.create(); const worklet = new Worklet({ assets: cacheDir.uri.replace(/^file:\/\//, ""), }); await worklet.start("/worklet.bundle", source); ``` If you omit `assets`, the SDK falls back to an inline base64 copy of the WASM bytes. The disk path is faster, so pass `assets` when you can. ## Related Clone the example Expo app and use it as a scaffold. See how the device verifies real confidential hardware. Learn the proxy modes, routes, keys, and daemon controls. Send your first encrypted request with the TypeScript client. # How It Works Source: https://docs.prem.io/how-it-works Architecture overview: how the SDK or local proxy encrypts content for processing in the Prem confidential runtime. ## The Simple Version This is the core idea, before the architecture diagrams: 1. **You type a prompt.** 2. **Your device encrypts the prompt** before it sends data over the network. 3. **Our gateway receives the encrypted payload.** The gateway handles authentication and billing, but it cannot read your data. 4. **The encrypted payload enters a sealed hardware environment** (a Confidential Virtual Machine). There, the enclave decrypts the payload, the AI model processes it, and the enclave encrypts the response again. 5. **The encrypted response travels back to your device.** Your device decrypts the response and shows it. Your data is plaintext in your application and, when used, the local Confidential Proxy. The protected runtime must decrypt it to process the request. The network-facing Prem gateway handles ciphertext plus authentication, routing, size, timing, and billing metadata. ## Architecture Overview ```mermaid theme={"system"} flowchart LR subgraph You["Your Device"] SDK["Prem API SDK"] end subgraph Platform["Prem API Platform"] Proxy["Proxy Gateway"] Enclave["Secure Enclave (TEE)"] Router["Model Router"] end subgraph Infra["Supporting Services"] S3["Encrypted File Storage"] VectorDB["RAG"] Redis["Redis Cache"] end SDK -->|"encrypted payload"| Proxy Proxy -->|"encrypted payload"| Enclave Enclave --> Router Router -->|"LLM inference"| Enclave Enclave --> S3 Enclave --> VectorDB Proxy --> Redis Enclave -->|"encrypted response"| Proxy Proxy -->|"encrypted response"| SDK ``` ## The Components ### Your Device: The Prem API SDK The SDK runs on your side: your laptop, your server, your application, or your browser. It is the only location, besides the sealed enclave, where your data exists in readable form. The functions of the SDK: * **Encrypts all data** before the data leaves your device, with modern, quantum-resistant cryptography * **Holds your master encryption key**: a key that you generate and that never leaves your device * **Decrypts responses** when they come back From the perspective of your code, the SDK operates like the standard OpenAI SDK. The encryption is invisible. The SDK does the encryption automatically. ### PREM API: The Blind Gateway The proxy is the entry point of the platform. The proxy handles the operational tasks: it checks your API key, enforces rate limits, tracks usage for billing, and routes requests. **The critical point: the proxy never sees your actual data.** The proxy processes only encrypted payloads and metadata, such as API keys and timestamps. It has no encryption keys and no method to decrypt the data that passes through it. | What the Proxy does | What the Proxy cannot do | | ----------------------------------------------- | ----------------------------------- | | Validate your API key and permissions | Read your prompts or responses | | Enforce rate limits for your organization | Access any encryption keys | | Route encrypted payloads to the correct enclave | Log or inspect your data | | Track usage for billing | Decrypt the files that you uploaded | If an attacker compromises the Prem API Gateway, the intended exposure is encrypted content plus operational metadata. A local Confidential Proxy is different: it is a client-side plaintext termination point and belongs inside your local trust boundary. ### Prem API Enclave: The Sealed Processing Environment The enclave is the location where your data is processed. The enclave runs inside a **Trusted Execution Environment (TEE)**, a sealed area of the processor with its own encrypted memory. The rest of the system cannot access this memory. The enclave is comparable to a bank vault inside a building. The building owner has keys to every room. But the vault has its own lock, and the building owner cannot open this lock. In this analogy, the "building" is the server. The "building owner" is the operator of the server: us, or our infrastructure provider. The "vault" is the TEE. The steps inside the enclave: 1. Your encrypted payload arrives. 2. The enclave decrypts the payload with a secure key exchange. 3. The AI model processes your request. 4. The enclave encrypts the response before the response leaves. 5. The enclave wipes all plaintext from memory. The enclave runs on **AMD SEV-SNP** or **Intel TDX** processors, with **NVIDIA Hopper and Blackwell architecture GPUs** in confidential compute mode. The hardware enforces the isolation. The isolation is not a software setting that admin privileges can turn off. ### Model Routing Model routing selects a backend for the requested alias, applies health and capacity decisions, and associates an attestation session with the subsequent request. The public API does not currently expose a machine-readable provider-backed or Reticle-verifiable assurance profile. Do not infer the complete backend trust boundary from an alias. See [Platform Status](/platform-status) for the current limitation. ### Confidential boundary The intended confidential boundary contains the components that decrypt or process protected content. The network gateway remains outside and handles encrypted payloads plus operational metadata. A complete assurance decision needs attestation evidence, approved measurements, and a route profile. The current API does not expose all three, and the reviewed CPU verifier still has TODOs for expected measurement comparison. See [Attestation](/attestation). ### The Infrastructure Locations Prem API runs on a **hybrid infrastructure**, a mix of hardware that we own and capacity that we rent: * **Owned infrastructure** is in **Switzerland**, under Swiss data protection law * **Rented infrastructure** is primarily in **Europe**, with some deployments in the **United States** TEE hardware is designed to reduce trust in the host operator. Deployment access controls, geography, and unattended-operation claims are operational controls that must be audited separately. [Attestation](/attestation) explains what the current client verifies and what remains policy work. ## The Lifecycle of a Chat Request This is the full lifecycle of a chat request: ```mermaid theme={"system"} sequenceDiagram participant You as Your Device participant Gateway as Proxy (sees only encrypted data) participant Enclave as Sealed Enclave (CVM) participant LLM as AI Model (inside CVM) Note over You: 1. Prepare You->>Enclave: Request enclave's public key Enclave-->>You: Public key Note over You: 2. Encrypt You->>You: Generate shared secret with enclave You->>You: Encrypt your message Note over You,Proxy: 3. Send (encrypted) You->>Proxy: Encrypted payload Note over Proxy: Check API key, rate limits Proxy->>Enclave: Forward (still encrypted) Note over Enclave: 4. Process (sealed hardware) Enclave->>Enclave: Decrypt your message Enclave->>LLM: Run inference LLM-->>Enclave: AI response Enclave->>Enclave: Encrypt response Note over Enclave,You: 5. Return (encrypted) Enclave-->>Proxy: Encrypted response Proxy-->>You: Forward (still encrypted) You->>You: Decrypt and display ``` For **streaming responses** (word-by-word output in the style of ChatGPT), the enclave encrypts each chunk individually before it sends the chunk. The proxy forwards the chunks and does not buffer or inspect them. ## The Tasks That the SDK Does for You The SDK does all these tasks automatically. You do not need to: * Understand or manage encryption algorithms * Do key exchanges manually * Encrypt or decrypt data in your application code * Handle streaming decryption From the perspective of your application, you make standard API calls and get standard responses. The encryption layer is fully invisible. See the [Encryption](/encryption) reference for the full cryptographic details: algorithms, key types, and protocols. Continue to [Security Model](/security-model) for the security guarantees and their limits. # Idempotency Source: https://docs.prem.io/idempotency Ensure safe retries and prevent duplicates using the Idempotency-Key header. We support **idempotent requests** so that you can retry safely without duplicates. This is important for actions that **create resources**, **upload files**, or **modify data**. For these actions, repeated requests can cause unwanted side effects. ## The definition of idempotency With idempotency, the **same request, sent many times**, has the **same effect** as one request. If your client gets a timeout or a network error, retry the request with the **same idempotency key**. We process the request only **once**. ## The operation of idempotency To make an idempotent request, include a unique key in the header: `Idempotency-Key: your-unique-idempotency-key` We store the **status code and response body** of the first request for that key. Each subsequent request with the same key: * Returns the **original result**, for success or for failure * Prevents **duplicate processing** of resource creation or updates * Keeps **data integrity** across retries This behavior applies also if the first request caused a `500` or other server error. This keeps the result consistent for all outcomes. The API ignores the `Idempotency-Key` in `GET` or `DELETE` requests. ## Key Expiration * We store idempotency keys for **24 hours**. * After expiration, the API treats the same key as a **new request**. * Keys can be **up to 255 characters long**. ## Response Headers When you use an `Idempotency-Key`, we include these response headers. They help clients see how we processed the request: | Header | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `Idempotency-Key` | Shows the key from the request. This confirms that the API accepted and applied the key | | `Idempotency-Status` | Shows how we processed the request: `new`: the first execution of the key, `replayed`: a cached response from an earlier identical request | Example: ``` HTTP/1.1 200 OK Idempotency-Key: 6aa2f8a3-4ef4-4899-8234-d45a93d1f191 Idempotency-Status: replayed ``` These headers help you debug retries. They show if the API replayed the result or generated a new one. ## Best Practices * Use a **UUIDv4** or another random, high-entropy string to generate keys. * **Use a key again** only when you retry the **same exact request**. * **Do not use the same key** for different operations, endpoints, or payloads. Prem API rejects a repeat request with the same key if the **parameters differ** from the original. This prevents accidental misuse. ## When to Use Idempotency Use idempotency for each request where duplication has negative effects. Use it when you: * Upload encrypted files * Create or modify resources with the API * Send webhooks or external callbacks * Start long-running or asynchronous tasks ## Summary | Header | Required | Description | | ----------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------- | | `Idempotency-Key` | Optional (but recommended for all `POST`/`PUT`/`PATCH` requests) | Makes sure that the API performs a given operation only once, also after retries | The idempotency layer of Prem API **does not cache invalid requests**. If a request fails validation, or if we reject it before execution, we do not store the key. You can retry those requests. If you need help with implementation or with debugging, contact [support@premai.io](mailto:support@premai.io). Include your request payload and your idempotency key. # What is Prem API Source: https://docs.prem.io/index Client-encrypted model APIs with confidential-computing evidence and explicit assurance boundaries. **In one sentence:** Prem API provides OpenAI- and Anthropic-compatible model routes with client-side encryption and confidential-computing evidence. Review the current measurement-policy and route-assurance gaps before making a workload-specific guarantee. ## The Problem When you use a standard AI API, your prompts, files, and conversations are processed on infrastructure that you do not control. HTTPS protects data in transit, but the service normally receives plaintext for processing. For many teams, this model is acceptable. Teams handling patient records, financial data, legal documents, source code, or trade secrets may need a narrower processing boundary and evidence they can inspect. Prem API changes where plaintext is available and adds confidential-computing evidence. It does not remove every dependency or trust assumption. ## What Prem API Does Prem API exposes supported chat and audio routes. The TypeScript SDK or local Confidential Proxy encrypts the request payload before network egress. The gateway handles ciphertext and operational metadata, and the selected processing runtime decrypts the payload for inference. The SDK or local proxy encrypts the request payload before sending it to the Prem gateway. The gateway receives payload ciphertext plus authentication, model, size, timing, routing, and billing metadata. Supported confidential routes process plaintext inside a Trusted Execution Environment (TEE). Attestation evidence and policy define what the client can conclude about that runtime. ## The Comparison with a Standard AI API | | Standard AI API | Prem API | | ----------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | **Access to your data** | The provider, the provider's staff, and possibly the cloud host | Your application and the protected processing runtime; a local proxy also sees plaintext when used | | **Gateway payload** | The service normally receives the request body | The Prem gateway receives ciphertext plus operational metadata | | **Processing boundary** | Provider-managed workload | A supported confidential route uses a protected runtime; verify the selected route | | **Security evidence** | Provider policy, controls, and audit material | Hardware-signed evidence plus measurement, TCB, and route policy | | **Host access control** | Defined by the provider's architecture | Confidential-computing hardware is designed to restrict host access; deployment validation still applies | | **API compatibility** | Provider-specific | Supported subsets of OpenAI- and Anthropic-compatible interfaces | ## Who Prem API Is For Healthcare, finance, legal, and government teams that need to map an AI data path to their own legal, compliance, and security controls. Development teams that need a client-encrypted inference path and can validate the supported API surface. Teams that require hardware evidence and an explicit policy decision in addition to provider documentation. Companies evaluating AI for internal documents, proprietary code, or strategic planning under a defined threat model. ## What You Can Do Today Prem API exposes a supported subset of OpenAI-compatible and Anthropic-compatible interfaces: * **Chat with AI models**: Streaming conversations, multi-step reasoning, and tool use * **Transcribe audio**: Use the currently enabled Deepgram model and its response shape The current response does not expose a provider-backed versus Reticle-verifiable assurance profile for each model route. A model alias does not inherit confidential-runtime properties without that evidence. See [Platform Status](/platform-status). ## Get Started You can integrate Prem API into an application, or you can evaluate Prem API for your organization. The procedure is the same: ```bash theme={"system"} npm install @premai/api-sdk ``` ```typescript theme={"system"} import { createRvencClient } from "@premai/api-sdk"; const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK, // You generate this. We never see it. }); ``` ```typescript theme={"system"} const response = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Hello, privately." }], }); ``` For Python, Go, or another language, use the bundled local proxy with a client that supports a custom base URL and the documented routes. Check the [Confidential Proxy](/confidential-proxy) compatibility limits before rollout. ## What to Read Next Learn the architecture: the function of each component and the data flow through the system. Examine the trust model: TEEs, attestation, the threat model, and the known limitations. Read the integration guide: SDK options, capabilities, code examples, and the API reference. See the features that are available today, the features that are not ready, and the roadmap. Building an unattended or agentic system? Start here. Go to the [Quickstart guide](/quickstart) to start immediately. # Platform Status & Roadmap Source: https://docs.prem.io/platform-status Verified current behavior, known gaps, and the work required to close them. ## Verified current behavior | Area | Current evidence | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Client encryption | Direct SDK and local proxy requests completed through the encrypted RVENC path | | Chat | Live non-streaming and streaming requests completed on OpenAI and Anthropic compatibility surfaces | | Coding clients | OpenCode `1.18.11`, Claude Code `2.1.220`, OpenClaw `2026.7.1-2`, Hermes Agent `0.20.0`, and Goose `1.45.0` completed live requests through the local proxy | | Audio transcription | `deepgram/general-nova-3` completed a live request and returned Deepgram-style results | | Model catalogue | The current documented aliases are listed below | | Attestation gate | The SDK calls high-level Reticle attestation and requires a session ID before protected requests when attestation is enabled | ## Attestation status | Capability | Status | Boundary | | ----------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | AMD SEV-SNP evidence verification | Implemented | Signature/certificate and nonce checks run; expected measurement comparison remains TODO | | Intel TDX evidence verification | Implemented | Quote collateral and nonce verification run; expected measurement comparison remains TODO | | NVIDIA evidence verification | Implemented with source-review caveat | Overall and detached JWTs, digest links, nonce values, and returned GPU claims are processed; selected per-GPU check semantics need code-owner review | | Combined module discovery | Implemented | The unified client discovers one CPU module and an optional NVIDIA evidence bundle; the bundle can contain multiple detached GPU claims | | Automatic approved-image comparison | Not implemented in reviewed CPU paths | Required before claiming that the verifier proves the expected release image | | Multi-GPU serving-path appraisal | Partial | The token parser handles multiple returned GPU claims, but the public result does not expose per-GPU verdicts or prove exact-set session and scheduler binding | Attestation evidence can still provide meaningful hardware authenticity and freshness checks. The missing policy checks limit the conclusion: a pass must not be described as complete proof that an approved release image processed the request. ## Model status The documented model set includes: * `qwen36-27b`; * `glm-5.2`, resolving to `zai-org/GLM-5.2`; * `deepgram/general-nova-3` for audio transcription. ## Known gaps | Gap | Impact | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | Multi-GPU evidence contract | Developers cannot audit from the public result that every participating GPU, and only those GPUs, were appraised and bound to serving | | Reproducible enclave images | A verifier cannot independently rebuild a release and connect the resulting measurements to production | ## Priority sequence 1. Publish reproducible images, provenance, and release-to-measurement reference sets. 2. Expose per-GPU verdicts, review the current check semantics, and enforce exact-set backend binding for multi-GPU inference. 3. Resolve package/runtime version reporting. ## Trust progression ```mermaid theme={"system"} flowchart LR A["Manufacturer evidence
signature, collateral, nonce"] --> B["Release policy
approved measurements and TCB"] B --> C["Route assurance
model and backend classification"] C --> D["Reproducible provenance
source to deployed measurement"] ``` Each stage answers a different question. Manufacturer evidence authenticates hardware claims. Release policy decides whether measured values are approved. Route assurance binds that decision to a model path. Reproducible provenance connects the approved measurements to source and build inputs. Status entries distinguish source review from live testing. The presence of an API route alone does not establish that a model or end-to-end workflow is operational. # Production Checklist Source: https://docs.prem.io/production-checklist Complete these steps before you send production traffic to Prem API. Complete this checklist before you send production traffic. Each item links to the page with the full instructions. ## Keys and secrets * Store your Key Encryption Key (KEK) in a secure location with backups. If you lose it, Prem cannot recover your data. See [Encryption](/encryption). * Make a backup of your DEK store. If you lose it, you lose access to your uploaded files. * Store all keys in environment variables. Do not commit keys to source control. See [Quickstart](/quickstart). * Use one API key for each environment, for example `development`, `staging`, and `production`. See [API Keys](/api-keys). ## API key security * Give each API key only the scopes that it needs. A key without explicit scopes has full permissions. See [API Keys](/api-keys). * Add IP restrictions to the keys for critical integrations. * Rotate your API keys on a regular schedule. * Do not share one API key between teams or environments. ## Reliability * Retry failed requests with exponential backoff and random jitter. See [Rate limits](/rate-limits). * Read the `Retry-After` header on `429` responses. Wait for that time before you retry. * Use idempotency keys for requests that you cannot repeat safely. See [Idempotency](/idempotency). * Handle each error code that the API returns. See [Errors](/errors). * Record the `support_id` value from error responses. Contact support in the week after the error occurs. See [Errors](/errors). ## Capacity and cost * Confirm that your tier gives enough requests per second, tokens per minute, and concurrent requests. See [Rate limits](/rate-limits). * Set a monthly budget limit for your organization. See [Limits](/billing/limits). * Configure auto top-up so that your balance does not reach zero. See [Balance](/billing/balance). * Monitor your token usage in the dashboard. See [Usage](/billing/usage). ## Verification * Keep attestation on. The SDK verifies the enclave automatically because `attest: true` is the default. See [Attestation](/attestation). * For independent verification, use the `@premai/reticle` package to check the attestation reports yourself. * Get the current endpoint values from [`dashboard.prem.io/endpoints.json`](https://dashboard.prem.io/endpoints.json) at deployment time. ## Before launch * Monitor the platform availability on the [status page](https://status.premai.io). * Read the [Security Model](/security-model) page, including the limitations section. * Read the [Platform Status](/platform-status) page for the current feature availability. * Confirm that the models in your code appear in [Models & Pricing](/billing/models-and-pricing). Questions before a production launch? Contact us at [support@premai.io](mailto:support@premai.io). # Quickstart Source: https://docs.prem.io/quickstart Send chat and audio requests with client-side encryption through the Prem SDK or local proxy. Prem API exposes client-encrypted chat and audio routes. Use the TypeScript SDK directly, or run the bundled **Confidential Proxy** for the documented OpenAI- and Anthropic-compatible routes. If you use an AI coding agent, load Prem API into the context of your agent with one command. See [Use LLMs](/use-llms). ## Use the Prem API SDK ### 1. Create an API key 1. Open the [dashboard](https://dashboard.prem.io). Sign in or register. 2. Go to the [API](https://dashboard.prem.io/api-keys) section. 3. Create a new API key. Copy the key to a safe location. Store the API key in an environment variable, for example `PREM_API_KEY`. Do not commit the API key to source control. ### 2. Install the SDK and make your first call Install the TypeScript SDK from npm: ```bash theme={"system"} npm install @premai/api-sdk@1.0.59 ``` The SDK runs natively on Node.js, Bun, Deno, and [Bare](https://github.com/holepunchto/bare). It needs no native add-ons. You use one client library across all of them, from a backend service to a mobile app. See the [React Native guide](/guides/react-native). Then use this code to initialize the client and call the API: ```typescript theme={"system"} import { createRvencClient } from "@premai/api-sdk"; async function main() { const apiKey = process.env.PREM_API_KEY; const clientKEK = process.env.CLIENT_KEK; if (!apiKey || !clientKEK) { throw new Error("Set PREM_API_KEY and CLIENT_KEK"); } const client = await createRvencClient({ apiKey, clientKEK }); const response = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Reply with exactly OK." }], reasoning_effort: "none", max_completion_tokens: 128, }); console.log(response.choices[0]?.message?.content); console.log("Resolved model:", response.model); } main().catch((error) => { console.error(error); process.exitCode = 1; }); ``` You must set all four environment variables. `CLIENT_KEK` is your Key Encryption Key (KEK). The SDK expects a 32-byte key encoded as exactly 64 hexadecimal characters. The KEK protects your encryption keys and does not leave your device. See [Encryption](/encryption). ```bash theme={"system"} export PREM_API_KEY="your-api-key" export CLIENT_KEK="$(openssl rand -hex 32)" export PROXY_URL="https://gateway.prem.io" export ENCLAVE_URL="https://conf-engine.prem.io" ``` Store the KEK in a secret manager and reuse it. Do not regenerate it on each process start. Get the latest endpoint values from [`dashboard.prem.io/endpoints.json`](https://dashboard.prem.io/endpoints.json). ### 3. Run a request Run your script. The console shows the response. For error codes and HTTP conventions, see [Errors](/errors). For request limits, see [Rate limits](/rate-limits). ## Use the OpenAI SDK Run the bundled **Confidential Proxy** to expose OpenAI-compatible routes on your machine. Point the `baseURL` of an OpenAI client at the Confidential Proxy. ```bash theme={"system"} npx -p @premai/api-sdk@1.0.59 confidential-proxy \ --compat openai \ --kek "$CLIENT_KEK" ``` The current CLI reads `PROXY_URL` and `ENCLAVE_URL` from the environment. Pass the KEK with `--kek`; `CLIENT_KEK` is not currently bound as a CLI environment option. Get the latest endpoint values from [`dashboard.prem.io/endpoints.json`](https://dashboard.prem.io/endpoints.json). When the Confidential Proxy runs, install the OpenAI JavaScript SDK. Point the SDK at your local `/v1` URL. The client sends your API key as the bearer token with each request: ```typescript theme={"system"} import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.PREM_API_KEY!, baseURL: "http://127.0.0.1:8787/v1", }); const stream = await client.chat.completions.create({ model: "glm-5.2", messages: [{ role: "user", content: "Hello!" }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } ``` See [Confidential Proxy](/confidential-proxy) for all proxy options: Anthropic compatibility, daemon mode, and the full configuration. ## Verify the selected route Model-list and completion responses expose the requested alias and, for OpenAI-format completions, the resolved model ID. They do **not** expose an assurance profile. Do not classify a route from its model name. For `glm-5.2`, the completion response returns the resolved backend ID in `response.model`. See [Platform Status](/platform-status) for the current assurance boundary. ## Next steps Chat completions and other endpoints in detail. Step-by-step guides for common flows (chat, audio, and more). Available models, pricing tiers, and deployment strategies. Building a bot, agent loop, or batch job? The config and gotchas that matter unattended. # Rate limits Source: https://docs.prem.io/rate-limits Understand API rate limits and restrictions. Rate limits restrict the number of requests that a user or client can send to the API in a given period. ## The purpose of rate limits Rate limits are common for APIs. We apply them for these reasons: * **Rate limits help protect the API against abuse or misuse.** For example, a malicious actor could flood the API with requests to overload it or to cause a disruption in service. Rate limits prevent this activity. * **Rate limits help give all users fair access to the API.** If one person or organization sends an excessive number of requests, the API can become slow for all other users. We limit the number of requests from a single user. So the largest possible number of people can use the API without slowdowns. * **Rate limits help us manage the total load on our infrastructure.** If requests to the API increase quickly, the servers can have performance problems. Rate limits help us keep a smooth and constant experience for all users. Read this full document to understand how our rate limit system operates. We include code examples and possible solutions for common problems. ## The operation of rate limits We measure rate limits in six ways: * **RPS** (requests per second) * **TPM** (tokens per minute) * **Tokens DQ** (tokens daily quota) * **concurrent requests** * **APM** (audio minutes per minute) * **Audio DQ** (audio minutes daily quota) Not all limit types are active at the same time. You can reach a rate limit on any active option, whichever occurs first. For example, assume that your concurrent request limit is 5 and your TPM limit is 150k. If you send 5 requests with only 100 tokens to the chat completions endpoint, you fill your concurrent request limit. This occurs although the 5 requests do not use 150k tokens. Also note these points: * **We define rate limits at the organization level.** All users and API keys in an organization share the same rate limit pool. * **Rate limits vary by request type.** General API requests and inference requests have different rate limits. * **We also set limits on token usage and audio processing time.** These limits refill continuously at the refill rate of your tier. We can enforce limits per minute and/or per day. Not all periods are active at the same time. ## Identifier scope The rate limiter uses the most specific and reliable identifier that is available to track usage: * **Organization ID**: The primary rate limit scope (tied to your API key) * **API Key**: The rate limiter uses this if the organization context is not clearly available * **User ID**: The rate limiter uses this if the organization context is not available * **IP Address**: The rate limiter uses this only when no other identifier is available (e.g., unauthenticated or anonymous requests) So all requests that use the API keys of the same organization share the same rate limit bucket. ### Rate limits by tier Rate limits vary with the **tier of your organization**. Each tier defines different capacities and refill rates for each request type. ### Available request types * `DEFAULT`: General-purpose API requests (e.g., models, projects, settings) * `INFERENCE`: Paid model inference requests (e.g., chat completions, embeddings) * `INFERENCE_FREE`: Free model inference requests * `AUTH`: Authentication-related requests (e.g., login, token exchanges) ### Request limits Request limits control the number of API requests that you can send per second. Each request type has a different limit that matches its sensitivity and resource usage. | Tier | Type | Capacity | Refill Rate (tokens/sec) | | -------- | --------------- | -------- | ------------------------ | | `BASE` | DEFAULT | 50 | 5 | | `BASE` | INFERENCE | 5 | 1 | | `BASE` | INFERENCE\_FREE | 1 | 1 | | `BASE` | AUTH | 5 | 1 | | `TIER_1` | DEFAULT | 150 | 15 | | `TIER_1` | INFERENCE | 5 | 1 | | `TIER_1` | INFERENCE\_FREE | 1 | 1 | | `TIER_1` | AUTH | 5 | 1 | | `TIER_2` | DEFAULT | 450 | 45 | | `TIER_2` | INFERENCE | 5 | 1 | | `TIER_2` | INFERENCE\_FREE | 1 | 1 | | `TIER_2` | AUTH | 5 | 1 | | `TIER_3` | DEFAULT | 1000 | 100 | | `TIER_3` | INFERENCE | 5 | 1 | | `TIER_3` | INFERENCE\_FREE | 1 | 1 | | `TIER_3` | AUTH | 5 | 1 | We use a **token bucket algorithm**. Each request consumes 1 token from your bucket. The bucket refills automatically at the specified rate per second. This permits short bursts of requests but prevents continuous overuse. ### Usage tiers You can see the rate and usage limits for your organization in the limits section of your account settings. When your usage of our API increases, we automatically move you to the next usage tier. This usually increases the rate limits for most endpoints. | Tier | Qualification | Usage limits | | ------ | -------------------------------------------------------- | ---------------- | | Base | Default tier | \$100 / month | | Tier 1 | \$5 paid | \$500 / month | | Tier 2 | \$100 paid and 7+ days since first successful payment | \$5,000 / month | | Tier 3 | \$1,000 paid and 30+ days since first successful payment | \$20,000 / month | ### Token limits (TPM / Tokens DQ) Token limits apply to the inference endpoints. These limits restrict the total number of tokens that you can process in a given period. We can enforce token limits per minute (TPM) and/or per day (Tokens DQ). Not all periods are active at the same time. | Tier | Token Limit | Refill Rate | Period | | -------- | ----------- | ----------- | ---------- | | `BASE` | 38,000 | 38,000 | per minute | | `TIER_1` | 540,000 | 540,000 | per minute | | `TIER_2` | 1,000,000 | 1,000,000 | per minute | | `TIER_3` | 2,500,000 | 2,500,000 | per minute | Token limits operate in the same way as request limits: * Each tier has a **maximum capacity** and a **refill rate** * When you process a request, the request consumes the **total tokens used** from your bucket * Your bucket **refills continuously** at the specified rate per period ### Audio processing limits (APM / Audio DQ) An **audio duration limit** also applies to the audio transcription and translation endpoints. This limit restricts the total duration of audio that you can process in a given period. We can enforce audio limits per minute (APM) and/or per day (Audio DQ). Not all periods are active at the same time. | Tier | Audio Limit (seconds) | Refill Rate (sec) | Period | | -------- | --------------------- | ----------------- | ---------- | | `BASE` | 10 | 10 | per minute | | `TIER_1` | 60 | 60 | per minute | | `TIER_2` | 300 | 300 | per minute | | `TIER_3` | 1,200 | 1,200 | per minute | Audio limits operate in the same way as request limits: * Each tier has a **maximum capacity** and a **refill rate**. * When you process audio, the audio consumes its **duration in seconds** from your bucket * Your bucket **refills continuously** at the specified rate per period ### Concurrent request limits Concurrent request limits control the number of inference requests that your organization can **process at the same time**. This limit is separate from the request rate limit (RPS). | Tier | Concurrent Requests | | -------- | ------------------- | | `BASE` | 5 | | `TIER_1` | 50 | | `TIER_2` | 500 | | `TIER_3` | 5,000 | When you reach your concurrent request limit, more requests receive a `429` error until an active request completes. ### Rate limits in headers You can see your rate limit in your account settings. You can also see important rate limit information in the headers of the HTTP response. The response can include these header fields: | Field | Sample Value | Description | | ----------- | ------------ | ---------------------------------------------------- | | Retry-After | 1 | The time in seconds until you can retry the request. | ## Error mitigation ### Steps to decrease rate limit errors Be careful when you give users programmatic access, bulk processing features, and automated posting. Enable these features only for trusted customers. Set a usage limit for each user in a specified period (daily, weekly, or monthly). This protects against automated and high-volume misuse. Add a hard cap or a manual review process for users who exceed the limit. ### Retry with exponential backoff One simple way to prevent rate limit errors is to retry requests automatically with a random exponential backoff. With this method, the client sleeps for a short time when a rate limit error occurs. Then the client retries the unsuccessful request. If the request is again unsuccessful, the client increases the sleep time and retries again. This continues until the request is successful or until the client reaches a maximum number of retries. This approach has many benefits: * Automatic retries let you recover from rate limit errors without crashes or lost data * With exponential backoff, the first retries occur quickly, and later retries have longer delays * Random jitter in the delay prevents all retries from occurring at the same time Unsuccessful requests count against your rate limit. Do not send the same request again and again without a delay. The example below uses exponential backoff. #### Example: This advanced implementation uses the `Retry-After` header from the API response for more efficient retries: ```typescript theme={"system"} import createRvencClient from "@premai/api-sdk"; async function retryWithExponentialBackoff( fn: () => Promise, maxRetries: number = 6, baseDelay: number = 1000 ): Promise { let retries = 0; while (true) { try { return await fn(); } catch (error: any) { const isRateLimitError = error?.status === 429; if (!isRateLimitError || retries >= maxRetries) { throw error; } // Some 429 responses (e.g. from an upstream backend) omit Retry-After. // Fall back to exponential backoff instead of retrying immediately. const retryAfterHeader = error?.headers?.["retry-after"]; const parsedRetryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : NaN; const delay = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter * 1000 : baseDelay * 2 ** retries; retries++; console.log( `Rate limited (${error?.log?.rate_limit?.tier || 'unknown'} tier). ` + `Retrying in ${Math.round(delay)}ms... (attempt ${retries}/${maxRetries})` ); await new Promise((resolve) => setTimeout(resolve, delay)); } } } // Usage async function main() { const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK }); try { const response = await retryWithExponentialBackoff( () => client.chat.completions.create({ model: "glm-5.2", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "What is the capital of France?" }, ], }), 6, 1000 ); console.log(response.choices[0].message.content); } catch (error) { console.error("Request failed after retries:", error); } } main().catch(console.error); ``` ### Handle concurrent request limits If your application sends many requests at the same time, monitor the concurrent request limits. Use a request queue that processes requests in sequence or in controlled batches. This prevents errors from the concurrent request limit. ```typescript theme={"system"} import createRvencClient from "@premai/api-sdk"; class RequestQueue { private maxConcurrent: number; private running: number = 0; private queue: Array<() => void> = []; constructor(maxConcurrent: number = 3) { this.maxConcurrent = maxConcurrent; } async execute(requestFn: () => Promise): Promise { while (this.running >= this.maxConcurrent) { await new Promise((resolve) => this.queue.push(resolve)); } this.running++; try { return await requestFn(); } finally { this.running--; const resolve = this.queue.shift(); if (resolve) resolve(); } } } // Usage async function main() { const client = await createRvencClient({ apiKey: process.env.PREM_API_KEY, clientKEK: process.env.CLIENT_KEK }); const queue = new RequestQueue(3); // max 3 concurrent requests const prompts = [ "Explain photosynthesis", "What is machine learning?", "Describe the water cycle", "Explain gravity", "What is DNA?", ]; try { const results = await Promise.all( prompts.map((prompt) => queue.execute(() => client.chat.completions.create({ model: "glm-5.2", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: prompt }, ], }) ) ) ); results.forEach((response, index) => { console.log(`Response ${index + 1}:`, response.choices[0].message.content); }); } catch (error) { console.error("One or more requests failed:", error); } } main().catch(console.error); ``` ## Tips for developers * **Group related operations** to decrease the number of requests. * **Cache frequently accessed data** so that you do not request the same data again and again. * **Monitor response headers** for usage patterns. Add alerts that operate when you are near the limits. * **Use the `Retry-After` header** from the error response to set the correct delay before your retry. * **Implement proper error handling**: Always check for 429 status codes and handle them safely. ## Example error response When your bucket has no tokens, your request returns an error with a `429 Too Many Requests` status. The response includes a `Retry-After` header (in seconds). This header tells you the time to wait before a retry. ```json theme={"system"} { "status": 429, "error": "Rate limit exceeded, try again in 1 seconds", "log": { "support": "Reach out to support@premai.io to request a higher tier, or upgrade your plan.", "rate_limit": { "resource": "/rvenc/chat/completions", "tier": "BASE", "type": "INFERENCE" } } } ``` ## Non-standard 429 responses The example above is the shape returned by the API's own rate limiter. Not every `429` you receive is guaranteed to look like it. Rate limiting can also happen further upstream (for example, at the model backend rather than the gateway), and those responses can carry a `429` status with a generic body and no `Retry-After` header at all, for example: ```json theme={"system"} { "error": { "message": "Internal server error", "type": "server_error" } } ``` This body shape reads like a `500`, but the status code is still `429`. Automated clients should: * **Branch on the HTTP status code, not the response body.** Treat any `429` as a rate limit, regardless of the `error.type` or message text. * **Never assume `Retry-After` is present.** Fall back to your own exponential backoff (with jitter) when the header is missing, instead of parsing a value that may not exist. If you consistently see `429` responses without a `Retry-After` header or with an unexpected body, [contact us](/contact-us) with the `support_id` (if present) and a timestamp so we can trace which layer applied the limit. ## Higher rate limits If you need higher rate limits for your use case, you can: * **Contact our support team** at [support@premai.io](mailto:support@premai.io) for enterprise plans with custom rate limits # API Capabilities Source: https://docs.prem.io/router/capabilities Use tool calls, structured output, reasoning controls, vision, log probabilities, and streamed usage with Router. Beta Router uses the OpenAI-compatible Chat Completions API. The capabilities on this page apply to the six models in the [Models](/router/models) base catalog. Additional key-dependent models can have different capabilities. Use the [quickstart](/router/overview#make-your-first-request) to create the `client` used in these examples. These are API capabilities. Individual harnesses may not expose every control in their user interface. Use authenticated `GET /v1/models` with the same API key for the exact capability flags available to that key. Router is not confidential. Do not send secrets, personal data, regulated data, or other sensitive data. ## Compatibility at a glance | Capability | Current behavior | | ---------------------------- | ------------------------------------------------------------------------ | | Tool calls | Supported by all six table models | | Multiple tool calls | Supported by all six table models | | `parallel_tool_calls: false` | Returns at most one tool call for all six table models | | Strict JSON Schema | Supported by all six table models through `response_format` | | Streamed usage | Supported by all six table models through `stream_options.include_usage` | | Reasoning toggle | Supported by all six table models through `reasoning.enabled` | | `max_completion_tokens` | Accepted for all six table models | ## Tool calling Pass standard OpenAI function tools. Set `parallel_tool_calls=False` when your application must execute one action at a time. ```python theme={"system"} response = client.chat.completions.create( model="kimi-k3", messages=[{"role": "user", "content": "What is the weather in Paris?"}], tools=[ { "type": "function", "function": { "name": "get_weather", "description": "Get the weather for a city.", "strict": True, "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], "additionalProperties": False, }, }, } ], tool_choice="auto", parallel_tool_calls=False, ) for call in response.choices[0].message.tool_calls or []: print(call.id, call.function.name, call.function.arguments) ``` To continue the tool loop, execute the function, append the complete assistant message, then append a `tool` message whose `tool_call_id` matches the call ID. Send the updated message list in the next request. ## Structured output Use strict JSON Schema when your application needs a predictable response shape. Include the word `JSON` in the request message. ```python theme={"system"} response = client.chat.completions.create( model="kimi-k3", messages=[{"role": "user", "content": "Return a JSON summary."}], response_format={ "type": "json_schema", "json_schema": { "name": "result", "strict": True, "schema": { "type": "object", "properties": {"summary": {"type": "string"}}, "required": ["summary"], "additionalProperties": False, }, }, }, ) ``` ## Reasoning and output limits Use `reasoning.enabled` to turn model reasoning on or off. Both `max_tokens` and `max_completion_tokens` are accepted; do not send conflicting values for both fields. ```python theme={"system"} response = client.chat.completions.create( model="kimi-k3", messages=[{"role": "user", "content": "Summarize this."}], max_completion_tokens=4096, extra_body={"reasoning": {"enabled": False}}, ) ``` ## Streamed usage Set `include_usage` to receive token usage in the final stream chunk. That chunk can contain an empty `choices` array. ```python theme={"system"} stream = client.chat.completions.create( model="kimi-k3", messages=[{"role": "user", "content": "Hello"}], stream=True, stream_options={"include_usage": True}, ) for chunk in stream: if chunk.usage is not None: print(chunk.usage.total_tokens) ``` ## Vision and log probabilities Vision support currently covers base64-encoded images in `image_url` content parts. | Model ID | Base64 images | Logprobs | | ----------------- | ------------- | ------------- | | `kimi-k3` | Yes | No | | `qwen-3.7-max` | No | Yes | | `qwen-3.7-plus` | Yes | Yes | | `qwen-3.6-plus` | Yes | Yes | | `qwen-3.5-9b` | Yes | Key-dependent | | `deepseek-v4-pro` | No | Key-dependent | Additional models are available only when returned by authenticated `GET /v1/models` for the same key: | Model ID | Base64 images | Logprobs | | ------------------- | ------------- | -------- | | `deepseek-v4-flash` | No | Yes | | `qwen-3.8-max` | Yes | Yes | Keep reasoning enabled when you use `qwen-3.8-max`. For a model that supports log probabilities, use the standard OpenAI fields: ```python theme={"system"} response = client.chat.completions.create( model="qwen-3.5-9b", messages=[{"role": "user", "content": "Reply with one word."}], logprobs=True, top_logprobs=2, ) ``` See [Custom Providers and Coding Tools](/router/integrations) for OpenCode, Pi, and other harnesses. # Custom Providers and Coding Tools Source: https://docs.prem.io/router/integrations Configure Router in supported OpenAI-compatible Chat Completions tools. Beta Request a Router API key through the [Contact Us form](https://form.typeform.com/to/vZnBDhzs) before configuration. See the [Router overview](/router/overview) for the quickstart, [Models](/router/models) for key-specific model discovery, and [API capabilities](/router/capabilities) for tool calling and structured output. Set your API key before you start a coding tool: ```bash theme={"system"} export PREM_ROUTER_API_KEY="your-api-key" ``` Router is not confidential. Do not send sensitive data. During the beta, Router supports Chat Completions only. A tool that requires the Responses API cannot connect yet. ## Choose the correct path | Requirement | Use | | ------------------------------------------- | ----------------------------------------------------- | | Sensitive prompts, files, or regulated data | [Confidential API](/confidential-proxy) | | Broader current chat-model catalog | Router | | OpenAI-compatible Chat Completions client | Either path, with different credentials and endpoints | | Anthropic Messages or Responses API client | Router is not currently compatible | The Router key is separate from the Confidential API key. Do not substitute `PREM_API_KEY` for `PREM_ROUTER_API_KEY` or reuse Confidential Proxy KEK settings with Router. ## Add Router as a custom provider In your tool, choose **Add provider**, then select **Custom**, **OpenAI-compatible**, or **OpenAI Chat Completions**. Enter these values: | Field | Value | | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Provider name | `Router` | | Provider or API type | OpenAI-compatible Chat Completions | | Base URL (`baseURL`, `baseUrl`, `apiBase`, or `openAiBaseUrl`) | `https://router.prem.io/v1` | | Full endpoint | `https://router.prem.io/v1/chat/completions` only when the tool explicitly asks for a complete endpoint URL | | API key (`apiKey`, `openAiApiKey`, or Bearer token) | Paste your Router API key in a GUI; use `PREM_ROUTER_API_KEY` only when the tool supports environment variables | | Model (`model`, `modelId`, `apiModelId`, or `openAiModelId`) | Use an exact model ID returned by `GET /v1/models` for the same API key | | Streaming | On or automatic | | Responses API | Off | | Organization or project | Leave blank | Before you configure a model, call authenticated `GET /v1/models` with the same API key. See [Models](/router/models) for the command. When the field is named **Base URL** or **API base**, do not append `/chat/completions`. The final request URL should contain `/v1` exactly once. Enter `kimi-k3` without a provider prefix; `prem/kimi-k3` is only the OpenCode model selector. Select Chat Completions rather than the Responses API. No custom headers are required when the tool has a dedicated API key field. If it only accepts custom headers, add `Authorization: Bearer `. Do not enter the literal text `$PREM_ROUTER_API_KEY` in a GUI unless that tool documents environment-variable expansion. Streaming requirements can differ by API key. If a model returned by `GET /v1/models` includes `"streaming_required": true`, enable streaming or leave it on automatic. OpenCode and Pi support streamed model responses. Test the custom provider outside your tool with the same connection settings: ```bash theme={"system"} curl --silent --show-error https://router.prem.io/v1/chat/completions \ --header "Authorization: Bearer $PREM_ROUTER_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "model": "kimi-k3", "messages": [ {"role": "user", "content": "Reply with exactly: router ok"} ] }' ``` A successful response includes `"model":"kimi-k3"` and an assistant message. ## OpenCode Create `opencode.json` in your project root: ```json theme={"system"} { "$schema": "https://opencode.ai/config.json", "model": "prem/kimi-k3", "provider": { "prem": { "npm": "@ai-sdk/openai-compatible", "name": "Router", "options": { "baseURL": "https://router.prem.io/v1", "apiKey": "{env:PREM_ROUTER_API_KEY}" }, "models": { "kimi-k3": { "name": "Kimi K3" }, "qwen-3.7-max": { "name": "Qwen 3.7 Max" }, "qwen-3.7-plus": { "name": "Qwen 3.7 Plus" }, "qwen-3.6-plus": { "name": "Qwen 3.6 Plus" }, "qwen-3.5-9b": { "name": "Qwen 3.5 9B" }, "deepseek-v4-pro": { "name": "DeepSeek V4 Pro" } } } } } ``` Start OpenCode: ```bash theme={"system"} opencode ``` OpenCode uses `prem/kimi-k3` as its default provider and model selector. To switch models, use `prem/` with an exact ID returned by `GET /v1/models` for the same API key. If the response contains either additional model, you can add the matching entries to the `models` object: ```json theme={"system"} { "deepseek-v4-flash": { "name": "DeepSeek V4 Flash" }, "qwen-3.8-max": { "name": "Qwen 3.8 Max" } } ``` Add only the entries returned for the active key. ## OpenClaw Add a separate Router provider to `~/.openclaw/openclaw.json`. Keep the confidential provider under a different name if you use both: ```json theme={"system"} { "models": { "mode": "merge", "providers": { "prem-router": { "baseUrl": "https://router.prem.io/v1", "apiKey": "${PREM_ROUTER_API_KEY}", "api": "openai-completions", "models": [ { "id": "kimi-k3", "name": "Kimi K3 through Router", "reasoning": true, "input": ["text"], "contextWindow": 1000000, "maxTokens": 8192 } ] } } }, "agents": { "defaults": { "model": { "primary": "prem-router/kimi-k3" } } } } ``` The example uses `kimi-k3`. Confirm that the model appears in `GET /v1/models` for the active key. If you select another returned model, update both the model `id` and `agents.defaults.model.primary`. Set `contextWindow` from that model's returned `context_length`. Run `openclaw config validate` before starting an agent. Router does not provide enclave confidentiality for the model request, and OpenClaw's local tools remain outside the confidential runtime. ## Hermes Agent Add a named Router provider to `~/.hermes/config.yaml`: ```yaml theme={"system"} providers: prem-router: api: https://router.prem.io/v1 key_env: PREM_ROUTER_API_KEY transport: chat_completions default_model: kimi-k3 models: kimi-k3: context_length: 1000000 model: default: kimi-k3 provider: custom:prem-router context_length: 1000000 ``` The example uses `kimi-k3`. Confirm that the model appears in `GET /v1/models` for the active key. If you select another returned model, update `default_model`, both model IDs, and both context-length values. Run `hermes config check`, then use `hermes --oneshot "Reply with exactly: router ok"` for the first request. ## Goose Set Goose's built-in OpenAI provider to the Router endpoint: ```bash theme={"system"} export GOOSE_PROVIDER="openai" export GOOSE_MODEL="kimi-k3" export OPENAI_API_KEY="$PREM_ROUTER_API_KEY" export OPENAI_HOST="https://router.prem.io" export OPENAI_BASE_PATH="v1/chat/completions" ``` The example uses `kimi-k3`. Confirm that the model appears in `GET /v1/models` for the active key. Otherwise, set `GOOSE_MODEL` to an exact returned model ID. Run a bounded request: ```bash theme={"system"} GOOSE_MODE=chat goose run \ --no-session \ --text "Reply with exactly: router ok" ``` ## Pi Add this provider to `~/.pi/agent/models.json`: ```json theme={"system"} { "providers": { "prem": { "baseUrl": "https://router.prem.io/v1", "api": "openai-completions", "apiKey": "$PREM_ROUTER_API_KEY", "compat": { "supportsStore": false, "supportsDeveloperRole": false, "supportsStrictMode": false, "maxTokensField": "max_tokens" }, "models": [ { "id": "kimi-k3", "name": "Kimi K3" }, { "id": "qwen-3.7-max", "name": "Qwen 3.7 Max" }, { "id": "qwen-3.7-plus", "name": "Qwen 3.7 Plus" }, { "id": "qwen-3.6-plus", "name": "Qwen 3.6 Plus" }, { "id": "qwen-3.5-9b", "name": "Qwen 3.5 9B" }, { "id": "deepseek-v4-pro", "name": "DeepSeek V4 Pro" } ] } } } ``` Start Pi with Router: ```bash theme={"system"} pi --provider prem --model kimi-k3 ``` Run a non-interactive test: ```bash theme={"system"} pi -p --provider prem --model kimi-k3 \ "Reply with exactly: router ok" ``` Replace `kimi-k3` only with an exact ID returned by `GET /v1/models` for the same API key. If the response contains either additional model, you can add the matching entries to the `models` array: ```json theme={"system"} [ { "id": "deepseek-v4-flash", "name": "DeepSeek V4 Flash" }, { "id": "qwen-3.8-max", "name": "Qwen 3.8 Max" } ] ``` Add only the entries returned for the active key. `supportsStrictMode` controls Pi function-tool metadata. It does not disable JSON Schema response formatting. # Models Source: https://docs.prem.io/router/models Browse Router model IDs and discover the models available to your API key. Beta Router provides a key-aware model catalog. Model availability, context windows, log probabilities, and streaming requirements can differ by API key. ## List models available to your key Call the authenticated Models endpoint with the same API key that you will use for chat requests: ```bash theme={"system"} curl --silent --show-error https://router.prem.io/v1/models \ --header "Authorization: Bearer $PREM_ROUTER_API_KEY" ``` With the OpenAI Python SDK: ```python theme={"system"} import os from openai import OpenAI client = OpenAI( api_key=os.environ["PREM_ROUTER_API_KEY"], base_url="https://router.prem.io/v1", ) for model in client.models.list().data: print(model.model_dump(exclude_none=True)) ``` The response includes: * `id`: The public model ID to send in chat requests. * `context_length`: The context window available to the key. * `input_modalities`: Supported input types. * `capabilities`: Feature flags such as tools, vision, and log probabilities. * `streaming_required`: Present and set to `true` when the key must use streaming for that model. ## Base catalog The base catalog currently includes six models: | Model ID | Family | Context window | Base64 images | Logprobs | Streaming | | ----------------- | -------- | ------------------ | ------------- | ------------- | ------------- | | `kimi-k3` | Kimi | Up to 1.05M tokens | Yes | No | Optional | | `qwen-3.7-max` | Qwen | 1M tokens | No | Yes | Key-dependent | | `qwen-3.7-plus` | Qwen | 1M tokens | Yes | Yes | Key-dependent | | `qwen-3.6-plus` | Qwen | 1M tokens | Yes | Yes | Key-dependent | | `qwen-3.5-9b` | Qwen | 262K tokens | Yes | Key-dependent | Optional | | `deepseek-v4-pro` | DeepSeek | Up to 1.05M tokens | No | Key-dependent | Optional | Use the exact lowercase model ID returned for your key. Do not add a provider prefix or a slash. ## Additional key-dependent models Some API keys also return these models: | Model ID | Family | Context window | Base64 images | Logprobs | Streaming | | ------------------- | -------- | -------------- | ------------- | -------- | --------- | | `deepseek-v4-flash` | DeepSeek | 1.05M tokens | No | Yes | Optional | | `qwen-3.8-max` | Qwen | 1M tokens | Yes | Yes | Optional | Use an additional model only when authenticated `GET /v1/models` returns its ID for the same key. `qwen-3.8-max` requires reasoning to remain enabled. If you change API keys, retrieve the model list again. Remove models that the new key does not return. ## Streaming requirements When a model includes `"streaming_required": true`, set `stream=True` in Python or `stream: true` in JavaScript. If the field is absent, streaming is optional. See [API capabilities](/router/capabilities) for tool calls, structured output, reasoning controls, vision, log probabilities, and streamed usage. # Router Source: https://docs.prem.io/router/overview Use Router to access a curated model catalog through an OpenAI-compatible API. Beta Router is a beta service that provides access to a curated model catalog through an OpenAI-compatible API. Router currently exposes a broader chat-model catalog than the Confidential API catalog documented on this site. The tradeoff is explicit: Router requests are not confidential. Use the Confidential API for sensitive data and use Router when model breadth is the priority. Router is not confidential. Do not send secrets, personal data, regulated data, or other sensitive data. Use [Prem API](/quickstart) for confidential processing. Router uses the Chat Completions API. All models accept text. Selected models accept base64 images. The model catalog and API behavior can change during the beta. ## Request an API key [Contact us](/contact-us) to request a Router API key. Include `Router access` in your message. Store the key in an environment variable. Do not commit it to source control. ```bash theme={"system"} export PREM_ROUTER_API_KEY="your-api-key" ``` The Router key is separate from the Confidential API key. `PREM_ROUTER_API_KEY` and `PREM_API_KEY` are not interchangeable. ## Make your first request ### Python Install the OpenAI Python SDK: ```bash theme={"system"} pip install openai ``` Create a Python file with this code: ```python theme={"system"} import os from openai import OpenAI client = OpenAI( api_key=os.environ["PREM_ROUTER_API_KEY"], base_url="https://router.prem.io/v1", ) response = client.chat.completions.create( model="kimi-k3", messages=[ { "role": "user", "content": "What can you help me build?", } ], ) print(response.choices[0].message.content) ``` Run the file. The response appears in your terminal. ### Node.js test Use Node.js 22 or later. Install the OpenAI JavaScript SDK: ```bash theme={"system"} npm install openai ``` Create a file named `router.test.mjs`: ```javascript theme={"system"} import assert from "node:assert/strict"; import test from "node:test"; import OpenAI from "openai"; const apiKey = process.env.PREM_ROUTER_API_KEY; if (!apiKey) { throw new Error("Set PREM_ROUTER_API_KEY before you run this test."); } const client = new OpenAI({ apiKey, baseURL: "https://router.prem.io/v1", maxRetries: 0, timeout: 120_000, }); test("Router returns a chat completion", async () => { const response = await client.chat.completions.create({ model: "kimi-k3", messages: [ { role: "user", content: "Reply with exactly: Router is working", }, ], }); assert.equal(response.model, "kimi-k3"); assert.equal(response.choices[0].message.role, "assistant"); assert.match(response.choices[0].message.content ?? "", /\S/); }); ``` Run the live test: ```bash theme={"system"} node --test router.test.mjs ``` ## Models Model availability, context windows, capabilities, and streaming requirements can differ by API key. Use authenticated `GET /v1/models` as the source of truth, then send an exact model ID from that response in your chat request. Router currently exposes a broader chat-model catalog than the Confidential API. This comparison is about catalog breadth, not confidentiality or model quality. A model listed for Router is not evidence that the same model runs in a confidential enclave. See [Models](/router/models) for the base catalog, additional key-dependent models, modality support, and streaming requirements. Browse model IDs and check which models are available to your API key. Use tool calls, structured output, reasoning, vision, log probabilities, and usage reporting. Configure Router in OpenCode, Pi, and other OpenAI-compatible tools. # Security Model Source: https://docs.prem.io/security-model Prem API threat boundaries, confidential-computing controls, attestation evidence, and current limitations. **Summary:** Prem API combines client-side encryption with confidential-computing hardware and attestation evidence. The strength of any claim depends on the verified evidence, an approved measurement/TCB policy, and the route that processed the request. The current limitations are documented below and on [Attestation](/attestation). ## The Problem With Trust Each AI API provider tells you that your data is safe. But traditional security relies on **trust**: * Trust that employees do not access server memory * Trust that the infrastructure provider does not inspect VMs * Trust that the privacy policy matches the actual implementation * Trust that a breach did not already occur Prem API combines hardware isolation with attestation evidence. A complete decision still requires manufacturer trust, approved measurements and TCB policy, route assurance, and deployment-specific controls. ## Trusted Execution Environments: The Foundation A Trusted Execution Environment (TEE) is designed to isolate a workload and its memory from the host operating system and hypervisor. The exact confidentiality and integrity guarantees depend on the hardware architecture, firmware, configuration, and known vulnerability state. Confidential-computing hardware protects designated workload memory according to the selected CPU or GPU architecture and configuration. A TEE is designed to restrict the host operating system and hypervisor from reading or modifying protected workload memory. Hardware evidence carries architecture-defined measurements. A verifier must compare them with an approved reference before treating a change as a policy failure. The TEE can produce hardware-signed evidence that includes measurements and security claims. A verifier must compare those values with an approved policy before concluding that the expected workload is running. ### An Analogy As a mental model, think of a sealed request processed inside a restricted chamber and returned in a new sealed envelope. The model explains the intended data boundary. It does not replace the hardware threat model, attestation policy, or software review. ### CPU Confidential Computing Prem API enclaves run on **Confidential Virtual Machines (CVMs)** with: * **AMD SEV-SNP**: Protects guest memory with hardware-managed keys and supplies an attestation report for policy evaluation. * **Intel TDX**: Creates an Intel trust domain with protected memory and a quote for policy evaluation. Reticle implements separate evidence-verification paths for SEV-SNP and TDX. Their evidence formats, trusted computing bases, and security properties are not interchangeable. ### GPU Confidential Computing GPU-backed inference introduces a second protected component. Reticle processes NVIDIA overall and detached GPU tokens, their signatures, digest links, and nonce claims. The current public result does not expose per-GPU verdicts or prove scheduler binding, and the selected per-GPU check semantics need code-owner review. Confidential-computing support also depends on the deployed GPU SKU, firmware, driver stack, and mode. “Hopper” and “Blackwell” are architecture-family labels, not attestation results. The CPU-to-GPU path and every plaintext-processing component must be included in the deployment threat model. GPU evidence does not automatically prove the complete application path. ## The Infrastructure Locations Prem API deployments can use Prem-owned hardware and rented provider capacity. Confirm the exact location, operator, hardware, and policy for the route that your workload uses. TEE evidence can reduce trust in the physical and cloud operator in both owned and rented environments. It does not make their complete operational risk identical. Physical controls, administrator access, firmware supply chain, geography, and incident response remain separate audit inputs. * **Owned infrastructure:** Prem controls more of the physical and operational stack. Attestation evidence does not replace physical-security and change-management review. * **Rented infrastructure:** The provider controls the physical host. Confidential-computing controls are designed to restrict host access, while attestation evidence lets a client inspect hardware claims and freshness. **For compliance teams:** Manufacturer evidence is one audit input. Current CPU Reticle verification authenticates evidence and freshness but does not yet compare measurements with an approved release reference. Geography and infrastructure ownership remain independent controls. **Legal scope:** Confidential computing can reduce the plaintext held by infrastructure operators, but it does not eliminate legal obligations, metadata disclosure, endpoint access, or jurisdictional risk. Obtain jurisdiction-specific legal advice for regulated deployments. ## Confidential processing boundary The intended boundary includes every service that decrypts or processes protected content. The Prem API Gateway handles encrypted payloads plus API-key, model, size, timing, routing, and billing metadata. A local Confidential Proxy is a separate plaintext component on the client side. The current public response does not expose a complete route-assurance profile. Verify the selected model path and deployment policy before transferring confidential-compute claims to a specific model. ## Attestation evidence and policy Attestation supplies evidence for selected hardware and workload claims. The client still needs an explicit acceptance policy: ```mermaid theme={"system"} flowchart TD A["Hardware Manufacturer (AMD / Intel / NVIDIA)"] -->|"signs attestation keys at the factory"| B["TEE Hardware (CPU + GPU)"] B -->|"records architecture-defined measurements"| C["Measured Workload State"] C -->|"generates"| D["Attestation Report"] D -->|"you verify"| H["Your Device (SDK or browser)"] ``` **The attestation report tells you:** 1. **Measurements and claims**: The report carries hardware-defined measurements and security-relevant fields. 2. **Evidence authenticity**: The verifier validates the applicable manufacturer certificate or key chain. 3. **Freshness**: The report contains the verifier's nonce. 4. **Policy decision**: A separate policy must decide whether measurements, TCB, debug, and confidential-mode claims are acceptable. Reticle can compile to WebAssembly for supported JavaScript environments. A browser integration can therefore run the verifier locally, but it must still use trusted collateral, approved references, and an explicit failure policy. **For non-technical readers:** Attestation is comparable to a signed hardware inspection record. The signature authenticates the source of the evidence. Your policy still decides whether the reported configuration is acceptable. See [Attestation](/attestation) for the full technical detail: CPU reports, GPU EAT tokens, certificate chains, and verification code examples. ## Layers of Defense Prem API uses several controls for different risks. These controls are not interchangeable, and one control does not guarantee that another remains effective after a failure: | Layer | What It Does | Plain-Language Impact | | ----------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | **Transport encryption** (TLS 1.3) | Encrypts the network connection | Protects against passive network observation within the TLS threat model | | **Client payload encryption** | Encrypts the request payload from the client to the protected processing path | Limits plaintext exposure at the Prem gateway | | **Post-quantum component** | Uses ML-KEM768 as part of a hybrid key exchange | Reduces reliance on classical public-key exchange alone | | **CPU isolation** (AMD SEV-SNP / Intel TDX) | Protects designated confidential-VM memory | Restricts host access according to the hardware and deployment configuration | | **GPU isolation** (NVIDIA Confidential Computing) | Protects designated GPU processing and memory | Requires supported hardware, firmware, drivers, mode, and verified evidence | | **Attestation** (hardware-signed, Rust/WASM verified) | Authenticates hardware evidence and freshness; policy must approve measurements and TCB | Reduces reliance on operator assertions | | **Client KEK custody** | You generate and retain the Key Encryption Key | Separates client key custody from the Prem API key and server-side routing | ## Common Questions The intended network-gateway exposure is encrypted content plus operational metadata. A compromise can still affect availability, routing, replay attempts, metadata, and software supply chain. The local Confidential Proxy is a plaintext component and must be secured separately. Distinguish the Prem API Gateway from the local Confidential Proxy. The gateway is intended to handle ciphertext and operational metadata. The local proxy handles plaintext, the API key, and the KEK on your machine; compromise of that local process can expose content and credentials. The observed payload is encrypted by TLS and the client payload-encryption layer. Security still depends on correct endpoint authentication, key handling, implementation, and the assumptions of the hybrid key exchange. Prem API uses a hybrid exchange that combines ML-KEM768 and X25519. The design reduces reliance on X25519 alone for the harvest-now, decrypt-later threat model. It does not support an absolute claim about future cryptographic security. TEE isolation is intended to restrict host access, while deployment access controls and change management reduce operator risk. The current CPU verifier does not yet compare measurements with an approved release reference, so operational controls and audit evidence remain necessary. Hardware evidence contains measurements that can change when measured components change. Detection requires a verifier to compare those values with an approved reference. That expected-measurement check remains TODO in the reviewed SEV-SNP and TDX client paths. Reticle is implemented in Rust and can compile to WebAssembly. Rust prevents many memory-safety bugs in safe code, while unsafe code, dependencies, logic errors, policy errors, and runtime vulnerabilities still require review and testing. Confidential-computing hardware is designed to reduce trust in the host operator. Manufacturer evidence, approved measurements, firmware policy, physical controls, and incident response must all be evaluated; infrastructure ownership does not make those risks identical. ## Shared Responsibility Model Prem operates the managed infrastructure and protected-runtime controls. Customers remain responsible for their applications, local proxy hosts, keys, tool integrations, and workload policy. ### The responsibilities of Prem * Prem operates the confidential-computing and attestation infrastructure * Prem maintains the documented transport, payload-encryption, and protected-processing controls * Prem maintains the documented model serving and protected-runtime controls * Prem manages the physical infrastructure security and the platform availability ### Your responsibilities * **Key management:** Generate, store, and rotate your encryption keys, for example the Key Encryption Key (**KEK**). Do not lose your master key. If you lose it, Prem cannot recover your data. * **Application security:** Secure the endpoints and devices where the Prem API SDK runs. The SDK decrypts data on your local hardware. * **Prompt and model level security:** Defend against prompt injections and jailbreaks in your specific AI workflows. ## Limitations No security system is absolute. The limitations above define the current assurance boundary and the checks that remain necessary. **Prem API does not protect against:** * **Hardware-level side-channel attacks**: Researchers found theoretical and practical side-channel vulnerabilities in TEE hardware. The manufacturers patch these vulnerabilities. The attestation reports include the firmware versions, so you can check the patch levels and we can enforce minimum requirements. Side-channel attacks could be possible in theory. But Prem API selects deployment locations that meet strict security criteria and can apply counter mechanisms to detect invalid states. This is an industry-wide challenge, not a challenge unique to Prem API. * **A compromised hardware manufacturer**: If the root signing keys of AMD, Intel, or NVIDIA are compromised, the attestation guarantees become weaker. This is the shared root of trust for the full confidential computing industry. * **Model behavior**: Prem API protects the privacy of your data during processing. Prem API does not control what the AI model itself does with context during a single inference pass. For example, model memorization is a model-level concern, not an infrastructure concern. * **Metadata**: The Prem gateway handles time, payload size, API key, model, routing, billing, and rate-limit metadata. Payload confidentiality depends on the client encryption path and the selected processing route. See the [Encryption](/encryption) reference for the full cryptographic specification. Continue to [Attestation](/attestation) for the attestation detail: reports, tokens, and verification code. # Trust & Compliance Source: https://docs.prem.io/trust-and-compliance Certifications, audits, and the verifiable security properties of the Prem API platform. This page collects the trust resources for the Prem API platform: certifications, planned audits, and the security properties that you can verify yourself. ## Certifications * **SOC 2 Type I**: An independent auditor issued a SOC 2 Type I report for Prem. The report is available on request. Contact us at [support@premai.io](mailto:support@premai.io). ## Independent verification Prem API does not ask you to trust a report alone. The core security properties are verifiable: Hardware-signed proof of the code and the hardware that process your data. Verify it with the SDK or with the open reticle library. End-to-end encryption with post-quantum algorithms. You hold the keys. Zero retention of inference content, and the metadata list that we do keep. The full threat model, the trust boundaries, and the limitations. The verification tools are open: * **TypeScript SDK**: [premAI-io/api-sdk-ts](https://github.com/premAI-io/api-sdk-ts) * **Attestation library**: `@premai/reticle` on npm, for browser and Node.js use, with Rust crates for independent verification. See [Attestation](/attestation). ## Planned audits An independent audit of the platform is planned. We track it on the [Platform Status](/platform-status) page and we will publish the results when the audit completes. ## Resources * **Whitepaper**: [Prem Enclave Whitepaper (PDF)](https://static.prem.io/Prem%20Enclave%20Whitepaper%20-%20v2.pdf). The technical design of the enclave platform. * **Status page**: [status.premai.io](https://status.premai.io). Live platform availability. * **Infrastructure locations**: Owned hardware in Switzerland and rented hardware in Europe and the United States, all with identical TEE guarantees. See the [Security Model](/security-model). Enterprise requirements, agreements, or security questionnaires? Contact us at [support@premai.io](mailto:support@premai.io). # Use LLMs Source: https://docs.prem.io/use-llms LLM-Friendly documentation for Faster Developer Integration ## Add Prem API to your AI agent with `skill.md` The documentation includes a [`skill.md`](https://docs.prem.io/skill.md) file. This file tells your AI coding agent (Claude Code, Cursor, Windsurf, and others) how to use the Prem API. No manual setup is necessary. ### One-command setup Run this command in your terminal to load the Prem API skill into the context of your agent: ```bash theme={"system"} npx skills add https://docs.prem.io/skill.md ``` No more steps are necessary. ## Use the `llms-full.txt` file with your code editor You can load the full documentation for Prem API into your code editor. The AI assistant can then refer to the documentation directly in your workflow. Do these steps to configure Cursor: 1. Go to **Cursor Settings** > **Features** > **Docs** 2. Click **"Add new doc"** 3. Paste this URL into the prompt: ```text theme={"system"} https://docs.prem.io/llms-full.txt ``` 4. After you add the URL, use **@docs** -> **Prem API** to refer to this documentation directly in your code. Use this page to load the API documentation into your coding agent. The agent then understands the SDK ecosystem and how the API works. It can help you write code that integrates with Prem. To deploy a system that calls Prem API itself, see [Agents & Automation](/agents). Examples are a bot, an agent loop, a batch job, or an automated pipeline.