curl --request POST \
--url https://gateway.prem.io/rvenc/chat/completions \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"encryptedInference": "encrypted_inference_payload_data",
"cipherText": "cipher_text_for_key_exchange",
"nonce": "nonce_value"
}
'import requests
url = "https://gateway.prem.io/rvenc/chat/completions"
payload = {
"encryptedInference": "encrypted_inference_payload_data",
"cipherText": "cipher_text_for_key_exchange",
"nonce": "nonce_value"
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
encryptedInference: 'encrypted_inference_payload_data',
cipherText: 'cipher_text_for_key_exchange',
nonce: 'nonce_value'
})
};
fetch('https://gateway.prem.io/rvenc/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://gateway.prem.io/rvenc/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'encryptedInference' => 'encrypted_inference_payload_data',
'cipherText' => 'cipher_text_for_key_exchange',
'nonce' => 'nonce_value'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://gateway.prem.io/rvenc/chat/completions"
payload := strings.NewReader("{\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://gateway.prem.io/rvenc/chat/completions")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://gateway.prem.io/rvenc/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\"\n}"
response = http.request(request)
puts response.read_body{
"stream": "event: data\ndata: a1b2c3d4e5f67890abcdef1234567890abcdef123456...\n\nevent: data\ndata: f6e5d4c3b2a19876543210fedcba9876543210fedcba...\n\nevent: data\ndata: 1234567890abcdef1234567890abcdef1234567890...\n\nevent: done\ndata: [DONE]"
}{
"status": 400,
"error": "Some error message",
"message": null,
"env": "development",
"log": {
"request_id": "req_1234567890"
},
"support_id": "support_uuidv7-something-else",
"data": {},
"validator": {
"email": "Invalid email address",
"password": "Password is required"
}
}{
"status": 401,
"error": "Some error message",
"message": null,
"env": "development",
"log": {
"request_id": "req_1234567890"
},
"support_id": "support_uuidv7-something-else",
"data": {},
"validator": {
"email": "Invalid email address",
"password": "Password is required"
}
}{
"status": 403,
"error": "Some error message",
"message": null,
"env": "development",
"log": {
"request_id": "req_1234567890"
},
"support_id": "support_uuidv7-something-else",
"data": {},
"validator": {
"email": "Invalid email address",
"password": "Password is required"
}
}{
"status": 429,
"error": "You already have an active chat stream. Please wait for it to complete or try again in a few moments."
}Encrypted Chat Completions (RVENC)
Create a chat completion from an end-to-end encrypted payload, decrypted only inside the confidential compute environment. Stateless: no chat history is kept and no files can be attached.
curl --request POST \
--url https://gateway.prem.io/rvenc/chat/completions \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"encryptedInference": "encrypted_inference_payload_data",
"cipherText": "cipher_text_for_key_exchange",
"nonce": "nonce_value"
}
'import requests
url = "https://gateway.prem.io/rvenc/chat/completions"
payload = {
"encryptedInference": "encrypted_inference_payload_data",
"cipherText": "cipher_text_for_key_exchange",
"nonce": "nonce_value"
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
encryptedInference: 'encrypted_inference_payload_data',
cipherText: 'cipher_text_for_key_exchange',
nonce: 'nonce_value'
})
};
fetch('https://gateway.prem.io/rvenc/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://gateway.prem.io/rvenc/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'encryptedInference' => 'encrypted_inference_payload_data',
'cipherText' => 'cipher_text_for_key_exchange',
'nonce' => 'nonce_value'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://gateway.prem.io/rvenc/chat/completions"
payload := strings.NewReader("{\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://gateway.prem.io/rvenc/chat/completions")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://gateway.prem.io/rvenc/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\"\n}"
response = http.request(request)
puts response.read_body{
"stream": "event: data\ndata: a1b2c3d4e5f67890abcdef1234567890abcdef123456...\n\nevent: data\ndata: f6e5d4c3b2a19876543210fedcba9876543210fedcba...\n\nevent: data\ndata: 1234567890abcdef1234567890abcdef1234567890...\n\nevent: done\ndata: [DONE]"
}{
"status": 400,
"error": "Some error message",
"message": null,
"env": "development",
"log": {
"request_id": "req_1234567890"
},
"support_id": "support_uuidv7-something-else",
"data": {},
"validator": {
"email": "Invalid email address",
"password": "Password is required"
}
}{
"status": 401,
"error": "Some error message",
"message": null,
"env": "development",
"log": {
"request_id": "req_1234567890"
},
"support_id": "support_uuidv7-something-else",
"data": {},
"validator": {
"email": "Invalid email address",
"password": "Password is required"
}
}{
"status": 403,
"error": "Some error message",
"message": null,
"env": "development",
"log": {
"request_id": "req_1234567890"
},
"support_id": "support_uuidv7-something-else",
"data": {},
"validator": {
"email": "Invalid email address",
"password": "Password is required"
}
}{
"status": 429,
"error": "You already have an active chat stream. Please wait for it to complete or try again in a few moments."
}TypeScript SDK
The SDK is available on GitHub: premAI-io/api-sdk-tsBasic Setup
Create a 32-byte KEK. Encode it as 64 hexadecimal characters. Keep the KEK. Reuse it for later requests.export CLIENT_KEK="$(openssl rand -hex 32)"
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: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
const response = await client.chat.completions.create({
model: "deepseek-v4-flash",
messages: [{ role: "user", content: "Hello!" }],
});
Streaming Requests
const stream = await client.chat.completions.create({
model: "deepseek-v4-flash",
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:bunx -p @premai/api-sdk@1.0.64 confidential-proxy --kek "$CLIENT_KEK"
# Server runs on http://127.0.0.1:8787
curl http://127.0.0.1:8787/v1/chat/completions \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{"model": "deepseek-v4-flash", "messages": [{"role": "user", "content": "Hello!"}], "stream": false}'
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: "deepseek-v4-flash",
messages: [{ role: "user", content: "Count to 10" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || "");
}
Reasoning models
Reasoning tokens are billed and count against your token rate limits. See Usage for billing, output-token interaction, and instructions to disable or limit reasoning. The validreasoning_effort values depend on the model family. See Models & Pricing for the values that each family accepts.
const response = await client.chat.completions.create({
model: "<model>",
messages: [{ role: "user", content: "Hello!" }],
reasoning_effort: "low", // reduce reasoning
});
API Reference
Authorizations
Send your access token as header Authorization: Bearer {accessToken}
Your API key that starts with sk_live or sk_test. You can create yours at go.prem.io/api-keys.
Body
Chat completion request with an end-to-end encrypted payload and the key material needed to decrypt it.
Encrypted JSON string containing all chat completion parameters. When decrypted, this string must match the structure shown in the expandable _decryptedInference property below (reference only - do not send this property).
Cipher text for shared secret generation (ECDH key exchange)
Nonce used for encrypting the inference payload
Model identifier to use for completion
If true, returns a server-sent events stream. If false, returns JSON with encryptedResponse and nonce. May also be set inside encryptedInference.
Response
RVENC chat completion response. Returns JSON when stream is false or omitted (default), or a server-sent events stream when stream is true.
Server-sent events (SSE) stream response structure. The actual response is a text stream, but this schema documents the structure for reference.
Server-sent events (SSE) stream with encrypted chat completion chunks. The stream contains four types of events:
event: datafollowed bydata: <encrypted_hex_string>- Encrypted chunk that must be decrypted using the same shared secret and nonce from the request. When decrypted, each chunk matches the structure shown in the_decryptedChunkproperty below (reference only).event: heartbeatfollowed bydata: {"message":"processing","timestamp":<ms>}- Keep-alive event sent every 5 seconds while waiting for the first inference token. Not encrypted; clients should ignore it.event: errorfollowed bydata: <error_data>- Error event (can be encrypted or plain JSON). Emitted when inference fails or when no inference data is received within 10 minutes.event: donefollowed bydata: [DONE]- Stream completion marker
Each encrypted data: line contains a hex-encoded string. Decrypt each chunk using XChaCha20-Poly1305 with the shared secret and nonce from your request.
Reference only - This shows the structure that each encrypted chunk should contain when decrypted. Decrypt each chunk in the stream using the same shared secret and nonce from your request to get this structure.
Show child attributes
Show child attributes
{
"id": "cd9d05b657a041a6a14ab2fc890a7d7e",
"object": "chat.completion.chunk",
"created": 1764004858,
"model": "openai/gpt-oss-120b",
"choices": [
{
"index": 0,
"delta": {
"role": null,
"content": null,
"reasoning_content": "This ",
"tool_calls": null
},
"logprobs": null,
"finish_reason": null,
"matched_stop": null
}
],
"usage": null
}
{
"id": "cd9d05b657a041a6a14ab2fc890a7d7e",
"object": "chat.completion.chunk",
"created": 1764004858,
"model": "openai/gpt-oss-120b",
"choices": [
{
"index": 0,
"delta": {
"role": "assistant",
"content": "Hello",
"reasoning_content": null,
"tool_calls": null
},
"logprobs": null,
"finish_reason": null,
"matched_stop": null
}
],
"usage": null
}
{
"id": "cd9d05b657a041a6a14ab2fc890a7d7e",
"object": "chat.completion.chunk",
"created": 1764004858,
"model": "openai/gpt-oss-120b",
"choices": [
{
"index": 0,
"delta": [],
"logprobs": null,
"finish_reason": "stop",
"matched_stop": null
}
],
"usage": null
}