Skip to content

Connect the project's AI provider, or replace its provider, model or credentials

PUT
/v1/settings/ai
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.

Media type application/json
object
provider
required

ADR-0004 — the three ways to connect a provider on this feature; each later addition is its own task.

string
Allowed values: anthropic openrouter custom
customAddress

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).

string format: uri
<= 2048 characters /^[Hh][Tt][Tt][Pp][Ss]:///
apiKey
required

Write-only — never returned by any read (AC-12).

string
>= 1 characters
model
required

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.

string
>= 1 characters <= 200 characters /^[A-Za-z0-9@][A-Za-z0-9._:/@+-]*$/
language

Draft language for the whole project (AC-05).

string
default: en
acceptedDisclaimer
required

Must be true — the “code goes to this provider on every request” acknowledgement (AC-05).

boolean
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
}

Connected — the owner view of AC-05

Media type application/json

What an owner reads — AiSettingsStatus plus AC-05/AC-09/AC-15. Never includes the credentials themselves (AC-12).

object
configured
required

Whether a provider has ever been connected (an ai_settings row exists).

boolean
enabled
required
boolean
provider

Present exactly when configured is true.

string
Allowed values: anthropic openrouter custom
model

Present exactly when configured is true.

string
customAddress

Present when provider is custom.

string
language

Draft language, default en (AC-05) — a project setting, not the reader’s interface language.

string
credentialsSavedAt

Present exactly when configured is true (AC-05, AC-12 — “коли й ким”, never the value).

string format: date-time
credentialsSavedBy

userId of the owner who last connected or replaced credentials.

string
lastIssueKind

AC-09 — the most recent provider issue, if any; null otherwise. Never aborted-shaped: a timeout is not a provider failure.

string
Allowed values: key_rejected provider_rate_limited provider_unavailable invalid_response address_rejected
lastIssueAt
string format: date-time
nullable
usage

Present exactly when configured is true.

object
month
required

Calendar month, UTC.

string
/^\d{4}-\d{2}$/
calls
required
object
succeeded
required
integer
failed
required
integer
aborted
required
integer
cost
required
object
knownUsd
required

Sum of costUsd over calls whose model had a known price at call time.

number
unknownPriceCalls
required

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.

integer
recommendedModels

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
anthropic
string
openrouter
string
models

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
anthropic
Array<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
id
required
string
inputUsdPerMillion
required

USD for a million input tokens.

number
outputUsdPerMillion
required

USD for a million output tokens.

number
openrouter
Array<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
id
required
string
inputUsdPerMillion
required

USD for a million input tokens.

number
outputUsdPerMillion
required

USD for a million output tokens.

number
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

Media type application/json
object
error
required
string
Examples
{
"error": "validation failed — customAddress: required when provider is custom"
}

No / invalid token or session

A member or a reader asked

Media type application/json
object
error
required
string
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.

Media type application/json
object
error
required
string
Example generated
{
"error": "example"
}
Retry-After
integer

Seconds until the window rolls over

SAD §7/§11 — no server encryption key; nothing is saved

Media type application/json
object
error
required
string
Example
{
"error": "AI platform not configured — contact your operator"
}