Connect the project's AI provider, or replace its provider, model or credentials
const url = 'https://beta-api.plune.ai/v1/settings/ai';const options = { method: 'PUT', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"provider":"anthropic","apiKey":"sk-ant-example-not-a-real-key","model":"claude-sonnet-5","language":"en","acceptedDisclaimer":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PUT \ --url https://beta-api.plune.ai/v1/settings/ai \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "provider": "anthropic", "apiKey": "sk-ant-example-not-a-real-key", "model": "claude-sonnet-5", "language": "en", "acceptedDisclaimer": true }'Owner only (AC-05). Encrypts the credentials with the server’s application key, bound to this project (ADR-0003) — the plaintext is never stored and never echoed back. customAddress is required exactly when provider is custom and is checked for being a public https address both here and on every later call (ADR-0005, AC-22); rejected here, the response never says which internal address it resolved to. acceptedDisclaimer must be sent true — the “code goes to this provider on every request” acknowledgement (AC-05), not stored beyond this check. A call that replaces an existing connection keeps enabled as it already was unless the caller changes it in the same call; a first connect always sets enabled: true.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”object
ADR-0004 — the three ways to connect a provider on this feature; each later addition is its own task.
Required exactly when provider is custom (AC-05). The scheme is checked structurally here (https only); whether it resolves to a public address cannot be, and is checked at request time, here and again on every call (AC-22). The address carries no credentials: one with a user name or a password (https://user:pass@host) is refused, because this column is stored and audited as plain text — the key has its own field (AC-12).
Write-only — never returned by any read (AC-12).
The provider’s model id (providers/prices.ts looks up a known price; an unlisted model still works, priced “ціна невідома”). Letters, digits and . _ : / @ + -, at most 200 characters — the id is copied into the Markdown export of every case a draft is accepted into.
Draft language for the whole project (AC-05).
Must be true — the “code goes to this provider on every request” acknowledgement (AC-05).
Examples
AC-05 — connect a provider from the list
{ "provider": "anthropic", "apiKey": "sk-ant-example-not-a-real-key", "model": "claude-sonnet-5", "language": "en", "acceptedDisclaimer": true}AC-05, AC-22 — a self-hosted OpenAI-compatible address
{ "provider": "custom", "customAddress": "https://models.example.test/v1", "apiKey": "example-not-a-real-key", "model": "local-llama-70b", "language": "en", "acceptedDisclaimer": true}Responses
Section titled “ Responses ”Connected — the owner view of AC-05
What an owner reads — AiSettingsStatus plus AC-05/AC-09/AC-15. Never includes the credentials themselves (AC-12).
object
Whether a provider has ever been connected (an ai_settings row exists).
Present exactly when configured is true.
Present exactly when configured is true.
Present when provider is custom.
Draft language, default en (AC-05) — a project setting, not the reader’s interface language.
Present exactly when configured is true (AC-05, AC-12 — “коли й ким”, never the value).
userId of the owner who last connected or replaced credentials.
AC-09 — the most recent provider issue, if any; null otherwise. Never aborted-shaped: a timeout is not a provider failure.
Present exactly when configured is true.
object
Calendar month, UTC.
object
object
Sum of costUsd over calls whose model had a known price at call time.
Count of calls whose tokens the provider reported but whose model has no price — “ціна невідома” — never folded into knownUsd as zero (SAD §8 rule 6). A call that reported no tokens (a timeout, a rejected key) has nothing to price and is counted in calls, not here.
GET, owner only (#896): the model the platform recommends for each provider it lists — none for custom, whose owner names the model. The connect form offers it first; always a model with a known price (AC-15).
object
GET, owner only (#898): the models the connect form lets the owner choose from for each provider the platform lists — the ones it has a price for, the recommended first, each with that price. None for custom. A model that is not listed is still allowed (model is free text): the list is a help, not a gate.
object
One model the connect form lists (#898): the id as a provider writes it and what the platform’s own price table charges for it, per million tokens (AC-15).
object
USD for a million input tokens.
USD for a million output tokens.
One model the connect form lists (#898): the id as a provider writes it and what the platform’s own price table charges for it, per million tokens (AC-15).
object
USD for a million input tokens.
USD for a million output tokens.
Example
{ "provider": "anthropic", "lastIssueKind": "key_rejected", "models": { "anthropic": [ { "id": "claude-sonnet-4-5", "inputUsdPerMillion": 3, "outputUsdPerMillion": 15 } ], "openrouter": [ { "id": "claude-sonnet-4-5", "inputUsdPerMillion": 3, "outputUsdPerMillion": 15 } ] }}Invalid body, or a rejected address (AC-22) — the message never names the resolved address
object
Examples
{ "error": "validation failed — customAddress: required when provider is custom"}{ "error": "validation failed — acceptedDisclaimer: must be true"}AC-22
{ "error": "address rejected — must be an https address"}AC-22 — resolves to a private, loopback or link-local address, now or later
{ "error": "address rejected — must resolve to a public address"}No / invalid token or session
A member or a reader asked
object
Example
{ "error": "project owner only"}Rate-limited: either the per-route throttle or a tenant quota. Retry-After carries the seconds until the window rolls over.
object
Example generated
{ "error": "example"}Headers
Section titled “Headers ”Seconds until the window rolls over
SAD §7/§11 — no server encryption key; nothing is saved
object
Example
{ "error": "AI platform not configured — contact your operator"}