Skip to content

Models

For organization administrators who add model providers. You need the rights named for each task. Only a platform admin can change settings under /v1/platform. Start with the administrator manual.

A Bot needs a language model to do work. OpenBot does not include a model. You connect one. OpenBot keeps two kinds of records for this:

  • A model describes one model, such as gpt-5: its type, its name, its options and its limits. A model does not belong to a provider.
  • A model provider is a model service and its access credentials. A provider links the models that it offers. A link can change some values of the model for this provider only.

You can also connect ChatGPT as a model provider. See Connect ChatGPT. Its account belongs to the provider.

You add both records through the API. OpenBot stores them in the database, so every instance uses the same ones.

The platform or an organization owns each model and each provider:

Owner Who adds it Who can use it
Organization An organization, with its own key Only that organization. This is “bring your own key”.
Platform A platform admin, with the platform’s key The organizations in its availability. See Platform.

An organization can use a model of a provider only when it may use both the provider and the model.

Each model has one of three types:

  • A language model writes text and calls tools. It runs Turns, judges Rules and learns Bot memory.
  • A decision model only picks an answer from a fixed list. It does not write text. It can judge Rules. Jev from TypeSafe is an example. See Decision model.
  • A live model talks with a person in real time, with sound. Only Live mode uses it.

When you run OpenBot from code, you can also give built-in models. See Built-in models.


POST /v1/organization/models adds a model to your organization. It needs write on ModelProvider.

Terminal window
curl -H "Authorization: Bearer $OPENBOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "id": "qwen3-32b", "name": "Qwen3 32B", "reasoningEfforts": [], "serviceTiers": [], "contextWindow": 131072 }' \
https://api.example.com/v1/organization/models
Field Rules
id Required. 1 to 200 characters. It is unique for its owner, and you cannot change it. A provider sends it as the model name, unless the link sets upstream.
type Optional. language, decision, or live. The default is language. You cannot change it later.
name Required. A name for people.
reasoningEfforts Required. The reasoningEffort values that the model accepts. Can be empty.
serviceTiers Required. The serviceTier values that the model accepts. Can be empty.
contextWindow Optional. The size of the context window, in tokens.
maxOutputTokens Optional. The most tokens that the model can write in one answer.

If your organization already has a model with this id, OpenBot returns 409 with ModelConflict.

Route What it does
GET /v1/organization/models Lists the models of your organization. Shared models are not in this list.
GET /v1/organization/models/:modelId Reads one model.
PATCH /v1/organization/models/:modelId Changes a model. The body needs expectedVersion. Send null for a limit to remove it.
DELETE /v1/organization/models/:modelId Removes a model.

Reading needs read on ModelProvider. Changes need write on ModelProvider. You cannot remove a model while one of your providers links it. OpenBot returns 409 with ModelInUse. Remove the link first.

POST /v1/organization/model_providers adds a provider to your organization. It needs write on ModelProvider.

Terminal window
curl -H "Authorization: Bearer $OPENBOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kind": "openai-compat",
"name": "Local vLLM",
"endpoint": "https://llm.example.com/v1",
"apiKey": "sk-...",
"models": [
{ "model": "qwen3-32b", "modelOwner": "organization", "upstream": "Qwen/Qwen3-32B" }
]
}' \
https://api.example.com/v1/organization/model_providers
Field Rules
kind Required. The API that the service speaks: chatgpt, openai, anthropic, openai-compat or typesafe. See the table below.
name Required. 1 to 120 characters.
endpoint Required for openai-compat and typesafe. Optional for the others. An http or https base URL, often ending in /v1. No user name, password, query, or # part.
apiKey Required except for chatgpt. ChatGPT uses browser sign-in.
models Required. 0 to 200 links. The order is the order of the model list.
kind API Model types it can link
openai OpenAI Responses API, and the Realtime API for live. language and live
anthropic Anthropic Messages API. language
chatgpt OpenAI Responses API (OAuth) language
openai-compat OpenAI Chat Completions API, and services that copy it. language and decision
typesafe The decision API of TypeSafe. decision

An openai-compat provider can link both types, so a gateway (a service that passes calls on to other services), such as OpenRouter or LiteLLM, can offer both. OpenBot connects only to public addresses. For a model server in your own network or on your machine, the operator must list its host in OPENBOT_OUTBOUND_ALLOW. Until then, calls to it fail. See Outbound connections. A typesafe provider has no default address yet, because TypeSafe has not published one official address. Give the address that your account uses. If a provider links a model of a type that it cannot serve, OpenBot returns 400 with ModelProviderInvalid.

Each link has these fields:

Field Rules
model Required. The id of the model. A provider links each model ID only once.
modelOwner Required. organization for a model of your organization, or platform for a shared model.
upstream Optional. The model name that the service gets, when it is not the model id.
reasoningEfforts Optional. Replaces the reasoningEfforts of the model for this provider.
serviceTiers Optional. Replaces the serviceTiers of the model for this provider.
contextWindow Optional. Replaces the contextWindow of the model for this provider.
maxOutputTokens Optional. Replaces the maxOutputTokens of the model for this provider.

A provider of your organization can link your own models, and the shared models that the platform makes available to you. For any other model, OpenBot returns 404 with ModelNotFound.

OpenBot answers with the provider. Its id starts with prov_. A provider ID is unique in the whole platform. A model selection names it as provider. The API key never comes back. The answer shows apiKey: { set, hint } instead. See Platform.

Route What it does
GET /v1/organization/model_providers Lists the providers.
GET /v1/organization/model_providers/:providerId Reads one provider.
PATCH /v1/organization/model_providers/:providerId Changes a provider.
DELETE /v1/organization/model_providers/:providerId Removes a provider.

Reading needs read on ModelProvider. Changes need write on ModelProvider.

The body of PATCH needs expectedVersion, the version that you read. If someone changed the provider after you read it, OpenBot returns 409 with ModelProviderConflict. Leave out apiKey to keep the current key. Send models to replace all links of the provider.

Terminal window
curl -X PATCH -H "Authorization: Bearer $OPENBOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"expectedVersion":0,"apiKey":"sk-new..."}' \
https://api.example.com/v1/organization/model_providers/prov_4c1e...

The next model call after a change uses the new key, endpoint or links. OpenBot builds each model once, and builds it again when its provider changes.

You cannot remove a provider while rulesModel or botMemoryModel in the organization settings names it. OpenBot returns 409 with ModelProviderInUse. Change the setting first.


A chatgpt model provider uses browser sign-in in place of an API key. The organization or platform owns the provider. All work that may use it uses the same connected account. ChatGPT uses the same model links, permissions, prices, budgets, and platform credits as other providers.

Create it with POST /v1/organization/model_providers:

{ "kind": "chatgpt", "name": "ChatGPT", "models": [] }

Do not send apiKey or endpoint. Keep the returned prov_* ID. For a platform provider, use /v1/platform/model_providers and set availability. You need the same provider write rights as for an API key provider.

Use the provider API to start sign-in, submit the browser callback, and disconnect the account. The paths below start with /v1/organization/model_providers/:providerId. For the platform, replace organization with platform.

Method and path Use
POST /oauth/authorizations Start sign-in with redirectUri and expectedVersion. Read id, url, and expiresAt.
POST /oauth/authorizations/:authorizationId/complete Send the full callbackUrl from the local listener.
DELETE /oauth Clear tokens and try to end the remote session. Keep the provider and model links.
GET /available_models Read the account’s upstream models in items.

The local listener uses http://127.0.0.1:<port>/auth/callback. Start it before opening the authorization URL. A sign-in attempt lasts ten minutes and can be completed once, by the user who started it. A provider change ends old attempts. API keys and same-origin session cookies work. Cookie writes need the exact public Origin. The server stores tokens encrypted and renews them as needed.

Read the provider’s oauth field for connected, loginRequired, and account info. No token comes back. The apiKey field has set: false and hint: null. If sign-out returns revoked: false, local tokens are gone but remote sign-out was not confirmed. You can also disconnect the app in ChatGPT Settings.

The available model list does not change saved models. Add a model and link it through the normal provider API. Use the returned model id as the upstream name when the local model ID differs. Select the normal provider ID:

{ "_tag": "Config.Selection", "provider": "prov_example", "model": "my-model" }

ChatGPT works in Chats, Rules, Bot memory, and scheduled work. It supports language models and OpenBot function tools, not Live audio. It uses Sign in with ChatGPT and the public Responses API. Plan limits still apply. A failed login or a spent plan limit does not switch the work to another provider.

See Client for typed calls.


GET /v1/organization/model_catalog lists the models that you may use now: the models of your organization where your policy allows use on Model. Each item is one model of one provider, with the values of the link already applied. A model that two providers link shows two times.

Terminal window
curl -H "Authorization: Bearer $OPENBOT_TOKEN" \
https://api.example.com/v1/organization/model_catalog
{
"providers": [
{
"id": "prov_4c1e...",
"owner": "organization",
"kind": "openai-compat",
"name": "Local vLLM"
}
],
"models": [
{
"provider": "prov_4c1e...",
"id": "qwen3-32b",
"owner": "organization",
"type": "language",
"name": "Qwen3 32B",
"reasoningEfforts": [],
"serviceTiers": [],
"contextWindow": 131072,
"price": {
"model": "qwen3-32b",
"timeZone": "UTC",
"tiers": [
{
"upToInputTokens": null,
"inputUncached": 400000,
"inputCacheRead": 100000,
"inputCacheWrite": 0,
"output": 1200000,
"outputReasoning": null
}
],
"periods": [],
"effectiveFrom": "2026-10-20T08:00:00.000Z"
}
}
]
}

price is the current price of the model at this provider. It is missing when nobody set a price. See Prices.

A model selection names both the provider and the model ID:

{ "_tag": "Config.Selection", "provider": "prov_4c1e...", "model": "qwen3-32b" }

OpenBot never picks a model for you. There is no default model. Each use names its own model:

  • A new Chat uses the model of its Bot. When the Bot has no model, the first message to it fails with 400, ChatModelInvalid and the reason missing. A new Bot has no model unless you give it one, so set its model when you make it, or later with PATCH /v1/bots/:id.
  • The judge of Rules uses rulesModel in the organization settings. When it is null, the judge gives no answer, so a person or a Bot decides.
  • Bot memory uses botMemoryModel in the same settings. When it is null, Bots learn nothing.

rulesModel can be a language model or a decision model. botMemoryModel must be a language model, because the memory Bot writes text. Otherwise OpenBot returns 400 with SettingsInvalid. The catalog gives the type of each model, and a Chat can choose only language models.

Older versions of OpenBot had defaultModel and helperModel. When you upgrade, OpenBot gives the old defaultModel to each Bot that has no model. rulesModel and botMemoryModel get the old helper model: helperModel, or else defaultModel. When both were null, the new values stay empty, and you must set them.

For example, to give a Bot a model, send its current revision as expected:

Terminal window
curl -X PATCH -H "Authorization: Bearer $OPENBOT_TOKEN" -H 'Content-Type: application/json' \
-d '{"expected":0,"model":{"_tag":"Config.Selection","provider":"prov_4c1e...","model":"qwen3-32b"}}' \
https://api.example.com/v1/bots/$BOT_ID

A Chat can save its own choice. Send the current Chat revision as expected:

Terminal window
curl -X PATCH -H "Authorization: Bearer $OPENBOT_TOKEN" -H 'Content-Type: application/json' \
-d '{"expected":3,"model":{"_tag":"Config.Selection","provider":"prov_4c1e...","model":"qwen3-32b"}}' \
https://api.example.com/v1/chats/$CHAT_ID

Send "model": null to use the model of the Bot again. GET /v1/chats/:id/models shows the same models for one Chat. See the Chats API reference for all fields.

To limit which providers a member may choose, give the member a deny rule for use on Model with the provider field. See Permissions.

Your organization has no setting for one decision model. Each use of a model names its own model. Some uses can take a decision model or a language model, so one fixed decision model for the whole organization does not fit them.

For example, to use Jev through TypeSafe, add the model, then a provider that links it:

Terminal window
curl -H "Authorization: Bearer $OPENBOT_TOKEN" -H "Content-Type: application/json" \
-d '{ "id": "jev-latest", "type": "decision", "name": "Jev", "reasoningEfforts": [], "serviceTiers": [] }' \
https://api.example.com/v1/organization/models
curl -H "Authorization: Bearer $OPENBOT_TOKEN" -H "Content-Type: application/json" \
-d '{
"kind": "typesafe",
"name": "TypeSafe",
"endpoint": "https://typesafe.example.com/v1",
"apiKey": "...",
"models": [{ "model": "jev-latest", "modelOwner": "organization" }]
}' \
https://api.example.com/v1/organization/model_providers

GET /v1/organization/model_catalog then lists Jev with "type": "decision".

OpenBot calls a typesafe provider with the TypeSafe client of Effect (@effect/ai-typesafe). It sends each request to <endpoint>/systemone with the API key as a bearer token, and sends upstream as the model name. OpenBot builds the client once for each provider and model, and builds it again when the provider changes. OpenBot cannot ask a decision model through an openai-compat provider yet. You can link one there, but use a typesafe provider to ask it.

The judge of Rules can use a decision model. Set rulesModel to it. See The judge model.

An organization without any provider can still read the Chats that it has, and delete their history. It cannot talk to a Bot, because each Turn needs a model. See Choose a model.

A message to a Bot fails with status 503 and the error ChatExecutionUnavailable when no model can run its Turn:

{ "_tag": "ChatExecutionUnavailable", "chat": "..." }

A notice from OpenBot, such as the result of a delegation, still goes into the inbox of its Chat. See Notices.


For this development task, see Model backends.

For this development task, see Model backends.