Skip to content

BYO Providers

Connect your own LLM, STT, TTS, and messaging provider keys so Rymi routes calls through your accounts. Keys are encrypted at rest and never returned in plaintext.

The dashboard surface lives at Settings → BYO Providers. The studio and any tooling that provisions providers programmatically share the same API.

Access

Provider keys are available on the active Free and Enterprise plans. Self-hosted custom endpoints (custom-llm, custom-voice, custom-transcriber) remain Enterprise-only. The URL lives per-agent; this endpoint stores only the optional bearer token the gateway forwards.

Supported providers

LLMs: openai, anthropic, perplexity, groq, cerebras, deepseek, xai, mistral, minimax, google, gemini, anthropic-bedrock, anthropic-vertex, azure-openai, aws-nova, zhipu, dashscope, moonshot, siliconflow, openrouter, cloudflare-workers-ai.

STT / TTS: elevenlabs, assemblyai, speechmatics, cartesia, gladia, hume, rime, playht, deepgram, sarvam, azure-speech, soniox, lmnt, smallest, inworld, fish-audio, aws-polly.

Custom endpoints: custom-llm, custom-voice, custom-transcriber. URLs are configured per-agent via the agent record (custom_llm_url, custom_voice_url, custom_transcriber_url); BYOK only stores the bearer token.

Messaging: whatsapp, telegram.

The full per-provider field schema is exposed via GET /v1/providers/fields.


List Connected Providers

GET/v1/providers

Returns active BYOK rows for the tenant with masked key prefixes.

Response 200

json
{
  "data": [
    {
      "id": "tp_...",
      "provider": "openai",
      "account_name": "Acme OpenAI",
      "is_active": true,
      "key_prefix": "sk-p···7f2a",
      "created_at": "...",
      "updated_at": "..."
    }
  ]
}

Get Field Schema

GET/v1/providers/fields

Returns the credential field map for every supported provider. The dashboard uses it to render dynamic connect forms without hardcoding shapes.

Response 200

json
{
  "data": {
    "openai": [{ "name": "api_key", "label": "API key", "secret": true, "required": true }],
    "anthropic-bedrock": [
      { "name": "access_key_id", "required": true },
      { "name": "secret_access_key", "secret": true, "required": true },
      { "name": "region", "required": true }
    ]
  }
}

Connect or Update a Provider

POST/v1/providers

Upserts credentials for a provider. The body shape varies per provider. Check GET /v1/providers/fields for the exact field name, or send a request and read the 400.

Common Request Bodies

jsonc
// OpenAI / Anthropic / most LLMs + voice providers
{ "provider": "openai", "api_key": "sk-...", "account_name": "Acme" }

// AWS Bedrock
{
  "provider": "anthropic-bedrock",
  "access_key_id": "AKIA...",
  "secret_access_key": "...",
  "region": "us-east-1"
}

// PlayHT
{ "provider": "playht", "user_id": "uid_...", "api_key": "playht_..." }

// Custom endpoint (URL lives on the agent)
{ "provider": "custom-llm", "api_key": "Bearer-token-or-blank" }

// Messaging
{ "provider": "whatsapp", "access_token": "EAA...", "phone_number_id": "..." }

Response 200

json
{
  "data": {
    "id": "tp_...",
    "provider": "openai",
    "account_name": "Acme",
    "is_active": true,
    "created_at": "...",
    "updated_at": "..."
  }
}

Errors

StatusCodeMeaning
400Missing or unknown field for the provider shape
400Unsupported provider: <id>
403plan_blocks_byokProvider keys are not enabled for this workspace

Validate Without Saving

POST/v1/providers/:provider/validate

Performs a live ping against the upstream API where supported (OpenAI, Anthropic, ElevenLabs and a handful of others) and returns a synthetic { valid: true } for providers without a cheap validation path. Messaging providers return shape-only success; the first send surfaces auth errors.

Request Body

Same shape as POST /v1/providers, minus provider (which lives in the URL).

Response 200

json
{ "valid": true, "accountName": "Acme OpenAI" }

Response 400

json
{ "valid": false, "error": "Invalid API key" }

Disconnect a Provider

DELETE/v1/providers/:provider

Soft-deletes the credential row (is_active = false). Future requests fall back to platform defaults until the provider is re-connected.

Response 200

json
{ "success": true }

Legacy: ElevenLabs voice catalog

The legacy /v1/voice-providers/elevenlabs/* endpoints predated the unified /v1/providers surface. They remain available for clients that haven't migrated, but new integrations should use /v1/providers with provider: "elevenlabs". The newer flow stores the API key and the gateway pulls the latest voice list at session start. The legacy endpoints also cache the voice catalog server-side and expose a manual sync operation:

GET/v1/voice-providers/elevenlabs/status
POST/v1/voice-providers/elevenlabs/connect
POST/v1/voice-providers/elevenlabs/sync
POST/v1/voice-providers/elevenlabs/disconnect

Cartesia exposes the same cached-catalog pattern through two legacy endpoints. status reports whether the tenant has active Cartesia credentials and how many Cartesia voices are cached; sync refreshes the tenant-scoped catalog using active tenant or platform credentials.

GET/v1/voice-providers/cartesia/status
POST/v1/voice-providers/cartesia/sync

Cost model

Provider keys are billed by the provider, not Rymi. An agent using your own keys is a Custom agent: the provider invoices you for usage directly, and Rymi charges only its flat platform fee ($0.02/min at time of writing; see Pricing). Channels left on Rymi-provided keys bill at component cost on top of that fee.


Turn detection is a separate concern

Speech-boundary detection (when the caller stops talking) is configured per agent, not per provider. Pick a Rymi-native detector mode (steady, balanced, responsive, sensitive, provider, or provider-backed) via the agent's advanced turnDetectorMode field. The provider and provider-backed modes use STT-emitted turn/end-of-speech signals from the recognizer adapter, when available; the other modes operate on the incoming PCM independent of the STT vendor.

See Turn-taking and speech boundary detection for the full mode/engine table.