## Function calling (tools)

Describe the functions the model may call, and it decides on its own when a call is needed. The model does not run the function — it returns the function name and arguments, and your code performs the call. You send the result back into the conversation, and the model composes the final answer.

When the model decides to call a function, `finish_reason` is `tool_calls` and `content` is `null`. The arguments arrive in `tool_calls[].function.arguments` as a JSON string.

## Function description

```bash
curl -X POST https://vibecode.bitrix24.com/v1/chat/completions \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bitrix/bitrixgpt-5.5",
    "messages": [
      {"role": "user", "content": "What is the weather in Berlin?"}
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "get_weather",
          "description": "Get the current weather in the specified city",
          "parameters": {
            "type": "object",
            "properties": {
              "city": {"type": "string", "description": "City name"}
            },
            "required": ["city"]
          }
        }
      }
    ],
    "tool_choice": "auto"
  }'
```

## Response example

```json
{
  "id": "chatcmpl-9aaee7576d8057ab",
  "object": "chat.completion",
  "model": "bitrix/bitrixgpt-5.5",
  "choices": [
    {
      "index": 0,
      "finish_reason": "tool_calls",
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "chatcmpl-tool-a423212c4e614cab",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\": \"Berlin\"}"
            }
          }
        ]
      }
    }
  ],
  "usage": {"prompt_tokens": 275, "completion_tokens": 26, "total_tokens": 301}
}
```

## Returning the result to the conversation

Run the function on your side and add a message to `messages` with the `tool` role and the same `tool_call_id`:

```json
{
  "messages": [
    {"role": "user", "content": "What is the weather in Berlin?"},
    {"role": "assistant", "content": null, "reasoning_content": "The question is about the weather in a city — calling get_weather with city=Berlin.", "tool_calls": [{"id": "chatcmpl-tool-a423212c4e614cab", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\": \"Berlin\"}"}}]},
    {"role": "tool", "tool_call_id": "chatcmpl-tool-a423212c4e614cab", "content": "+5°C, cloudy"}
  ]
}
```

In its next response the model composes human-readable text based on the returned result.

A reasoning model reads the reasoning of every earlier assistant turn in such a conversation, so send it back in the `reasoning_content` field next to the `tool_calls` of the same message — otherwise the model loses the thread between calls. The acceptance rules for the field and its character limit — [Create a chat completion](./completions.md).

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 400 | `unsupported_tool_type` | The `tools` array contains an element with a `type` other than `function`. For web search use [`POST /v1/search`](/docs/search/run) |
| 400 | `tool_choice_without_tools` | `tool_choice` was passed, but the `tools` array is absent |

The full list of common API errors — [Errors](/docs/errors).

## Known specifics

**Only `type: "function"` is supported.** The endpoint has no built-in tools such as web search. A request with a different `type` value is rejected with `400` rather than silently executed without tools.

**Names in synchronous responses are checked against your list.** If the model invents a function name that is not in `tools`, that call is removed from the response. When all calls turn out to be invented, `finish_reason` switches to `stop` and a textual explanation arrives in `content` — the conversation does not loop on a nonexistent function.

The name filter does not apply to streaming responses: check each call name against your own `tools` list before execution. If a call was discarded before reaching the platform, an empty turn or a call finish without a call carries a textual explanation. Synchronous responses also carry the `TOOL_CALL_MISSING` warning; streams carry the explanation in `delta.content` before `finish_reason: "stop"`. Continue with the declared tools. This explanation is not added for `tool_choice: "none"` or structured output.

**Arguments arrive as a string.** The `arguments` field is a JSON string, and you must parse it before use. The model can return syntactically valid JSON that does not match your parameter schema, so validate the values before the call.

## See also

- [Create a chat completion](./completions.md)
- [Guaranteed JSON response](./json.md)
- [Web search](/docs/search/run)
- [List of models](/docs/ai/models/list)
