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

You're reading the Conversational AI Platform (Preview) docs. Switch to Main.

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:

  1. You create a secret set that contains your provider keys.
  2. 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.
  3. 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 name

Send 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 name

Send 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:

ErrorCauseSolution
400 UnknownSecretReferenceAn 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 MissingRequiredFieldA byok block has no fields.Pass your credentials in credential.fields, as described in Use the key in an agent.
400 InvalidRequestBodyA managed block has fields.Remove fields, or set mode to byok.
400 InvalidFieldValuefields 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 SecretSetAlreadyExistsA secret set with this name already exists.To change its keys, set a key or update the set.
409 SecretSetInUseYou tried to delete a key or set that an agent refers to.Update or delete the agents that refer to it first.
422 ResourceQuotaLimitExceededYour 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:

API reference