curl --request POST \
--url https://gateway.prem.io/rvenc/audio/transcriptions \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "whisper-large-v3",
"encryptedInference": "encrypted_inference_payload_data",
"cipherText": "cipher_text_for_key_exchange",
"nonce": "nonce_value",
"fileNameNonce": "file_name_nonce_value",
"encryptedFileName": "encrypted_audio_file_name_data",
"fileNonce": "file_nonce_value",
"encryptedFile": "encrypted_audio_file_data"
}
'import requests
url = "https://gateway.prem.io/rvenc/audio/transcriptions"
payload = {
"model": "whisper-large-v3",
"encryptedInference": "encrypted_inference_payload_data",
"cipherText": "cipher_text_for_key_exchange",
"nonce": "nonce_value",
"fileNameNonce": "file_name_nonce_value",
"encryptedFileName": "encrypted_audio_file_name_data",
"fileNonce": "file_nonce_value",
"encryptedFile": "encrypted_audio_file_data"
}
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({
model: 'whisper-large-v3',
encryptedInference: 'encrypted_inference_payload_data',
cipherText: 'cipher_text_for_key_exchange',
nonce: 'nonce_value',
fileNameNonce: 'file_name_nonce_value',
encryptedFileName: 'encrypted_audio_file_name_data',
fileNonce: 'file_nonce_value',
encryptedFile: 'encrypted_audio_file_data'
})
};
fetch('https://gateway.prem.io/rvenc/audio/transcriptions', 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/audio/transcriptions",
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([
'model' => 'whisper-large-v3',
'encryptedInference' => 'encrypted_inference_payload_data',
'cipherText' => 'cipher_text_for_key_exchange',
'nonce' => 'nonce_value',
'fileNameNonce' => 'file_name_nonce_value',
'encryptedFileName' => 'encrypted_audio_file_name_data',
'fileNonce' => 'file_nonce_value',
'encryptedFile' => 'encrypted_audio_file_data'
]),
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/audio/transcriptions"
payload := strings.NewReader("{\n \"model\": \"whisper-large-v3\",\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\",\n \"fileNameNonce\": \"file_name_nonce_value\",\n \"encryptedFileName\": \"encrypted_audio_file_name_data\",\n \"fileNonce\": \"file_nonce_value\",\n \"encryptedFile\": \"encrypted_audio_file_data\"\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/audio/transcriptions")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"whisper-large-v3\",\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\",\n \"fileNameNonce\": \"file_name_nonce_value\",\n \"encryptedFileName\": \"encrypted_audio_file_name_data\",\n \"fileNonce\": \"file_nonce_value\",\n \"encryptedFile\": \"encrypted_audio_file_data\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://gateway.prem.io/rvenc/audio/transcriptions")
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 \"model\": \"whisper-large-v3\",\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\",\n \"fileNameNonce\": \"file_name_nonce_value\",\n \"encryptedFileName\": \"encrypted_audio_file_name_data\",\n \"fileNonce\": \"file_nonce_value\",\n \"encryptedFile\": \"encrypted_audio_file_data\"\n}"
response = http.request(request)
puts response.read_body{
"status": 200,
"data": {
"id": "123"
},
"error": null,
"log": null,
"validator": null,
"support_id": null,
"message": "Resource created successfully",
"env": "development"
}{
"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": 413,
"error": "Audio file size exceeds the maximum allowed limit of 25MB."
}{
"status": 429,
"error": "Rate limit exceeded. Please try again later."
}Encrypted Audio Transcriptions (RVENC)
Transcribe an end-to-end encrypted audio file into text, in the language spoken in the audio. Nothing is stored: neither the audio nor the transcript.
curl --request POST \
--url https://gateway.prem.io/rvenc/audio/transcriptions \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "whisper-large-v3",
"encryptedInference": "encrypted_inference_payload_data",
"cipherText": "cipher_text_for_key_exchange",
"nonce": "nonce_value",
"fileNameNonce": "file_name_nonce_value",
"encryptedFileName": "encrypted_audio_file_name_data",
"fileNonce": "file_nonce_value",
"encryptedFile": "encrypted_audio_file_data"
}
'import requests
url = "https://gateway.prem.io/rvenc/audio/transcriptions"
payload = {
"model": "whisper-large-v3",
"encryptedInference": "encrypted_inference_payload_data",
"cipherText": "cipher_text_for_key_exchange",
"nonce": "nonce_value",
"fileNameNonce": "file_name_nonce_value",
"encryptedFileName": "encrypted_audio_file_name_data",
"fileNonce": "file_nonce_value",
"encryptedFile": "encrypted_audio_file_data"
}
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({
model: 'whisper-large-v3',
encryptedInference: 'encrypted_inference_payload_data',
cipherText: 'cipher_text_for_key_exchange',
nonce: 'nonce_value',
fileNameNonce: 'file_name_nonce_value',
encryptedFileName: 'encrypted_audio_file_name_data',
fileNonce: 'file_nonce_value',
encryptedFile: 'encrypted_audio_file_data'
})
};
fetch('https://gateway.prem.io/rvenc/audio/transcriptions', 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/audio/transcriptions",
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([
'model' => 'whisper-large-v3',
'encryptedInference' => 'encrypted_inference_payload_data',
'cipherText' => 'cipher_text_for_key_exchange',
'nonce' => 'nonce_value',
'fileNameNonce' => 'file_name_nonce_value',
'encryptedFileName' => 'encrypted_audio_file_name_data',
'fileNonce' => 'file_nonce_value',
'encryptedFile' => 'encrypted_audio_file_data'
]),
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/audio/transcriptions"
payload := strings.NewReader("{\n \"model\": \"whisper-large-v3\",\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\",\n \"fileNameNonce\": \"file_name_nonce_value\",\n \"encryptedFileName\": \"encrypted_audio_file_name_data\",\n \"fileNonce\": \"file_nonce_value\",\n \"encryptedFile\": \"encrypted_audio_file_data\"\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/audio/transcriptions")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"whisper-large-v3\",\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\",\n \"fileNameNonce\": \"file_name_nonce_value\",\n \"encryptedFileName\": \"encrypted_audio_file_name_data\",\n \"fileNonce\": \"file_nonce_value\",\n \"encryptedFile\": \"encrypted_audio_file_data\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://gateway.prem.io/rvenc/audio/transcriptions")
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 \"model\": \"whisper-large-v3\",\n \"encryptedInference\": \"encrypted_inference_payload_data\",\n \"cipherText\": \"cipher_text_for_key_exchange\",\n \"nonce\": \"nonce_value\",\n \"fileNameNonce\": \"file_name_nonce_value\",\n \"encryptedFileName\": \"encrypted_audio_file_name_data\",\n \"fileNonce\": \"file_nonce_value\",\n \"encryptedFile\": \"encrypted_audio_file_data\"\n}"
response = http.request(request)
puts response.read_body{
"status": 200,
"data": {
"id": "123"
},
"error": null,
"log": null,
"validator": null,
"support_id": null,
"message": "Resource created successfully",
"env": "development"
}{
"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": 413,
"error": "Audio file size exceeds the maximum allowed limit of 25MB."
}{
"status": 429,
"error": "Rate limit exceeded. Please try again later."
}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";
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: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
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
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-stylemetadataandresults, 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: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/audio/transcriptions \
-H "Authorization: Bearer your-api-key" \
-F "file=@audio.mp3" \
-F "model=deepgram/general-nova-3"
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));
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
Audio transcription request with an end-to-end encrypted payload and the key material needed to decrypt it.
Model identifier to use for transcription
Encrypted JSON string containing all audio transcription 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
The encrypted audio file name. The file name is encrypted using XChaCha20-Poly1305 with the shared secret and fileNameNonce.
Nonce used for encrypting the encrypted audio file name
The encrypted audio file data. The audio content is encrypted using XChaCha20-Poly1305 with the shared secret and fileNonce.
Nonce used for encrypting the encrypted audio file
Response
Encrypted audio transcription response. The response contains the encrypted transcription result which must be decrypted using the shared secret derived from the request's cipherText, along with the response nonce.
Status code of the response
200, 201, 202 Encrypted audio transcription response. Contains the encrypted transcription result that must be decrypted using the shared secret derived from the request's cipherText.
Show child attributes
Show child attributes
{
"encryptedResponse": "a1b2c3d4e5f67890abcdef1234567890abcdef123456789012345678901234567890abcdef...",
"nonce": "f6e5d4c3b2a19876543210fedcba9876"
}
Message of the response, human readable
"Resource created successfully"
API environment
development, production Error message of the response, human readable
"Invalid email address"
Useful informaiton, not always present, to debug the response
{ "request_id": "req_1234567890" }
"Some pertinent log message"
Validator response object, each key is the field name and value is the error message
{
"email": "Invalid email address",
"password": "Password is required"
}
Support ID linked to the response, used to identify it when talking with our team
"support_uuidv7-something-else"