curl --request PUT \
--url https://app.thinnest.ai/api/v1/byok/credentials \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"kind": "llm",
"provider": "openai",
"credentials": {
"apiKey": "sk-proj-4fT9xQ2mLr8Vw1Zc7Kb3Nh6Yd0Pe5Gs9Qw7m",
"model": "gpt-5.4-mini"
}
}
'{
"kind": "llm",
"provider": "openai",
"label": "OpenAI",
"key": "…Qw7m",
"fields": {
"model": "gpt-5.4-mini"
},
"model": "gpt-5.4-mini",
"models": [
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
"gpt-4.1-mini"
],
"verifiedAt": "2026-10-06T10:01:40.902Z",
"updatedAt": "2026-10-06T10:01:40.902Z"
}Add BYOK Key
Adds or replaces your key for one job. The key is checked with the provider before anything is stored — a key the provider rejects is not saved — then stored encrypted and only ever shown by its last four characters. The provider’s model list is pulled at the same time. Without credentials.model, a speech key starts on the provider’s usual model and an LLM key on its usual one where it has one; replacing a key with one for the same provider keeps the model already chosen. Needs a full key.
curl --request PUT \
--url https://app.thinnest.ai/api/v1/byok/credentials \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"kind": "llm",
"provider": "openai",
"credentials": {
"apiKey": "sk-proj-4fT9xQ2mLr8Vw1Zc7Kb3Nh6Yd0Pe5Gs9Qw7m",
"model": "gpt-5.4-mini"
}
}
'{
"kind": "llm",
"provider": "openai",
"label": "OpenAI",
"key": "…Qw7m",
"fields": {
"model": "gpt-5.4-mini"
},
"model": "gpt-5.4-mini",
"models": [
"gpt-5.4",
"gpt-5.4-mini",
"gpt-5.4-nano",
"gpt-4.1-mini"
],
"verifiedAt": "2026-10-06T10:01:40.902Z",
"updatedAt": "2026-10-06T10:01:40.902Z"
}Authorizations
Your API key (ta_live_…) from Settings → API keys, sent as Authorization: Bearer <key>. Keep it on a server: it can message every customer you have. A key is full, build or read-only; a request its level does not allow is refused with 403.
Headers
Developers only: the customer workspace this request acts in — its org_… id from POST /customers. Leave it out to act in your own workspace.
"org_3fKq9TzQ1mN8vB2xR7cLpA"
Body
The job this key does: speech-to-text, LLM or voice.
stt, llm, tts Who does the job. Speech-to-text: deepgram, assemblyai, soniox, speechmatics, gladia, sarvam, elevenlabs, cartesia, openai, groq, mistral, azure. LLM: openai, anthropic, google, groq, mistral, sarvam, deepseek, xai, together, fireworks, cerebras, openrouter, openai_compatible. Voice: deepgram, elevenlabs, cartesia, openai, sarvam, soniox, mistral, azure, rime, inworld, hume, murf, speechify. A provider that does not do the job is refused with the list that does.
deepgram, assemblyai, soniox, speechmatics, gladia, sarvam, elevenlabs, cartesia, openai, groq, mistral, azure, rime, inworld, hume, murf, speechify, anthropic, google, deepseek, xai, together, fireworks, cerebras, openrouter, openai_compatible The provider's fields. Every provider takes apiKey and an optional model; azure also takes region and optionally endpoint; openai_compatible also takes baseUrl. Any other field is refused.
Show child attributes
Show child attributes
Response
The key as stored, masked.
One of your own provider keys, masked.
Its job: speech-to-text, LLM or voice.
stt, llm, tts The provider's id, e.g. deepgram.
The provider's name as the console shows it.
The key's last four characters after … — never more.
"…k9z2"
The key's other settings, as stored: model, region, endpoint or baseUrl. The secret is never among them.
Show child attributes
Show child attributes
The model this key runs, or null while one is still to be chosen (or for a provider with no model to choose, such as Azure Speech).
What the provider offered this key when it was added or last refreshed.
When the provider last accepted the key.
When the key or its model last changed.