> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://platform.bctrl.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://platform.bctrl.ai/_mcp/server.

# AI models

> Choose models and manage the credentials that Agents can use.

Use `bctrl.ai.models` to discover the models BCTRL can use for hosted Agent
execution. Use `bctrl.ai.credentials` when a model should run with a
customer-owned provider key. A model catalog entry and a saved credential are
different resources:

| Resource        | Answers                                                    | SDK entry point                |
| --------------- | ---------------------------------------------------------- | ------------------------------ |
| Model           | Which model IDs are available, and what can each model do? | `bctrl.ai.models`              |
| Credential      | Which provider key can an Agent use?                       | `bctrl.ai.credentials`         |
| Saved selection | Which model and authentication should a Space default use? | `Space.environment.ai.default` |

The model catalog is the source of truth for availability. Providers and model
IDs can change as the catalog evolves, so discover them at runtime instead of
hard-coding a complete list.

## Choose a model

List recommended models:

```ts
const models = await bctrl.ai.models.list({
  status: "recommended",
});

const modelId = models.data[0]?.id;
if (!modelId) {
  throw new Error("No recommended Browser Use model is available");
}

for (const model of models.data) {
  console.log(model.id, model.displayName, model.provider);
}
```

To inspect the full catalog, omit the filters. You can derive the currently
available providers from the response:

```ts
const { data } = await bctrl.ai.models.list();
const providers = [...new Set(data.map((model) => model.provider))];

console.log(providers);
```

Use the returned model ID when creating a Conversation or sending a message:

```ts
const conversation = await bctrl.conversations.create({
  runtimeId,
  model: modelId,
});

await bctrl.conversations.messages.create(conversation.id, {
  text: "Summarize the page",
  model: modelId,
});
```

### Model filters

| Parameter  | Type                                             | Required | Description                                          |
| ---------- | ------------------------------------------------ | -------- | ---------------------------------------------------- |
| `provider` | `AiModelProvider`                                | No       | Return models from one provider.                     |
| `status`   | `"recommended" \| "supported" \| "experimental"` | No       | Filter by support level.                             |
| `managed`  | `boolean`                                        | No       | Filter by whether BCTRL-managed access is available. |

### Providers and support status

The model catalog currently uses these provider identifiers:

```ts
type AiModelProvider =
  | "openai" | "anthropic" | "google" | "azure" | "groq" | "deepseek"
  | "mistral" | "cerebras" | "openrouter" | "xai" | "perplexity"
  | "togetherai" | "minimax" | "tencent" | "xiaomi" | "z-ai"
  | "mistralai" | "x-ai" | "moonshotai" | "meta-llama"
  | "vercel-ai-gateway";
```

The catalog status tells you how to choose among returned models:

| Status         | Meaning                                                    |
| -------------- | ---------------------------------------------------------- |
| `recommended`  | Curated default for hosted Agent execution.                |
| `supported`    | Available for production use, but not the curated default. |
| `experimental` | Available for evaluation and subject to change.            |

`managed: true` means BCTRL-managed access is available for that model. When a
model requires your own provider account, save a credential and select it with
`auth` as shown below.

### Model fields

| Field                      | Type                                             | Always present | Description                                              |
| -------------------------- | ------------------------------------------------ | -------------- | -------------------------------------------------------- |
| `id`                       | `string`                                         | Yes            | Model identifier returned by the catalog.                |
| `provider`                 | `AiModelProvider`                                | Yes            | Provider that supplies the model.                        |
| `displayName`              | `string`                                         | Yes            | Human-readable model name.                               |
| `managed`                  | `boolean`                                        | Yes            | Whether BCTRL-managed access is available for the model. |
| `status`                   | `"recommended" \| "supported" \| "experimental"` | Yes            | Support level.                                           |
| `supportsTools`            | `boolean`                                        | Yes            | Whether the model supports Tool use.                     |
| `supportsVision`           | `boolean`                                        | Yes            | Whether the model accepts visual input.                  |
| `supportsStructuredOutput` | `boolean`                                        | Yes            | Whether structured output is supported.                  |
| `supportsReasoningEffort`  | `boolean`                                        | Yes            | Whether reasoning effort can be selected.                |
| `supportsThinkingBudget`   | `boolean`                                        | Yes            | Whether a thinking token budget can be selected.         |

## Store a provider credential

Create a credential when an Agent should use your provider account:

```ts
const credential = await bctrl.ai.credentials.create({
  name: "openai-production",
  provider: "openai",
  apiKey: process.env.OPENAI_API_KEY,
  test: true,
});

console.log(credential.id, credential.hasApiKey);
```

The API key is accepted only when creating or updating the credential. It is
not returned by list, get, or update responses. Use the credential ID in a
Space environment or a saved model selection.

For an OpenAI-compatible provider, set `provider: "custom"` and provide a
`baseUrl`:

```ts
const credential = await bctrl.ai.credentials.create({
  provider: "custom",
  apiKey: process.env.MODEL_API_KEY,
  baseUrl: "https://models.example.com/v1",
});
```

### Credential fields

| Parameter      | Type                      | Required | Description                                                    |
| -------------- | ------------------------- | -------- | -------------------------------------------------------------- |
| `provider`     | `AiCredentialProvider`    | Yes      | Provider whose API accepts the credential.                     |
| `name`         | `string`                  | No       | Human-readable label.                                          |
| `apiKey`       | `string`                  | No       | Provider key. Required when creating an enabled credential.    |
| `status`       | `"enabled" \| "disabled"` | No       | Whether the credential can be used.                            |
| `defaultModel` | `string`                  | No       | Default model for this credential.                             |
| `baseUrl`      | `string`                  | No       | OpenAI-compatible endpoint. Required for `provider: "custom"`. |
| `test`         | `boolean`                 | No       | Test the credential after creation.                            |

`baseUrl` is valid only for `provider: "custom"`. A disabled credential may be
created without an API key, but it cannot be used until enabled with a key.

Credentials support these provider identifiers:

```ts
type AiCredentialProvider =
  | "openai" | "anthropic" | "google" | "azure" | "groq" | "deepseek"
  | "mistral" | "cerebras" | "openrouter" | "xai" | "perplexity"
  | "togetherai" | "vercel-ai-gateway" | "custom";
```

The credential provider set is intentionally smaller than the model catalog:
some catalog providers are reached through another provider or through an
OpenAI-compatible `custom` endpoint.

## Saved model selections

The Space environment default uses `AiStoredModelSelection`. It is either a model
ID string or an object when the model needs explicit authentication or
controls. The model ID is always required in the object form.

The short form is usually enough:

```ts
default: modelId
```

Use a saved credential with the object form:

```ts
default: {
  model: modelId,
  auth: { credential: credential.id },
  reasoningEffort: "medium",
}
```

The object form has this shape:

```ts
type AiStoredModelSelection =
  | string
  | {
      model: string;
      auth?: "managed" | { credential: string };
      provider?: AiCredentialProvider;
      reasoningEffort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
      thinkingBudgetTokens?: number;
      responseFormat?: "text" | "json";
      responseSchema?: Record<string, unknown>;
      request?: Record<string, unknown>;
    };
```

When `auth` names a saved credential, the provider comes from that credential;
do not send a separate `provider` in the same selection.

### Selection fields

| Field                  | Type                                                                     | Required | Description                                                           |
| ---------------------- | ------------------------------------------------------------------------ | -------- | --------------------------------------------------------------------- |
| `model`                | `string`                                                                 | Yes      | Model ID returned by `bctrl.ai.models.list()`.                        |
| `auth`                 | `"managed" \| { credential: string }`                                    | No       | Use BCTRL-managed access or a saved credential.                       |
| `provider`             | `AiCredentialProvider`                                                   | No       | Provider override when the selection does not use a saved credential. |
| `reasoningEffort`      | `"none" \| "minimal" \| "low" \| "medium" \| "high" \| "xhigh" \| "max"` | No       | Reasoning effort, when the selected model supports it.                |
| `thinkingBudgetTokens` | `number`                                                                 | No       | Thinking-token budget, when supported by the selected model.          |
| `responseFormat`       | `"text" \| "json"`                                                       | No       | Preferred response format.                                            |
| `responseSchema`       | `Record<string, unknown>`                                                | No       | JSON schema for a structured response.                                |
| `request`              | `Record<string, unknown>`                                                | No       | Provider/model-specific request options.                              |

The selection also accepts advanced generation, tool, routing, and provider
options defined by the SDK type. Use the model capability flags before sending
those options; an option supported by one provider or model may not be valid
for another.

## Manage credentials

```ts
for await (const item of bctrl.ai.credentials.iter({ status: "enabled" })) {
  console.log(item.id, item.name, item.provider, item.defaultModel);
}

const current = await bctrl.ai.credentials.get(credential.id);
await bctrl.ai.credentials.test(credential.id);

await bctrl.ai.credentials.update(credential.id, {
  name: "openai-production-rotated",
});

await bctrl.ai.credentials.delete(credential.id);
```

Credential responses expose metadata only:

| Field                     | Type                      | Always present | Description                                                |
| ------------------------- | ------------------------- | -------------- | ---------------------------------------------------------- |
| `id`                      | `string`                  | Yes            | Credential identifier.                                     |
| `name`                    | `string`                  | Yes            | Credential label.                                          |
| `provider`                | `AiCredentialProvider`    | Yes            | Provider.                                                  |
| `status`                  | `"enabled" \| "disabled"` | Yes            | Current status.                                            |
| `subaccountId`            | `string`                  | No             | Subaccount owner, when applicable.                         |
| `defaultModel`            | `string`                  | No             | Default model, when configured.                            |
| `baseUrl`                 | `string`                  | No             | Custom provider endpoint, when configured.                 |
| `hasApiKey`               | `boolean`                 | Yes            | Whether a key is stored; the key itself is never returned. |
| `createdAt` / `updatedAt` | `string`                  | Yes            | Timestamps.                                                |