# Crew Blend AI API implementation guide

Source: https://crewblend.com/docs/
Updated: October 23, 2026

This text version mirrors the API and integration sections of the public guide.

## Implementation workflow


This is the recommended sequence for a developer or an AI coding assistant. Implement the public inference API from this guide; dashboard account-management endpoints are a separate interface. Do not assume that every OpenAI endpoint or feature exists on Crew Blend AI.
1. Prepare access. The owner activates an API-enabled plan, connects their own account or provider and generates a Crew Blend AI key with Models and requests permission. The coding assistant uses that key through a secret store; it does not need the owner's password or provider key.
2. Discover models. Call GET /v1/models. Present returned IDs to the owner or use an explicitly configured ID. Do not invent a model, choose data[0] blindly, or substitute a different model after a failure.
3. Choose a protocol. Start with Chat Completions for text and function tools. Use Responses when your client requires it and the selected connection is OpenAI or ChatGPT. DeepSeek uses Chat Completions.
4. Send a minimal request. Use a model ID, a text message and stream=false. Check HTTP status before parsing a success response.
5. Handle each response branch. Process text, tool calls, usage and errors explicitly. HTTP 200 alone is not proof of a completed stream.
6. Add context. Keep history in your application and resend required messages or input items, or, with Responses, store responses with store: true and continue with previous_response_id.
7. Add streaming or tools. Use the matching event and tool formats. Validate arguments and bound tool rounds.
8. Verify. Complete the integration checks below with the owner's authorized test connection before reporting success.


Keep keys on the server or in the client's secret store. Browser applications should call their own backend, which calls Crew Blend AI. Do not expose keys in public JavaScript or assume arbitrary browser origins are permitted.

