Skip to main content

AI Providers

The AI Providers module lets you register and manage LLM provider configurations for a project. Each provider record stores the model slug, optional base URL, optional configuration, and an optional link to a Secret that supplies the API key.

Overview

An AI provider is a named configuration that tells the system how to reach a specific LLM endpoint. A project can have multiple providers — for example, one for GPT-4o and another for Claude 3.5.

When a provider is linked to a secret the secret's encrypted value is retrieved and passed as the API key when calling the LLM. The key is never exposed through the API. See it end to end in Connect Third-Party LLMs - Step 4 (Create provider records).

See the Permissions Reference for the IAM action strings for this module.

Data Model

FieldTypeDescription
idstringPublic identifier (e.g. aip_…)
project_idstringID of the owning project
secret_idstring | nullPublic ID of the linked secret, or null
namestringHuman-readable label
providerAiProviderSlugProvider slug (see below)
default_modelstringDefault model name sent to the provider API
base_urlstring | nullOverride base URL (optional, useful for self-hosted LLMs)
configobject | nullArbitrary provider-specific configuration object
created_atstringISO 8601 creation timestamp
updated_atstringISO 8601 last-updated timestamp

Provider Slugs

Valid values for the provider field:

SlugDescription
openaiOpenAI
anthropicAnthropic
googleGoogle Gemini
xaixAI (Grok)
groqGroq
ollamaOllama (local)
azureAzure OpenAI
bedrockAmazon Bedrock
gatewayGeneric API gateway
customCustom / self-hosted model

A local ollama provider needs no linked secret — it uses the server's OLLAMA_BASE_URL instead. See it end to end in Chat with an LLM - Step 3 (Create a local AI provider).

Key Concepts

Bedrock authentication

The bedrock provider supports two authentication modes, determined by the shape of the linked secret's JSON value:

IAM credentials — pass accessKeyId, secretAccessKey, and optionally sessionToken. The client signs requests with AWS SigV4.

{
"accessKeyId": "<aws-access-key-id>",
"secretAccessKey": "<aws-secret-access-key>",
"sessionToken": "<optional-session-token>"
}

Bedrock API key — pass apiKey only (format ABSK…). The client uses Bearer token authentication via AWS_BEARER_TOKEN_BEDROCK. This is the new authentication mechanism introduced for Amazon Bedrock in 2025.

{ "apiKey": "ABSK..." }

Important: Store the secret value as a JSON object (shown above) — this is the canonical form and the only one that supports IAM credentials. As a convenience, a bare ABSK… string (with no JSON wrapper) is also accepted: the server tries to parse the value as JSON first, and if that fails but the value starts with ABSK it is treated as { "apiKey": "<value>" }. IAM credentials (accessKeyId / secretAccessKey) must always use the JSON object form.

If neither field is present the default AWS credential chain (environment variables, instance profile, etc.) is used. The region field in the provider's config object defaults to us-east-1.

You can also pass the API key directly in the provider's config object as api_key (without linking a secret). This is useful for quick testing but the secret-linked approach is recommended for production.

{ "api_key": "ABSK..." }

Price overrides

A project can price its own provider instances without a global admin. A per-provider price override is a price-book row bound to a specific AI provider — an enterprise-negotiated rate or a gateway with markup — that wins over the global default when usage cost is computed for that provider. Manage them with:

  • GET /api/v1/ai-providers/{ai_provider_id}/prices — list this provider's overrides
  • PUT /api/v1/ai-providers/{ai_provider_id}/prices — upsert them, keyed on (model, effective_from)

Both are authorized by the caller's access to the provider's own project (ai-providers:GetAiProviderPrices / ai-providers:ManageAiProviderPrices), so one project never sees another's negotiated rates — unlike the global price book, which lists defaults only. The provider slug is taken from the AI provider itself (an override matches only when its slug equals the provider's), so you supply just the model, rates, and effective_from. effective_from must be in the future; past prices are immutable, so ship corrections as new future-dated rows. See Usage - Pricing for how the effective price is chosen and frozen onto each meter.

Deleting a provider

DELETE /api/v1/ai-providers/{ai_provider_id} classifies everything that references the provider into two kinds:

DependentKindBehavior
Chats, agents, discussionsLive referenceAlways block with 409. force does not override them — delete or repoint each resource first.
Price overridesSoft dependentBlock with 409 unless force=true, which deletes the overrides (meaningless without the provider).
Usage/generation records, discussion participantsSoft dependentBlock with 409 unless force=true, which unlinks them (nulls the provider FK), preserving the row and its as-billed receipt.

A delete with no dependents (or force=true and only soft dependents) returns 204. On a 409 the response carries error.code = "AI_PROVIDER_HAS_DEPENDENTS" and an error.meta describing what blocked it:

{
"error": {
"code": "AI_PROVIDER_HAS_DEPENDENTS",
"message": "AI provider 'aip_01' is in use by 2 chat(s), 1 agent(s) ...",
"meta": {
"chatCount": 2, "chatIds": ["chat_01", "chat_02"],
"agentCount": 1, "agentIds": ["agent_01"],
"discussionCount": 0, "discussionIds": [],
"priceOverrideCount": 0, "usageEventCount": 0, "discussionParticipantCount": 0,
"forcible": false
}
}
}

forcible is true only when the block comes solely from soft dependents — i.e. a force=true retry would succeed. The *Ids arrays sample up to 20 offending IDs so you can act on them directly; the *Count fields always report the true totals.

Examples

Create an AI provider

soat create-ai-provider \
--project-id proj_ABC \
--name "OpenAI GPT-4o" \
--provider openai \
--default-model gpt-4o \
--secret-id sec_01

List providers in a project

soat list-ai-providers --project-id proj_ABC

Set a per-provider price override

soat update-ai-provider-prices \
--ai-provider-id aip_ABC \
--prices '[{"model":"gpt-4o","input_price_per_m":5,"output_price_per_m":15,"effective_from":"2099-01-01T00:00:00.000Z"}]'