Skip to main content

LLM Providers (BYOK)

DriftWise uses LLMs to generate plain-English narratives and risk classifications. Every plan gets some platform-LLM allocation (see Plans & Billing); Bring Your Own Key (BYOK) bypasses the weekly quota and hourly rate limit entirely and routes calls through your own provider account.

Breaking change (April 2026)

Per-request BYOK via the llm_config body field on /analyze has been removed. Configure a persisted BYOK credential via PUT /api/v2/orgs/:id/llm-config instead — every LLM call from that org (including the async drift-narrative worker) will use it automatically.

Supported Providers

ProviderConfig valueDefault model
Anthropicanthropicclaude-sonnet-4-6
OpenAIopenaigpt-4o
AWS Bedrockbedrockanthropic.claude-sonnet-4-5-20251001-v1:0 (cross-region inference profile)
Google Geminigeminigemini-2.0-flash
Azure OpenAIazure_openai(your deployment)
info

The Bedrock default is a cross-region inference profile, not a plain model ID. Bedrock routes each call to whichever supported region has capacity, which improves throughput during peak load. Override it with any model ID your account has access to — set model on the config.

Configuring BYOK

Credentials are encrypted at rest (AES-256-GCM with a server-held key) and used automatically for every LLM call your org makes. Only owners and admins can write this config.

PUT /orgs/:id/llm-config is full-replace — the body is the complete provider configuration (see per-provider shapes below). Unknown fields in the body return 400 via strict JSON decoding, so a typo won't silently no-op. last_validated_at resets to null on every write.

The GET endpoint returns provider + timestamps only; credentials never leave the server after storage. DELETE hard-deletes the ciphertext row — encrypted bytes do not linger after revocation.

See the llm tag of the API reference for the full request shapes (LLM config endpoints + usage are documented there; subscription management lives under the billing tag).

Checking your usage

GET /orgs/:id/llm-usage returns the current-bucket used/cap for both the weekly and hourly gates plus the next reset timestamp for each. -1 means unlimited. When byok_configured: true, the caps are advisory — BYOK requests never touch the counters.

Provider Configuration

Anthropic

{
"provider": "anthropic",
"api_key": "sk-ant-..."
}

api_key is required for persisted BYOK (the old "hosted-mode" path where api_key could be omitted for Anthropic is gone — platform LLM uses the weekly/hourly gate instead).

OpenAI

{
"provider": "openai",
"api_key": "sk-..."
}

AWS Bedrock

{
"provider": "bedrock",
"aws_region": "us-east-1",
"aws_access_key_id": "AKIA...",
"aws_secret_access_key": "..."
}

Bedrock requires explicit keys in persisted mode — instance-profile auth would attach to DriftWise's own service account, which is not what you want for a per-tenant credential. For cross-account access, aws_role_arn can still be added for STS-assume.

Google Gemini

{
"provider": "gemini",
"api_key": "AIza..."
}

Azure OpenAI

{
"provider": "azure_openai",
"azure_endpoint": "https://your-resource.openai.azure.com",
"azure_deployment": "your-deployment-name",
"azure_api_key": "..."
}

Optionally set azure_api_version (defaults to 2024-12-01-preview). azure_endpoint is validated as a public HTTPS URL at configure time — private/loopback/link-local addresses are rejected (SSRF guard).

Overriding the Model

Anthropic, OpenAI, Bedrock, and Gemini accept an optional model field:

{
"provider": "anthropic",
"api_key": "sk-ant-...",
"model": "claude-opus-4-6"
}

Azure OpenAI is different: the model is bound to the deployment in the Azure portal, so model is ignored. Point at a different model by changing azure_deployment to a deployment backed by the model you want.

Security & Storage

  • Encrypted at rest: AES-256-GCM (AEAD) using the same 32-byte server key (WEBHOOK_SECRET_ENCRYPTION_KEY) that protects webhook secrets. Ciphertext is stored; plaintext lives only inside the resolver and a short-lived variable in the worker.
  • Hard-deleted on revocation: DELETE physically removes the row.
  • Audited: every create / update / delete writes to admin_audit_log with the provider name only — never a prefix of the key or any credential field.
  • Never returned: the GET endpoint reports provider and timestamps only.
  • Trade-off vs. per-request: persisted BYOK means DriftWise now holds ciphertext + encryption key in the same blast radius. If your security policy forbids any key at rest, open a ticket — an enterprise contract can negotiate a hybrid ephemeral path.

BYOK failure handling

When a BYOK provider returns an error (timeout, 5xx, auth failure, rate limit), DriftWise does not silently retry the request against the platform LLM. Falling back would leak your data to a model you didn't consent to and mask provider-side failures from billing and observability. Instead:

  • The narrative snapshot is marked skipped with a concrete skip_reason (byok_client_error, byok_rate_limited, decrypt_failed, or key_unavailable).
  • Drift items, counts, and import blocks are still returned — only the LLM- generated narrative is skipped.
  • Repeat failures feed an in-memory back-off so a broken BYOK credential doesn't hammer the upstream provider.

Plan-side gates on the platform LLM (weekly quota, hourly rate limit) remain independent: a BYOK org never touches those counters.

Availability

BYOK is available on every plan. It is the recommended way to use DriftWise if you have a provider account of your own, because:

  • Free: bypass the 5/week platform cap.
  • Team: bypass the 20/hour platform rate limit.
  • Enterprise: use your own contracted pricing instead of DriftWise's managed allocation.