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.
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
| Provider | Config value | Default model |
|---|---|---|
| Anthropic | anthropic | claude-sonnet-4-6 |
| OpenAI | openai | gpt-4o |
| AWS Bedrock | bedrock | anthropic.claude-sonnet-4-5-20251001-v1:0 (cross-region inference profile) |
| Google Gemini | gemini | gemini-2.0-flash |
| Azure OpenAI | azure_openai | (your deployment) |
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_logwith 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
skippedwith a concreteskip_reason(byok_client_error,byok_rate_limited,decrypt_failed, orkey_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.