[Download the API implementation guide as Markdown](https://crewblend.com/docs/api.md). This text export contains the same instructions and examples for tools that ingest documentation.

## API quickstart


SDK base URL: `https://api.crewblend.com/v1`. Raw HTTP endpoint example: `https://api.crewblend.com/v1/chat/completions`. Do not append a second /v1 in SDK configuration.

Prerequisites: active API access, a connected account or provider, and a key with request permission. Model names, IDs, timestamps and token counts below are illustrative; replace MODEL_ID with an ID from your own inventory.

### 1. Set a secret and discover models


macOS, Linux or Git Bash:

```
export CREWBLEND_API_KEY="YOUR_CREWBLEND_API_KEY"

curl --fail-with-body https://api.crewblend.com/v1/models \
  -H "Authorization: Bearer $CREWBLEND_API_KEY"
```


PowerShell:

```
$env:CREWBLEND_API_KEY = "YOUR_CREWBLEND_API_KEY"
$headers = @{ Authorization = "Bearer $env:CREWBLEND_API_KEY" }
$inventory = Invoke-RestMethod -Uri "https://api.crewblend.com/v1/models" -Headers $headers
$inventory.data | Select-Object id, owned_by
```


### 2. Select a model and send text


Save this JSON as request.json, replacing MODEL_ID. A file avoids shell quoting differences.

```json
{
  "model": "MODEL_ID",
  "messages": [
    {
      "role": "user",
      "content": "Hello"
    }
  ]
}
```


macOS, Linux or Git Bash:

```
curl --fail-with-body https://api.crewblend.com/v1/chat/completions \
  -H "Authorization: Bearer $CREWBLEND_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @request.json
```


PowerShell:

```
$body = Get-Content -Raw ./request.json
$result = Invoke-RestMethod -Method Post -Uri "https://api.crewblend.com/v1/chat/completions" -Headers $headers -ContentType "application/json" -Body $body
$result.choices[0].message.content
```


The last line reads a normal text answer. Reusable application code must also handle tool calls and empty content as described below.

## Authentication and endpoints


Model and inference requests need `Authorization: Bearer YOUR_CREWBLEND_API_KEY`. POST requests also need `Content-Type: application/json`. Dashboard session tokens and provider API keys are not substitutes for Crew Blend AI application keys.
- Method | Full path | Key permission
- GET | /v1/models | Models
- GET | /v1/models/{model_id} | Models
- POST | /v1/chat/completions | Requests
- POST | /v1/responses | Requests


Models and requests includes both permissions; Models only cannot infer. Expiry, revocation and plan access are checked independently. This guide does not define embeddings, images, audio, files, batch, realtime, assistants or a stored-response retrieval API.

## Discover models and choose a connection


GET /v1/models returns an object with a data array. This inventory is illustrative:

```
{
  "object": "list",
  "data": [
    {
      "id": "chatgpt/EXAMPLE_MODEL",
      "object": "model",
      "created": 0,
      "owned_by": "chatgpt",
      "context_window": 32768,
      "reasoning_efforts": [
        "low",
        "medium",
        "high"
      ],
      "default_reasoning_effort": "low"
    },
    {
      "id": "openai/EXAMPLE_MODEL",
      "object": "model",
      "created": 0,
      "owned_by": "openai"
    },
    {
      "id": "deepseek/EXAMPLE_MODEL",
      "object": "model",
      "created": 0,
      "owned_by": "deepseek"
    },
    {
      "id": "auto",
      "object": "model",
      "created": 0,
      "owned_by": "crewblend"
    }
  ]
}
```


Only use IDs returned to your account. context_window and reasoning_efforts can be absent; do not fabricate metadata. Inventory membership is not a guarantee of remaining quota when a request runs.
- chatgpt/ identifies an own ChatGPT model.
- openai/ or deepseek/ identifies a direct provider model.
- Unprefixed model IDs and provider aliases can appear. Prefer a prefixed ID for an unambiguous connection type.
- auto requests automatic selection. Its choice can change with inventory; it does not promise a fixed model.


GET /v1/models/{model_id} returns one model or 404. URL-encode the entire ID in the path. Refresh inventory after connection changes and do not silently substitute another model on 404 or 503.

## Chat Completions and context


POST /v1/chat/completions uses model and messages. Portable text roles are system, user and assistant. ChatGPT also accepts developer; direct provider support depends on the model. Assistant content can be null when it contains tool_calls.

```json
{
  "model": "MODEL_ID",
  "messages": [
    {
      "role": "system",
      "content": "Answer clearly and briefly."
    },
    {
      "role": "user",
      "content": "Hello"
    }
  ],
  "stream": false
}
```


### Example response


```
{
  "id": "chatcmpl_example",
  "object": "chat.completion",
  "created": 1791244800,
  "model": "MODEL_ID",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 10,
    "completion_tokens": 3,
    "total_tokens": 13
  }
}
```


Inspect each choice's finish_reason and message. Text is in message.content. Handle message.tool_calls before continuing. Content can be null or empty. finish_reason may be stop, tool_calls or a provider-specific result such as length; truncated content is not a complete answer.

ChatGPT usage can be omitted when telemetry is unavailable. Do not invent token counts or interpret missing usage as zero. Ignore unknown additional response fields.

### Continue a conversation


Your application owns history. Resend relevant instructions, prior messages and the new user message:

```json
{
  "model": "MODEL_ID",
  "messages": [
    {
      "role": "system",
      "content": "Answer briefly."
    },
    {
      "role": "user",
      "content": "Hello"
    },
    {
      "role": "assistant",
      "content": "Hello."
    },
    {
      "role": "user",
      "content": "What can you help me with?"
    }
  ]
}
```


A response ID is not a conversation handle for this endpoint. Keep history within the model context budget; never separate a tool call from its matching result.

## Chat Completions streaming


Set stream=true. Success uses Content-Type: text/event-stream. Parse complete SSE events separated by blank lines. A network chunk can contain part of an event or several events; it is not itself JSON. Use an SSE-aware SDK rather than immediately parsing each network chunk.

```json
{
  "model": "MODEL_ID",
  "messages": [
    {
      "role": "user",
      "content": "Hello"
    }
  ],
  "stream": true,
  "stream_options": {
    "include_usage": true
  }
}
```


### Illustrative events


```
data: {"id":"chatcmpl_example","object":"chat.completion.chunk","model":"MODEL_ID","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}

data: {"id":"chatcmpl_example","object":"chat.completion.chunk","model":"MODEL_ID","choices":[{"index":0,"delta":{"content":"Hello."},"finish_reason":null}]}

data: {"id":"chatcmpl_example","object":"chat.completion.chunk","model":"MODEL_ID","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl_example","object":"chat.completion.chunk","model":"MODEL_ID","choices":[],"usage":{"prompt_tokens":10,"completion_tokens":3,"total_tokens":13}}

data: [DONE]
```

1. If the event name is error or the payload has an error, stop and report failure.
2. If data is [DONE], mark transport completion.
3. Otherwise parse JSON and record usage when present; it can be null or absent.
4. Iterate choices safely, including an empty array. Append delta.content only when it is text. Role-only events and empty deltas are normal.
5. Accumulate tool fragments by choice index and tool-call index. Preserve ID and type, concatenate function name and arguments, then parse arguments only after the call finishes.
6. Record finish_reason separately. tool_calls requires continuation; length can mean truncation.


A consumption event with choices=[] is valid and has no text choice. Errors can occur after HTTP 200:

```
event: error
data: {"error":{"message":"Request interrupted"}}
```


For a raw SSE client, EOF before [DONE] is incomplete. Do not present partial output as confirmed success or execute actions based on an incomplete call.

### Python SDK consumer


After creating client and model as in the SDK section, use:

```python
stream = client.chat.completions.create(
    model=model,
    messages=[{"role": "user", "content": "Hello"}],
    stream=True,
    stream_options={"include_usage": True},
)
finish_reason = None
usage = None
try:
    for chunk in stream:
        if chunk.usage is not None:
            usage = chunk.usage
        for choice in chunk.choices:
            if choice.delta.content:
                print(choice.delta.content, end="", flush=True)
            if choice.finish_reason:
                finish_reason = choice.finish_reason
finally:
    stream.close()
if finish_reason is None:
    raise RuntimeError("Stream ended without a completion reason")
print("\nFinish reason:", finish_reason)
print("Usage:", usage if usage is not None else "not reported")
```


This consumer is text-only; add the tool-fragment logic above for tools. SDKs hide raw framing, so handle their exceptions and completion metadata. A raw consumer must also check the terminal marker.

## Responses: input, output and events


POST /v1/responses supports available OpenAI and ChatGPT models. Use Chat Completions for DeepSeek. Responses has its own input, tool and event structures; do not reuse Chat Completions JSON unchanged.

```json
{
  "model": "MODEL_ID",
  "instructions": "Answer in plain English.",
  "input": "Hello",
  "stream": false
}
```


### Example response


```
{
  "id": "resp_example",
  "object": "response",
  "created_at": 1791244800,
  "status": "completed",
  "model": "MODEL_ID",
  "output": [
    {
      "id": "msg_example",
      "type": "message",
      "role": "assistant",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "text": "Hello.",
          "annotations": []
        }
      ]
    }
  ],
  "error": null,
  "usage": null
}
```


Check status and error. Iterate output: for type=message, join text from content entries with type=output_text; for type=function_call or custom_tool_call, handle the tool; reasoning items come first. ChatGPT connections report usage on every successful response; other providers can omit it. SDK output_text is a convenience; raw clients read output items.

### Context and continuation


ChatGPT connections store a response only when the request sets store: true. Then continue with previous_response_id and send only the new input; otherwise resend the instructions and every input item on each request. instructions and tools are not carried over by previous_response_id, so send them on every request. conversation is not available. Keep all output items, including reasoning items. With include: ["reasoning.encrypted_content"] the gateway returns encrypted reasoning; send those items back unchanged so a rebuilt context keeps the model's reasoning. To shorten a long window, call POST /v1/responses/compact and start the next input with its output, or set context_management so the native engine compacts automatically. See [ChatGPT connection features](https://crewblend.com/docs/#chatgpt-parity).

```json
{
  "model": "MODEL_ID",
  "instructions": "Answer in plain English.",
  "input": [
    {
      "role": "user",
      "content": "Hello"
    },
    {
      "role": "assistant",
      "content": "Hello."
    },
    {
      "role": "user",
      "content": "What can you help me with?"
    }
  ]
}
```


### Flat tool definitions


```json
{
  "model": "MODEL_ID",
  "input": "What is the weather in London?",
  "tools": [
    {
      "type": "function",
      "name": "get_weather",
      "description": "Get weather for a city",
      "parameters": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string"
          }
        },
        "required": [
          "city"
        ]
      }
    }
  ],
  "tool_choice": "auto",
  "parallel_tool_calls": false
}
```


A returned function_call has call_id, name and arguments. Parse and validate the argument JSON string, execute the approved function, and continue with the original input, the function_call and its matching function_call_output. Resend tool definitions and any required instructions. Preserve the returned call_id:

```json
{
  "model": "MODEL_ID",
  "input": [
    {
      "role": "user",
      "content": "What is the weather in London?"
    },
    {
      "type": "function_call",
      "call_id": "call_example",
      "name": "get_weather",
      "arguments": "{\"city\":\"London\"}"
    },
    {
      "type": "function_call_output",
      "call_id": "call_example",
      "output": "{\"temperature_c\":18,\"condition\":\"clear\"}"
    }
  ],
  "tools": [
    {
      "type": "function",
      "name": "get_weather",
      "description": "Get weather for a city",
      "parameters": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string"
          }
        },
        "required": [
          "city"
        ]
      }
    }
  ],
  "tool_choice": "auto",
  "parallel_tool_calls": false
}
```


### Streaming event handling


With stream=true, consume named events. ChatGPT connections use the following events; direct OpenAI can supply additional provider events. Unknown events must not crash your consumer.
- Event | Action
- response.created / response.in_progress | Initialize state.
- response.output_item.added | Create output item at output_index.
- response.output_text.delta | Append delta to the identified text item.
- response.function_call_arguments.delta | Accumulate arguments for the identified call.
- response.function_call_arguments.done / response.output_item.done | Finalize that item, not the entire response.
- response.completed | Read final response and usage; mark success.
- error / response.failed / response.incomplete | Report failure or incomplete output.


```
event: response.output_text.delta
data: {"type":"response.output_text.delta","item_id":"msg_example","output_index":0,"content_index":0,"delta":"Hello."}

event: response.completed
data: {"type":"response.completed","response":{"id":"resp_example","status":"completed","output":[{"type":"message","role":"assistant","content":[{"type":"output_text","text":"Hello.","annotations":[]}]}],"usage":null}}
```


Responses ends with response.completed, not a Chat Completions [DONE] marker. EOF before a terminal event is incomplete. An error can arrive after HTTP 200; the initial status does not establish success.

## Chat Completions: complete tool cycle


Your application defines and executes functions. Crew Blend AI returns model requests; it does not execute your functions. Tools require a request/response loop.

### 1. Define a tool


```json
{
  "model": "MODEL_ID",
  "messages": [
    {
      "role": "user",
      "content": "What is the weather in London?"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get weather for a city",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string"
            }
          },
          "required": [
            "city"
          ]
        }
      }
    }
  ],
  "tool_choice": "auto",
  "parallel_tool_calls": false
}
```


### 2. Inspect the tool request


```
{
  "id": "chatcmpl_example",
  "object": "chat.completion",
  "model": "MODEL_ID",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_example",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\":\"London\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ]
}
```


auto may produce text instead of a call. Handle both. For calls, allow only approved function names, parse JSON arguments and validate against your schema. Never evaluate arguments as code. Tool output is data, not a new system instruction.

### 3. Return the result


The weather result below is illustrative. Replace it with your function result. Include the assistant call message and one tool result per call before the next ordinary message.

```json
{
  "model": "MODEL_ID",
  "messages": [
    {
      "role": "user",
      "content": "What is the weather in London?"
    },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_example",
          "type": "function",
          "function": {
            "name": "get_weather",
            "arguments": "{\"city\":\"London\"}"
          }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "call_example",
      "content": "{\"temperature_c\":18,\"condition\":\"clear\"}"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get weather for a city",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string"
            }
          },
          "required": [
            "city"
          ]
        }
      }
    }
  ],
  "tool_choice": "auto",
  "parallel_tool_calls": false
}
```


Preserve call IDs. Return results for every call. If execution fails, return explicit error text for that call instead of omitting its result.

### 4. Continue with a bounded loop


Repeat for further calls, with a fixed maximum number of rounds, such as five. Obtain approval for destructive or consequential actions. Track executed call IDs locally so repeated events or retries do not execute an action twice. This does not make inference requests idempotent.

## Parameters and connection capabilities


Start with the minimum request and add only options supported by its connection. Accepted parameters do not necessarily change generation.
- Option | ChatGPT connections | Direct provider APIs
- model | Exact returned ID; prefer chatgpt/ prefix. | Exact returned ID; prefer provider prefix.
- stream | Boolean, default false. | Boolean, default false.
- stream_options.include_usage | Chat Completions usage chunk, present on every successful response. | Use with stream=true.
- tools | Function tools with object JSON schemas; in Responses also custom tools, namespaces and additional_tools items. | Provider/model dependent.
- tool_choice | auto or none; no forced selection. | Forced-selection support depends on the model.
- parallel_tool_calls | Default true returns every call the engine issues together; false returns them one per step. | ChatGPT models in code mode run client tools one at a time.
- reasoning_effort / reasoning.effort | Chat Completions / Responses respectively. Select a returned reasoning_efforts value. | Provider/model dependent.
- temperature / top_p | Valid values are accepted but not applied. Omit, or use exported client defaults. | Use only if supported by the model.
- max_tokens / max_completion_tokens | Positive values are accepted but not enforced as account output limits. Omit; they are not hard output or cost caps. | Provider/model support and gateway validation apply.
- max_output_tokens | Responses output-budget form; not a hard account output cap. | OpenAI Responses model dependent.
- response_format / text.format | Chat Completions / Responses structured formats below. | Provider/model dependent.
- n / logprobs / presence_penalty / frequency_penalty / store | n must be 1. The others are accepted and reported as ignored; store: true stores Responses requests. | Provider/model dependent.
- Images, audio, attachments | Use text and function tools. | This guide does not define media endpoints. Verify separate provider/client support.


ChatGPT responses can include x-gateway-ignored-parameters identifying sampling or output options that were accepted but not applied. This is a response header, not an enforceable cap setting. If effort metadata is absent, omit the option rather than guessing levels. An option accepted by another OpenAI-compatible service can still return 400 here.

### Structured JSON output


Chat Completions uses response_format. Parse and validate returned text against your schema; tool parameters are a separate schema.

```json
{
  "model": "MODEL_ID",
  "messages": [
    {
      "role": "user",
      "content": "Hello"
    }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "answer",
      "schema": {
        "type": "object",
        "properties": {
          "answer": {
            "type": "string"
          }
        },
        "required": [
          "answer"
        ],
        "additionalProperties": false
      }
    }
  }
}
```


Responses uses text.format, with name and schema directly in that format:

```json
{
  "model": "MODEL_ID",
  "input": "Say hello as JSON.",
  "text": {
    "format": {
      "type": "json_schema",
      "name": "answer",
      "schema": {
        "type": "object",
        "properties": {
          "answer": {
            "type": "string"
          }
        },
        "required": [
          "answer"
        ],
        "additionalProperties": false
      }
    }
  }
}
```


General JSON-object mode uses response_format={"type":"json_object"} in Chat Completions or text.format={"type":"json_object"} in Responses. Explain the desired structure in instructions and validate output.

### Size, context and timing


Inference request bodies must be JSON and no larger than 16 MiB; other endpoints accept 2 MiB. ChatGPT connections accept up to 4000 messages and 256 tool definitions. The model context budget may be smaller than the transport limit. The examples use a 150-second client timeout as a starting setting, not a service guarantee. Keep ChatGPT requests serial per workspace and handle 409 when another operation is active.

## Errors, timeouts and retries


Check HTTP status before interpreting non-streaming success data. A typical error envelope is:

```
{
  "error": {
    "message": "Invalid API key",
    "type": "authentication_error",
    "param": null,
    "code": null
  }
}
```


Message and type vary; code and param may be null. Use HTTP status as the primary category and code for specific handling; the ChatGPT codes are listed in [ChatGPT connection features](https://crewblend.com/docs/#chatgpt-parity). Do not depend on one exact English message.
- Status | Handling
- 400 | Fix JSON, options, roles or model format; do not retry unchanged.
- 401 | Fix credentials; do not retry unchanged.
- 402 | Check service fee and credit requirements.
- 403 | Check API entitlement and key permissions.
- 404 | Check path and refresh inventory; do not invent a fallback.
- 409 | Wait for the active operation and serialize requests.
- 413 / 415 | Reduce body size / send Content-Type: application/json.
- 422 | Fix provider/model validation issues.
- 429 | Respect Retry-After and check usage windows. ChatGPT usage limits always include Retry-After; it is an estimate when resets_at is null.
- 502 / 503 | Check connection availability; delayed retry may be appropriate.
- 400 model_not_available | None of your connected ChatGPT accounts currently offers the model. Choose a model listed by GET /v1/models or connect an account whose plan includes it.
- 504 | gateway_timeout: the step exceeded the gateway time limit. Retry the same request; the gateway charges no fee for it.


When a retry is appropriate and no useful output was received, bound the total attempts, increase delays and add jitter. For example, allow at most three total attempts with waits around one and two seconds between them, unless Retry-After requires longer. Do not retry permanent access or validation errors.

A timeout, disconnected stream or lost response has uncertain outcome: the original request may have been processed. Do not automatically repeat consequential tool actions or assume Idempotency-Key deduplicates inference. This API does not document inference idempotency. Use application action tracking and confirm outcomes when necessary.

SDKs can retry automatically. Examples set max_retries or maxRetries to zero so behavior is explicit. Streaming errors can follow HTTP 200; check error events, terminal states and missing completion markers.

## Verify your integration


Use authorized test credentials without publishing secrets. A build or HTTP 200 alone is insufficient.
1. Discovery: valid access returns object=list and data; the selected ID exists. Empty inventory produces an actionable setup message.
2. Text: an explicit detected model returns an assistant response; null content, tool calls and absent usage are handled.
3. Context: a second request resends history and receives a relevant continuation.
4. Streaming, if used: role-only events, empty choices and absent telemetry do not crash the consumer. Terminal events are recognized; early EOF or errors produce incomplete/failure state.
5. Tools, if used: local fixtures cover fragmented arguments, multiple calls, errors and continuation JSON. Verify a real round with an approved test function. auto does not guarantee a call.
6. Responses, if used: verify output parsing, flat tool definitions, matching call_id values and named events.
7. Errors: an invalid key is rejected; a models-only key cannot infer; invalid requests produce handled errors.
8. Secrets: keys do not appear in public bundles, commits, support messages or logs.


Report exactly what was verified: protocol fixtures, simulated providers or a real connected account. An AI without valid access must identify the remaining live check rather than inventing a successful production request.

Integrations

## Choose a client


In API keys, expand OpenCode and VS Code setup. Use Load detected model configuration to generate configuration from your current connections. Choose a client, then copy or download its configuration.

Refresh your accounts and re-download the configuration after adding or changing models. Keep existing client settings when merging the Crew Blend AI configuration.

## OpenCode

1. Choose Download OpenCode setup in API keys.
2. Save the file as `opencode.json` in `%USERPROFILE%/.config/opencode/` on Windows, or `~/.config/opencode/` on macOS and Linux.
3. If you already have configuration, merge `provider.crewblend` and the selected `model`.
4. Run `/connect`, choose the custom OpenAI-compatible provider option and use provider ID `crewblend`.
5. Enter your Crew Blend AI key, restart the client and send a message.


The downloaded setup includes detected models and their effort options. Choose another detected model or effort level through the client's model controls.

## Continue in VS Code


Select Continue YAML in the configuration export. Merge its model entries into your Continue configuration and define the `CREWBLEND_API_KEY` secret. The export sets the Crew Blend AI base URL, detected model IDs and Chat Completions options.

## Cline and Roo Code


In your extension's provider settings, choose OpenAI Compatible. Set the base URL to `https://api.crewblend.com/v1`, enter your Crew Blend AI key and select a model ID from the detected list.

For ChatGPT models, use native function calling, streaming, temperature 1 and top_p 1. Disable image and attachment options for these connections. Use the dashboard's connection guide for the settings generated for your workspace.

## VS Code Chat


Download VS Code / Copilot JSON from the configuration export. In VS Code, use Chat: Manage Language Models and add a custom endpoint. Choose Chat Completions and store your Crew Blend AI key through the client's secret prompt.

Merge the generated model entries into `chatLanguageModels.json`, keeping the secret input reference created by your client. Select a detected model to use it in compatible chat and agent workflows.

## Codex CLI


Codex CLI works with ChatGPT connections through the Responses endpoint. Its tool definitions arrive in an additional_tools input item with namespaces and custom (free-form) tools such as exec; calls are returned as function_call or custom_tool_call items with the original namespace and name, and their outputs are accepted as text or text parts.

```toml
model = "chatgpt/gpt-6.1-sol"
model_provider = "crewblend"

[model_providers.crewblend]
name = "Crew Blend AI"
base_url = "https://api.crewblend.com/v1"
env_key = "CREWBLEND_API_KEY"
wire_api = "responses"
```


Set CREWBLEND_API_KEY to a Crew Blend AI key with Models and requests permission. Codex sends store: false and its full history, and requests encrypted reasoning, so continuity works without stored responses. Hosted tools such as web_search are not available and are reported in x-gateway-ignored-parameters. Commands that Codex runs are executed by Codex on your computer under its own sandbox and approval settings.

## Executable Python and JavaScript


Select an exact inventory ID and store it as CREWBLEND_MODEL. These examples do not choose automatically. Save the code as client.py or client.mjs; keep secrets outside source control.

SDK references: [OpenAI Python SDK](https://github.com/openai/openai-python) and [OpenAI JavaScript SDK](https://github.com/openai/openai-node). These libraries provide transport and parsing; Crew Blend AI connection capabilities are defined in this guide.

### Install


```
python -m pip install openai
# For JavaScript, in your application directory:
npm install openai
```


### Environment: macOS, Linux or Git Bash


```
export CREWBLEND_API_KEY="YOUR_CREWBLEND_API_KEY"
export CREWBLEND_MODEL="EXACT_ID_FROM_YOUR_INVENTORY"
```


### Environment: PowerShell


```
$env:CREWBLEND_API_KEY = "YOUR_CREWBLEND_API_KEY"
$env:CREWBLEND_MODEL = "EXACT_ID_FROM_YOUR_INVENTORY"
```


### Python


```python
import os
from openai import OpenAI, APIError

key = os.environ["CREWBLEND_API_KEY"]
model = os.environ["CREWBLEND_MODEL"]
client = OpenAI(
    api_key=key,
    base_url="https://api.crewblend.com/v1",
    timeout=150.0,
    max_retries=0,
)
try:
    available = {item.id for item in client.models.list().data}
    if model not in available:
        raise ValueError("Configured model is not in your inventory")
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": "Hello"}],
    )
    for choice in response.choices:
        if choice.message.tool_calls:
            raise RuntimeError("Handle tool calls before continuing")
        print(choice.message.content or "")
        print("Finish reason:", choice.finish_reason)
    print("Usage:", response.usage if response.usage is not None else "not reported")
except APIError as error:
    raise SystemExit(f"API request failed (HTTP {getattr(error, 'status_code', None)})")

```


### JavaScript


```javascript
import OpenAI from "openai";

const key = process.env.CREWBLEND_API_KEY;
const model = process.env.CREWBLEND_MODEL;
if (!key || !model) throw new Error("Set CREWBLEND_API_KEY and CREWBLEND_MODEL");
const client = new OpenAI({
  apiKey: key,
  baseURL: "https://api.crewblend.com/v1",
  timeout: 150000,
  maxRetries: 0,
});
try {
  const inventory = await client.models.list();
  if (!inventory.data.some(item => item.id === model)) {
    throw new Error("Configured model is not in your inventory");
  }
  const response = await client.chat.completions.create({
    model,
    messages: [{ role: "user", content: "Hello" }],
  });
  for (const choice of response.choices) {
    if (choice.message.tool_calls?.length) throw new Error("Handle tool calls before continuing");
    console.log(choice.message.content ?? "");
    console.log("Finish reason:", choice.finish_reason);
  }
  console.log("Usage:", response.usage ?? "not reported");
} catch (error) {
  console.error("Request failed:", error.status ?? "client/transport error");
  process.exitCode = 1;
}

```


### Run


```
python client.py
node client.mjs
```


Expect a model response, a finish reason, and reported usage or not reported. Do not demand one exact output phrase. Resolve access failures in the dashboard instead of substituting credential types or inventing models.

## ChatGPT connection features


This section describes what applies when the selected model is served by one of your connected ChatGPT accounts. Direct OpenAI and DeepSeek connections keep their provider behavior.
- Feature | ChatGPT connections
- Chat Completions and Responses, JSON and streaming | Available
- Function tools, custom tools and tool namespaces | Available; see code mode below
- Encrypted reasoning (include: ["reasoning.encrypted_content"]) | Available
- store: true, previous_response_id, GET and DELETE /v1/responses/{id} | Available; 30-day encrypted retention
- POST /v1/responses/compact and context_management | Available
- Usage with cached and reasoning tokens | On every successful response
- text.verbosity, reasoning effort and summaries | Applied
- conversation, reusable prompts, background: true | Not available (400)
- Hosted tools (web_search, file_search, image generation) | Not available; reported as ignored


### Stored responses


With store: true the gateway stores the request input, the response and the model's encrypted reasoning, encrypted at rest, for 30 days. Without it nothing is stored as a response; store defaults to false on ChatGPT connections. previous_response_id rebuilds the stored conversation, including that reasoning, so later steps keep it even without include. GET /v1/responses/{id} returns the stored response, GET /v1/responses/{id}/input_items its input and DELETE /v1/responses/{id} removes it. Stored responses belong to your account and are readable by all of its API keys. An unknown, expired or deleted id returns 400 previous_response_not_found, or 404 on GET and DELETE. Deleting a response breaks continuation from later responses in the same chain. 

First request, stored:

```
{
  "model": "chatgpt/gpt-6.1-sol",
  "instructions": "Answer briefly.",
  "input": "Remember the code word ORION.",
  "store": true
}
```


Continuation with only the new input; instructions and tools are sent again:

```
{
  "model": "chatgpt/gpt-6.1-sol",
  "instructions": "Answer briefly.",
  "previous_response_id": "resp_...",
  "input": "What was the code word?",
  "store": true
}
```


Read or delete it with GET /v1/responses/resp_... and DELETE /v1/responses/resp_..., using the same API key header as other requests.

### Encrypted reasoning


Send include: ["reasoning.encrypted_content"] with store=false. Responses output then starts with reasoning items that carry encrypted_content, before the message and function_call items of the same step. Return those items unchanged in the next input. When the native context is still active the gateway continues it directly; when it must be rebuilt, the gateway restores the reasoning by item ID, so the model keeps its reasoning across tool calls. Encrypted reasoning only verifies on the connected account that produced it. Items from another account are dropped, and x-gateway-reasoning-replay reports accepted and dropped counts. 

### Compaction


POST /v1/responses/compact accepts model, instructions, input and tools and returns object response.compaction with one compaction item. Start the next /v1/responses input with that item, followed by new items, and send the same instructions and tools. The compacted native context is kept for 24 hours after it is created or last used to start a context, or 30 minutes with prompt_cache_retention=in_memory. After that the item returns 400 compaction_unavailable, and so does an item from another account or with different tools. Alternatively, context_management: [{"type": "compaction", "compact_threshold": 200000}] lets the native engine compact once the context passes the threshold. Both compaction paths consume usage on the connected ChatGPT account. 

### Context retention


Requests through ChatGPT connections keep their native conversation context for 24 hours after the last successful request, or 30 minutes with prompt_cache_retention=in_memory. This is gateway context retention, not a provider token-cache lifetime; x-gateway-cache-ttl reports it and x-gateway-provider-cache-ttl stays unknown. Matching requests continue the existing context; otherwise it is rebuilt from the input you send. Compactions follow the same retention. Stored responses (store: true) are retained separately for 30 days. 

### Usage


usage is reported on every successful response, including tool-call steps and streams with stream_options.include_usage. It contains total_tokens, prompt_tokens_details.cached_tokens and completion_tokens_details.reasoning_tokens; in Responses, input_tokens_details and output_tokens_details. Values come from each upstream completion. A step that needed no model request, such as a queued tool call, reports zero. On non-streaming responses, x-gateway-usage-scope=native_responses marks exact per-response counts. native_since_last_report marks a continuation that also includes an earlier step the engine reported late. usage is null only when the engine has reported nothing yet; that amount is included in the next response. 

### Code mode (tool_mode: code_mode_only)


GET /v1/models reports tool_mode for each model. With code_mode_only, which applies to GPT-6.1 Sol and the other GPT-6 and GPT-5.6 models on ChatGPT accounts, the model does not call your function tools directly. It writes a short program in the engine's internal code tool, and that program calls your tools. For your client the protocol stays the same: each call arrives as a function_call (or custom_tool_call) with JSON arguments that follow your schema, and you answer with function_call_output. What changes:
- Calls are serialized. The engine runs one client tool at a time, so parallel_tool_calls has no effect; supports.parallel_tool_dispatch reports serialized_by_engine.
- call_id values are generated by the engine (for example exec-...). Treat them as opaque and return them unchanged.
- One program can call several tools in a row without a new model response. Those steps report zero usage because no model request ran.
- The program reads your tool output as text. Return concise results; large outputs consume context. Image parts in tool output are not passed to the model and are reported as ignored.
- Descriptions and JSON Schemas are the only documentation the program has for your tools. Describe each parameter and keep schemas to JSON Schema objects.
- tool_choice supports auto and none; forced tool calls are not available.
- Tools in reserved namespaces (such as functions or web) or named mcp__... get an internal alias. Your client always receives its own namespace and name.
 

### Parallel tool calls


parallel_tool_calls defaults to true. Calls that the engine issues together are returned in one step and must all be answered in the next request, in any order. With false they are returned one per step. GPT-6 and GPT-5.6 models on ChatGPT accounts use code mode, where the engine runs client tools one at a time; supports.parallel_tool_dispatch reports serialized_by_engine for those models. 

### Agent mode


x-gateway-mode: agent adds task instructions to the native context: complete the requested task with the supplied tools, treat a successful tool result as progress rather than an interruption, verify results before claiming completion, and report the actual blocker instead of inventing cancellations or asking for a new user turn. Agent mode keeps a context separate from default mode. Use it for autonomous multi-step agents. Keep default mode for chat interfaces where the model should stop and ask the user. 

### Model catalog


context_window and reasoning levels come from the OpenAI catalog of each connected account; catalog_fetched_at records when it was read. They can differ between accounts on different ChatGPT plans and change without notice. Read GET /v1/models at the start of a session rather than hard-coding limits. Each connected account lists the models its ChatGPT plan offers. If OpenAI rejects a model for one account, for example after a plan change, only that account stops listing it: the request is served by another of your connected accounts that offers the model, or returns 400 model_not_available when none does. Notable catalog changes are noted in the [changelog](https://crewblend.com/changelog/). 

### Parameters that do not apply


Valid OpenAI parameters without a ChatGPT-account control are accepted, not applied and listed in x-gateway-ignored-parameters. Examples: client_metadata, service_tier, stream_options, truncation, temperature, top_p, store: true on Chat Completions, reasoning.context, include values other than reasoning.encrypted_content, hosted tools such as web_search, and image parts in input. text.verbosity is applied. Parameters that would change the result if ignored return 400: conversation, prompt and background: true. Unknown parameters return 400, as in the OpenAI API. Inference requests may be up to 16 MiB. 

### Errors and retries


Usage limits return 429 with code usage_threshold or usage_window_exhausted, resets_at and Retry-After. When OpenAI reports no reset time, resets_at is null and Retry-After is a 60-second estimate. Steps have no gateway time limit by default; a step that exceeds a configured limit returns 504 gateway_timeout with Retry-After: 1; retry the same request. The interrupted step is discarded and the gateway charges no fee, but tokens already processed count against the ChatGPT account. An error after a stream has started cannot change the HTTP status: Chat Completions sends an error event and Responses sends response.failed with the same code. Streams open as soon as the request is dispatched and stay active during long reasoning: after 15 seconds without output, Responses streams repeat response.in_progress and Chat Completions streams send an SSE comment, so proxies and client idle timeouts do not close the connection. Use stream: true for long reasoning; a non-streaming request sends nothing until it finishes. 

### Error codes

- Status and code | Meaning and action
- 400 model_not_available | No connected account currently offers the model. Choose a model from GET /v1/models.
- 400 no_account_for_model | No connected account lists the model. Connect an account or choose another model.
- 400 previous_response_not_found | The stored response is unknown, expired, deleted or belongs to another account. Resend the full input.
- 400 compaction_unavailable | The compaction item expired, belongs to another account or was created with other tools. Compact again.
- 400 unsupported_reasoning_effort | Use a value from the model reasoning_efforts.
- 409 operation_in_progress | Another request of the same account is running. Retry after Retry-After, or send x-gateway-wait.
- 429 usage_threshold / usage_window_exhausted | Usage limit. Wait for Retry-After; resets_at gives the reset time when known.
- 503 usage_unavailable | Account usage could not be read. Retry after Retry-After or refresh the account.
- 504 gateway_timeout | The step exceeded the gateway time limit. Retry the same request.
 

### Gateway headers

- Header | Use
- x-gateway-wait (request) | Wait 0 to 30 seconds for a busy account instead of receiving 409.
- x-gateway-mode: agent (request) | Task-oriented instructions for autonomous agents; see Agent mode.
- x-request-id | Identifier to quote in support requests.
- x-gateway-ignored-parameters | Valid parameters that were accepted but not applied.
- x-gateway-usage-scope | Source of the reported usage on non-streaming responses.
- x-gateway-reasoning-replay | Encrypted reasoning items accepted and dropped in this request.
- x-gateway-context-reused | Whether an existing native context was continued.
- x-gateway-reasoning-effort | Requested and applied reasoning effort.
- Retry-After | Seconds to wait before retrying 409, 429, 503 and 504.
 

### Not available


conversation, reusable prompts and background responses are not available for ChatGPT connections. prompt_cache_retention controls gateway context retention only; the native engine does not send it to the provider. max_output_tokens has no native control and is reported in x-gateway-ignored-parameters.
