Use your own provider keys
Updated
Bring your own key (BYOK) for ASR, LLM, and TTS. Store your provider keys in a secret set, and reference them from an agent.
Preview documentation
To use your own provider accounts for ASR, LLM, or TTS, bring your own key (BYOK). You store each provider API key in a secret set in your project, and your agent refers to the key by name. The key never appears in the agent's configuration, and the API never returns it.
Understand the tech
A secret set is a named group of keys in your project, such as support_prod. Each key holds one secret value, such as an OpenAI API key. The workflow is as follows:
- You create a secret set that contains your provider keys.
- For each model that uses your own provider account, you pass a reference to a key in the format
$secrets.<set>.<key>instead of the key itself. Agents accept only references, never the keys themselves. Agora checks that the set and key exist when you create or update the agent. - When a session starts, Agora resolves each reference to the key's current value. The session keeps that value until it ends.
You choose the credential mode for each model. For example, an agent can use your own LLM key while ASR and TTS use managed mode.
Prerequisites
Before you begin, make sure that you have:
- An Agora project and its App ID. To create one, use the Agora CLI.
- REST API credentials for the Platform, or an SDK client. For details, see Prerequisites.
- An account with each provider you want to use, and an API key for it.
Store your keys in a secret set
Create a secret set with a name for the set and your keys. Choose key names that describe their purpose. This example stores an OpenAI API key under the name llm.
Set names and key names are case-sensitive, and can contain up to 64 letters, digits, and underscores. Hyphens aren't allowed. A set name must be unique in your project. Key names only need to be unique in their set. A set can hold at most 64 KiB of keys and values. By default, an account can have up to 10 secret sets across all of its projects. To raise this limit, contact Agora support.
client.secrets.create(
name="support_prod",
secrets={"llm": "<your_openai_api_key>"},
)await client.secrets.create({
name: 'support_prod',
secrets: { llm: '<your_openai_api_key>' },
});The last argument is an idempotency key. Secret set creation doesn't support one, so pass an empty string.
secretSet, err := client.Secrets.Create(ctx, agentkit.SecretCreate{
Name: "support_prod",
Secrets: map[string]string{"llm": "<your_openai_api_key>"},
}, "")
if err != nil {
log.Fatal(err)
}
fmt.Println(secretSet.Body.Name) // The secret set nameSend a request to Create a secret set:
curl --request POST \
--url https://preview.ai.agora.io/conversational-ai/v1/projects/<your_app_id>/secrets \
--header 'Authorization: Basic <your_base64_encoded_credentials>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "support_prod",
"secrets": {
"llm": "<your_openai_api_key>"
}
}'If the request is successful, the response contains the set's name and key names. It never contains the values.
// 201 Created
{
"name": "support_prod",
"keys": ["llm"],
"create_ts": 1789898400,
"update_ts": 1789898400
}Create one secret set for each environment, such as support_dev and support_prod, with the same key names in each. To switch an agent between environments, you then only change the set name in its references.
Use the key in an agent
Create an agent that refers to the key. For each model that uses your own key, pass the reference instead of the key. If a provider needs another secret in a header, such as an organization ID, set the header value in the model's headers to a reference too. Don't put keys anywhere else in the agent.
In the following example, the LLM uses the OpenAI key stored as llm in the support_prod set. ASR and TTS use managed mode.
Pass the reference that secret_ref() returns as the API key. With your own key, you also set the provider's endpoint in base_url.
from agora_agent import Agent, DeepgramSTT, OpenAI, OpenAITTS, secret_ref
agent = (
Agent(client=client)
.with_stt(DeepgramSTT(model="nova-3", language="en-US"))
.with_llm(
OpenAI(
api_key=secret_ref("support_prod", "llm"),
base_url="https://api.openai.com/v1/chat/completions",
model="gpt-4.1-mini",
system_messages=[
{"role": "system", "content": "You are a friendly support agent for Acme. Keep your answers short."}
],
)
)
.with_tts(OpenAITTS(model="tts-1", voice="alloy"))
)
saved_agent = agent.save(name="Support agent")Pass the reference that secretRef() returns as the API key. With your own key, you also set the provider's endpoint in url.
import { Agent, DeepgramSTT, OpenAI, OpenAITTS, secretRef } from 'agora-agents';
const agent = new Agent({ client })
.withStt(new DeepgramSTT({ model: 'nova-3', language: 'en-US' }))
.withLlm(
new OpenAI({
apiKey: secretRef('support_prod', 'llm'),
url: 'https://api.openai.com/v1/chat/completions',
model: 'gpt-4.1-mini',
systemMessages: [
{ role: 'system', content: 'You are a friendly support agent for Acme. Keep your answers short.' },
],
}),
)
.withTts(new OpenAITTS({ model: 'tts-1', voice: 'alloy' }));
const savedAgent = await agent.save({ name: 'Support agent' });Pass the reference that agentkit.SecretRef() returns as the API key. With your own key, you also set the provider's endpoint in BaseURL.
agent := agentkit.NewAgent(client).
WithStt(vendors.NewDeepgramSTT(vendors.DeepgramSTTOptions{Model: "nova-3", Language: "en-US"})).
WithLlm(vendors.NewOpenAI(vendors.OpenAIOptions{
APIKey: agentkit.SecretRef("support_prod", "llm"),
BaseURL: "https://api.openai.com/v1/chat/completions",
Model: "gpt-4.1-mini",
SystemMessages: []map[string]interface{}{
{"role": "system", "content": "You are a friendly support agent for Acme. Keep your answers short."},
},
})).
WithTts(vendors.NewOpenAITTS(vendors.OpenAITTSOptions{Model: "tts-1", Voice: "alloy"}))
savedAgent, err := agent.Save(ctx, agentkit.SaveOptions{Name: "Support agent"})
if err != nil {
log.Fatal(err)
}Send a request to Create an agent with a pipeline. For each block that uses your own key, set credential.mode to byok, and set the provider's key field in credential.fields to the reference. For most providers, the field is api_key. Each model page lists its provider's fields. Managed blocks don't accept fields, and keys don't belong in params.
curl --request POST \
--url https://preview.ai.agora.io/conversational-ai/v1/projects/<your_app_id>/agents \
--header 'Authorization: Basic <your_base64_encoded_credentials>' \
--header 'Content-Type: application/json' \
--data '
{
"agent_name": "Support agent",
"instructions": "You are a friendly support agent for Acme. Keep your answers short.",
"pipeline": {
"mode": "cascaded",
"asr": {
"credential": { "mode": "managed" },
"vendor": "deepgram",
"model": "nova-3",
"language": "en-US"
},
"llm": {
"credential": {
"mode": "byok",
"fields": {
"api_key": "$secrets.support_prod.llm"
}
},
"vendor": "openai",
"style": "openai",
"model": "gpt-4.1-mini"
},
"tts": {
"credential": { "mode": "managed" },
"vendor": "minimax",
"params": {
"model": "speech-2.6-turbo",
"voice_setting": {
"voice_id": "English_captivating_female1",
"speed": 1
}
}
}
}
}'If the request is successful, the response contains the agent ID and the saved configuration. In the response, each block's credential contains only mode. Neither the reference nor the key is returned.
You then start sessions with the agent as usual. The session request doesn't change. For details, see Start a session.
Rotate a key
To replace a key's value, set the key to its new value. You don't need to change the agents that reference the key.
client.secrets.set_key(
name="support_prod",
key="llm",
value="<your_new_openai_api_key>",
)await client.secrets.setKey({
name: 'support_prod',
key: 'llm',
value: '<your_new_openai_api_key>',
});This example also imports github.com/AgoraIO/agora-agents-go/v2/v3.
secretKey, err := client.Secrets.SetKey(ctx, "support_prod", "llm",
v3.SecretKeyWrite{Value: "<your_new_openai_api_key>"}, v3.SecretKeyOptions{})
if err != nil {
log.Fatal(err)
}
fmt.Println(secretKey.Body.Key) // The key nameSend a request to Set a secret key with the new value:
curl --request PUT \
--url https://preview.ai.agora.io/conversational-ai/v1/projects/<your_app_id>/secrets/support_prod/keys/llm \
--header 'Authorization: Basic <your_base64_encoded_credentials>' \
--header 'Content-Type: application/json' \
--data '
{
"value": "<your_new_openai_api_key>"
}'If the request is successful, the response contains the key's name:
// 200 OK
{
"key": "llm"
}New sessions use the new value. Sessions that are already running keep the value they started with. Keep the old key active with your provider until those sessions end.
To change several keys at the same time, update the secret set with all the new values in one request.
Troubleshoot secret references
The following table lists common errors when you work with secret sets and references:
| Error | Cause | Solution |
|---|---|---|
400 UnknownSecretReference | An agent refers to a set or key that doesn't exist. | Check the spelling and case of the set and key names in $secrets.<set>.<key>. Names can't contain dots. |
400 MissingRequiredField | A byok block has no fields. | Pass your credentials in credential.fields, as described in Use the key in an agent. |
400 InvalidRequestBody | A managed block has fields. | Remove fields, or set mode to byok. |
400 InvalidFieldValue | fields uses the wrong field names for the provider, a value in fields isn't a $secrets.<set>.<key> reference, a key is in params, or the vendor doesn't support the credential mode. | Use the field names on the provider's model page, refer to your keys by reference, and keep keys out of params. |
409 SecretSetAlreadyExists | A secret set with this name already exists. | To change its keys, set a key or update the set. |
409 SecretSetInUse | You tried to delete a key or set that an agent refers to. | Update or delete the agents that refer to it first. |
422 ResourceQuotaLimitExceeded | Your account has reached its secret set quota. By default, an account can have up to 10 secret sets across all of its projects. | Delete secret sets you no longer use. |
For all error reasons, see Status codes.
Reference
For more information, see the following pages:
