MachineMachine
MachineMachine
MachineGetting startedQuickstartAccounts & access
Your project
AgentsLegacy kortix.tomlManifest referenceModelsAgent permissionsSecrets
Running work
Change requestsRuntime & sandboxSessions
Connect & automate
ComputersConnectorsInkbox: iMessage, SMS, email & callsMCP serverPixio: images, video & audioRemindersSlack & channelsMicrosoft TeamsTriggers
Feature flags
Apps

Develop

CLI
TypeScript SDKFull exampleAuthenticationSign in with KortixSessionsAppsReact hooksSDK reference
Machine as a BackendAPI reference
GitHub
Your project

Models

How Machine picks a model, and how billing works for managed vs. your-own-key models.

Machine runs each session on a model. This page explains managed models vs. your own provider key (BYOK), how Machine picks a model automatically, and two billing gotchas to know.

This page applies to projects with the LLM Gateway on. LLM Gateway is an experimental feature flag, on by default where the platform offers it — check or toggle it in Settings → Experimental (operators can default a whole deployment off with LLM_GATEWAY_DEFAULT_ENABLED=false).

Turning the flag off is a fully supported path. The project then runs native OpenCode model management:

  • Your provider API keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY, …) are injected into the sandbox as ordinary env vars — add them on the Model settings page or as secrets. OpenCode connects each provider from its key automatically.
  • Model ids are OpenCode's native provider/model refs, like anthropic/claude-opus-4-8. Managed bare ids and kortix/… refs do not exist off-gateway.
  • The model picker shows one list before and after the sandbox boots: the providers your keys connect, plus OpenCode Zen's free models (OpenCode connects those without a key). Thinking effort is the composer's thinking control (the model's own variants); the gateway's Generation defaults do not apply.
  • The gateway surfaces on this page — managed models, the model-defaults chain, budgets, logs — do not apply; OpenCode resolves the default model in the sandbox.

Managed models and BYOK

A model id has one of three shapes:

  • Managed — a bare id, like grok-4.6 or deepseek-v4-pro-0813. Machine supplies the credentials. Cloud accounts pay with Machine credits.
  • BYOK — a provider/model id, like anthropic/claude-opus-4-8. You supply the key. Your provider account pays.
  • ChatGPT — a codex/<id> id. You connect your ChatGPT plan once through OAuth, and it pays.

Connect a BYOK key on the project's Model settings page, or set the provider's env var directly as a secret.

Each provider reads its own key. Some providers share one env var on models.dev, for example OpenCode Zen (opencode) and OpenCode Go (opencode-go) both list OPENCODE_API_KEY. The provider that owns the name keeps it. Every other provider reads <PROVIDER_ID>_API_KEY: OpenCode Go reads OPENCODE_GO_API_KEY, Z.ai Coding Plan reads ZAI_CODING_PLAN_API_KEY, and Moonshot China reads MOONSHOTAI_CN_API_KEY. A Zen key therefore never lists Go models. The connect form and kortix providers set use the right name.

OpenCode Zen and Go (sign in with OpenCode)

Connect OpenCode Zen or OpenCode Go with an API key, or sign in with your OpenCode Console account instead:

  1. Open Customize → Models → Providers and find OpenCode Go.
  2. Choose Sign in with OpenCode. Copy the code, choose Open auth page, and approve the device in your OpenCode workspace.
  3. The key field shows the provider as connected. Remove the key to sign out.

From the CLI: kortix providers login opencode-go, or kortix providers login opencode for Zen. Zen models are used by id, for example opencode/glm-5.3-flash; the picker does not list them. Zen and Go each hold their own login, so signing in to one never connects the other. The login lasts 30 days, and Machine renews it before it expires. When OpenCode refuses a login, Machine renews it once and retries the request. When the renewal fails too, the request returns provider_reauth_required: sign in again.

Disable providers or models

Open Customize → Models → Providers. Open the ⋯ menu in the Machine row and choose Disable provider to use only your own providers. Machine appears in the same list as other providers, with its model count and credit-billing description. Every provider uses the same menu. Only disabled providers show a status badge. Choose Enable provider from the menu to restore access.

Open the Models tab to enable or disable individual models. A disabled provider blocks all of its models, including models added later. Re-enabling the provider preserves individual model choices. Credentials stay saved throughout.

The project default and its provider cannot be disabled. Select a different enabled default first. Project managers can change access; readers can inspect it.

These controls apply to new requests through the Machine gateway, including saved session selections and fallback candidates. A request already sent to an upstream finishes normally. Disabled targets return provider_disabled or model_disabled; choose an enabled model to continue. Native OpenCode mode does not use this gateway policy.

Existing picker-only visibility preferences remain separate. Resetting the model list does not remove explicit access restrictions.

Thinking effort

The composer's thinking control sets the session's model variant in both modes. The choices are the model's own published tiers (models.dev reasoning_options), never a fixed ladder; a model without a knob shows no control. Auto clears the variant.

  • Native (gateway off): OpenCode applies the variant as the provider's own request field.
  • Gateway on: the request carries reasoning_effort; the gateway maps it per upstream (OpenAI → reasoning_effort, Claude → adaptive thinking, OpenAI on Amazon Bedrock → Bedrock's reasoning.effort request field). For an upstream it cannot map yet (Nova, Grok on Bedrock today) the value is dropped and the model runs at its own default.
  • Amazon Bedrock refuses the bare in-region id of most current models ("on-demand throughput isn't supported"). The picker prefers the global. / regional inference-profile id when the catalog carries one, and the gateway retries a refused bare id once per profile prefix (global., us.).
  • The sandbox learns the project's servable model set from the API at every boot (GET /v1/llm/models?scope=picker, the same composition as the web picker), so a model the picker offers always resolves in the runtime.
  • A project default per model lives in Customize → Models → Routing → Advanced → Generation defaults (model_generation_config). It fills only a field the request left unset, so a session's variant always wins.

How auto picks a model

Set no model, and Machine resolves one through five layers, in order. (The id auto covers this same behavior, but it is not yet a selectable option in the model picker.)

  1. An explicit pin — a session, channel, or trigger's own model: field.
  2. The agent's default for this project.
  3. The project's default.
  4. The account's default.
  5. The platform default.

Machine uses the first layer that has a value it can still serve. A saved default that stops working — a disconnected key, a retired model — is skipped automatically. A session never dies from a stale default. See the manifest reference for the trigger model: field.

:::warning[Billing surprises on BYOK] Two costs are easy to miss on a paid cloud account:

  • Platform fee. Machine adds a 10% fee, billed as credits, on top of what your own provider charges. Free-tier and self-hosted accounts are exempt.
  • Fallback chains. If your BYOK key or ChatGPT plan fails, for example on its usage limit, Machine runs the fallback chain you set in Customize → Models → Routing. The chain also runs when every key or ChatGPT account the request may use is paused after a rate limit. With Retry on: Any error, it also runs when they need reconnection. A Machine model in that chain bills your credits, and runs only when your account can pay for it. The platform's own fallback never moves a BYOK or ChatGPT request onto credits.

If you see credit charges on a BYOK-only project, check these two causes before reporting a billing bug. :::

When a fallback model answers, the session says so. The composer shows Running on and the model's name beside the model selector. Each turn's details name the model that answered, the model it replaced, and what Machine billed for the turn. Customize → Models → Costs counts that spend under the model that answered.

Per-project model enablement

The project controls which models its pickers offer. By default, the newest model of each family is offered automatically. Machine-managed models and any model your project's defaults or routing policy reference are always offered — a guard never prunes them.

You can override the default for individual models on the Manage models page (Customize → Models). An exception is stored per project and takes effect immediately. The session model picker and the command palette hide anything you turn off; new models stay on by default as the catalog grows.

Enablement governs what is offered, not what is served: a request that names a disabled model outright (for example through the raw API) still runs. The project's default model cannot be turned off — set a different default first.

Provider keys and member access

With Pooled provider secrets off, a connected provider key applies to the whole project. Personal overrides for these project secrets return llm_credentials_project_wide.

Enable Pooled provider secrets and LLM Gateway in Settings → Experimental to use provider secret resources. Open Customize → Models → Providers and choose Add key beside a provider. Give each key a distinct label. The value is write-only; later screens show its label and access list.

New keys belong to this project and are private to you by default (Only you). Any project member can add a key for their own use. Sharing a key with Everyone in this project or Specific members requires permission to manage project secrets; without it, those options are disabled. Open a key's ⋯ → Manage access to change who can use it. Legacy account-wide resources retain their member grants. Account membership, project permissions, the agent's secret permissions, and session selection still apply. A member grant permits use without revealing the key. Rotating a key preserves its access and session selections. Deleting it removes access for everyone.

Choose keys for a session

Open the composer's Session overrides → Provider keys. Choose a provider, then select up to ten keys. Choose Save changes in the panel footer. For a new session, the selection is sent with the first prompt.

  • Without an explicit selection, the existing project key remains available.
  • Selecting no keys disables that provider for the session. It does not restore the project key.
  • Reset to project default removes the explicit selection.
  • Revoked or inactive keys appear as unavailable. Remove them from the selection, select another key, or reset the selection.
  • Deleting the last selected key leaves an empty pool. The session settings still show that provider so you can reset it.
  • A failed read shows Try again. It never appears as an inherited selection.

A session lists only the keys it can use when it runs: keys shared with the whole project, and, in your own private session, keys granted to you. A session shared with the project never uses a key granted to one member, so the list leaves those keys out and saving one is refused. Changing a shared session to a model that only such keys reach is refused too, instead of failing every turn.

Sharing a private session, with the whole project or with chosen people, is checked the same way. When its model ran only on keys that work in your private sessions, such as your own ChatGPT connection, the session switches to every key shared with the whole project for that provider. When no such key runs the model, the share is refused with 409 SHARED_SESSION_NEEDS_PROJECT_KEY and the session stays private. Share a key with the whole project, or switch the session to another model, then share it.

Sessions that name only a model

The CLI, the SDK, and Teams and Slack name a model but no keys. When that model runs only on pooled keys, the session gets every key its creator may use for that provider, up to ten, so they rotate. The agent must be allowed the key's name in its secrets. Otherwise the turn fails with agent_grant_excludes: add the name, for example CODEX_AUTH_JSON for ChatGPT, to the agent's secrets, or choose another agent. Changing a running session to such a model does the same when the session has no selection for that provider. A selection made on purpose, an empty one included, is left alone.

Your own keys and ChatGPT connections count only in a session that is private to you and acts on your behalf. In a shared session only keys shared with the whole project count: a Teams channel or group chat, a Slack channel.

On a rate limit before output starts, the gateway tries another selected key. It never replays a partial answer across keys. If every key is rate-limited, retry after the earliest cooldown, capped at 60 seconds. An explicit pool does not silently borrow another member's key or the legacy project credential.

ChatGPT accounts (bring your own subscription)

Every project member can connect their own ChatGPT Plus or Pro subscription. Project permissions to manage secrets are not required.

  1. In a session, open the model picker and choose Use your ChatGPT subscription. Project managers can also use Models → Providers → ChatGPT accounts.
  2. Choose Connect ChatGPT. The label defaults to ChatGPT · <your name>.
  3. Keep Only you, then choose Connect account. Sign in to ChatGPT with the code shown. If the authorization fails or times out, the dialog shows why; choose Try again.

The subscription's models appear in your model picker under ChatGPT subscription. Each authorization creates a separate named account, so you can connect more than one. Only you means only you can select the account; choosing Everyone in this project or Specific members requires permission to manage project secrets.

If ChatGPT signs you out, open the account's ⋯ → Reconnect and sign in again. Reconnecting keeps the account's name, access, and session selections. Only the member who connected an account can reconnect it. Account owners and admins can delete any account.

When an account's login stops working, the account shows Needs reconnection. This happens when ChatGPT rejects the renewal of the login, or when the stored login cannot be read. ChatGPT can also refuse a login before it expires. Machine then renews the login once and retries the request. If the renewal is refused too, the account needs reconnection, and the request moves to the next selected account when there is one. A brief outage at ChatGPT does not mark an account. The member who connected the account sees Reconnect beside it. Session overrides → Provider keys shows the same mark. A turn that fails names the accounts that need reconnection. The account stays selected, and reconnecting it or a successful renewal clears the mark.

A turn that fails for this reason also shows the fix beside its error:

  • provider_reauth_required shows Reconnect ChatGPT. It appears in your own private session, and in a shared session that selects ChatGPT accounts.
  • provider_not_connected shows Connect ChatGPT. It appears only in your own private session without a ChatGPT selection. A session with a selection is fixed in Session overrides → Provider keys instead.

Both open your ChatGPT accounts. After you connect or reconnect, send your message again. In Slack and Microsoft Teams the same failure reads ChatGPT login needs reconnection.

A session can select multiple granted ChatGPT accounts in Session overrides → Provider keys. Without a selection, the member's newest usable connection is the default in their own private session. A connection marked Needs reconnection is the default only when all of the member's connections are marked. A session shared with the project never uses a member's own connection: select a connection shared with the whole project for it. Scheduled sessions cannot use an Only you account either. Another member's shared connection requires an explicit selection. The legacy project login remains available when the member has no usable connection of their own. A project gateway key uses the connections shared with Everyone in this project. It never uses its creator's private connection or a connection restricted to specific members.

A project manager can turn off ChatGPT subscription for the whole project in Models → Providers. The picker then hides the connect entry.

Google Gemini API keys use Google's OpenAI-compatible endpoint with the gateway on. Native OpenCode mode continues to use project secrets.

Models hidden by older picker preferences show Hidden from picker. Direct requests remain allowed. Open that model’s menu and choose Disable model to block inference too.

Provider model-count links open the Models tab. All provider groups appear together in the same list used to enable or disable models and set defaults. Search narrows the list. Connect a provider before managing its models.

Provider selections stay as drafts until you click Save changes in the session overrides footer. This saves changes for every provider, including a reset to the default. You can switch sections or close and reopen the panel without losing these drafts. Reloading the page discards unsaved changes. If a save fails, the panel stays open and shows the error. Correct the selection or retry. Providers already saved remain saved; unfinished changes stay in the panel.

Use the gateway from Claude Code

The gateway serves the Anthropic Messages API at POST /v1/messages. Claude Code and Anthropic SDKs can call any gateway model through it.

  1. Create a key in Customize → Gateway, tab Gateway.
  2. Start Claude Code with the gateway as its API endpoint:
bash
ANTHROPIC_BASE_URL=https://machineapi.myapps.ai/v1/llm \
ANTHROPIC_AUTH_TOKEN=mach_... \
claude --model deepseek-v4-pro-0813

The gateway reads the key from Authorization: Bearer or from x-api-key, so ANTHROPIC_API_KEY also works.

The gateway translates these Claude Code fields for every provider:

  • thinking: {type: "adaptive"} with output_config.effort becomes reasoning_effort. See Thinking effort.
  • Usage on the final message_delta carries input_tokens, cache_read_input_tokens, and cache_creation_input_tokens. Claude Code uses them for /context and auto-compaction.
  • Images that a tool returns, such as a Read of a screenshot, reach the model in a user message after the tool result.
  • A provider stream that ends before completion returns an api_error event, so Claude Code retries the turn.

Limits:

  • /v1/messages/count_tokens returns 404. Claude Code then estimates counts from characters.
  • Reasoning text is not returned as thinking blocks.
  • ChatGPT models (codex/*) need a ChatGPT connection shared with Everyone in this project. A gateway key cannot use a private connection.
  • Claude Code cannot set a context window per model. Set CLAUDE_CODE_MAX_CONTEXT_TOKENS to the smallest window you use.
PreviousManifest referenceNextAgent permissions

On this page

Managed models and BYOKOpenCode Zen and Go (sign in with OpenCode)Disable providers or modelsThinking effortHow auto picks a modelPer-project model enablementProvider keys and member accessChoose keys for a sessionSessions that name only a modelChatGPT accounts (bring your own subscription)Use the gateway from Claude Code