> ## Documentation Index
> Fetch the complete documentation index at: https://hub.hcompany.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> H Platform has four products: the Agents API (managed computer-use agents, base URL https://agp.eu.hcompany.ai/api/v2 or https://agp.hcompany.ai/api/v2 for the US), the Models API (OpenAI-compatible Holo vision-language models at https://api.hcompany.ai/v1), HoloDesktop CLI (Holo on the user's own desktop), and HoloTab (a free no-code Chrome extension that runs Holo in the user's browser, with recordable routines and schedules).
> Authenticate with a bearer API key from the HAI_API_KEY environment variable. SDKs: `pip install hai-agents` (Python, `from hai_agents import Client`) and `npm install hai-agents` (TypeScript, `import { HaiAgentsClient } from "hai-agents"`). CLI: `hai`.
> Agents do work in a browser or on a desktop; describe the task as an imperative instruction. To run a task quickly, prefer the pre-built agent `h/web-surfer-flash`. Read results from the session's `latest_answer` after it reaches a terminal status.
> Sessions are the unit of work; wait for a terminal status (completed, failed, timed_out, interrupted) before reading the answer. Use webhooks or the `changes` long-poll endpoint to follow progress.

# Create chat completion

> OpenAI-compatible chat completion with Holo-specific structured outputs and reasoning.

export const Notice = ({kind = "note", title, children}) => {
  const kinds = {
    warning: {
      label: "User notice",
      icon: <>
          <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
          <path d="M12 9v4" />
          <path d="M12 17h.01" />
        </>
    },
    gotcha: {
      label: "Gotcha",
      icon: <>
          <circle cx="12" cy="12" r="10" />
          <path d="M12 16v-4" />
          <path d="M12 8h.01" />
        </>
    },
    note: {
      label: "Note",
      icon: <>
          <circle cx="12" cy="12" r="10" />
          <path d="M12 16v-4" />
          <path d="M12 8h.01" />
        </>
    }
  };
  const k = kinds[kind];
  return <div className="notice my-6 rounded-xl border border-zinc-200 bg-white p-5 dark:border-zinc-800 dark:bg-zinc-950">
      <div className={`${kind === "warning" ? "not-prose flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-red-400/80 dark:text-red-400/70" : kind === "gotcha" ? "not-prose flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-amber-500/80 dark:text-amber-400/70" : "not-prose flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-zinc-400 dark:text-zinc-500"}`}>
        <svg className="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          {k.icon}
        </svg>
        {k.label}
      </div>
      {title && <div className="not-prose mt-2 text-base font-semibold text-zinc-900 dark:text-zinc-100">{title}</div>}
      <div className="notice-body mt-3 text-sm leading-6 text-zinc-700 dark:text-zinc-300">{children}</div>
    </div>;
};

The single inference endpoint. It is OpenAI-compatible: the official OpenAI clients work as-is with `base_url` pointed at `https://api.hcompany.ai/v1/`. Holo-specific behavior (structured outputs, the reasoning toggle) is controlled by extra body fields documented below.

**Returns** a chat completion object, or a stream of chunk objects when `stream` is `true`.

***

## Body parameters

<ParamField body="model" type="string" required>
  Model ID to run. One of the IDs listed on the [Models](/models-api/introduction#models) page, e.g. `holo4-27b`.
</ParamField>

<ParamField body="messages" type="array" required>
  The conversation so far. Standard OpenAI message objects (`role`, `content`); `content` can be a string or an array of `text` and `image_url` parts. Images accept HTTPS URLs or base64 data URIs (JPEG, PNG, WebP), up to 5 per request.
</ParamField>

<ParamField body="structured_outputs" type="object">
  Holo-specific. Constrain the response, at the decoding level, to a JSON object matching a schema: pass `{"json": <JSON Schema>}`. The object is returned in `message.content`. Use this for the [structured-output agent loop](/models-api/build-an-agent/core-concepts) and [element localization](/models-api/element-localization).

  <Notice kind="gotcha" title="Holo-specific fields with the OpenAI SDKs">
    With the OpenAI SDKs, pass this and `chat_template_kwargs` via `extra_body` in Python or an untyped spread in TypeScript. The SDK merges them into the request body. On the raw wire they are top-level fields, as in the cURL example. The API silently ignores a body nested under a literal `"extra_body"` key.
  </Notice>
</ParamField>

<ParamField body="chat_template_kwargs" type="object">
  Holo-specific. `{"enable_thinking": bool}` toggles the reasoning channel. Use `true` for agent loops (Holo plans before acting), `false` for single-shot calls like grounding and OCR.
</ParamField>

<ParamField body="reasoning_effort" type="string">
  How much the model plans before acting: `"low"`, `"medium"`, or `"high"`. `"medium"` is a sensible default for agent loops.
</ParamField>

<ParamField body="tools" type="array">
  OpenAI-style function declarations for [native function calling](/models-api/build-an-agent/function-calling). Supported by every model except `holo3-122b-a10b`. A model supports it when `tools` is in its `supported_features` in [`GET /v1/models`](/models-api/list-models). Set `tool_choice: "required"` so the model acts on every step, and do not mix with `structured_outputs`.
</ParamField>

<ParamField body="tool_choice" type="string">
  Standard OpenAI semantics. Use `"required"` in function-calling agent loops.
</ParamField>

<ParamField body="stream" type="boolean" default="false">
  Stream the response as server-sent chunk events. Reasoning tokens arrive in `delta.reasoning`, content in `delta.content`.
</ParamField>

<ParamField body="max_tokens" type="integer">
  Output cap for this request. The hard ceiling is the model's `max_output_length` in [`GET /v1/models`](/models-api/list-models), 8,192 tokens on the Holo3 models.
</ParamField>

<ParamField body="temperature" type="number">
  Sampling temperature. Use `0.0` for deterministic single-shot calls ([localization](/models-api/element-localization), [OCR](/models-api/document-ocr)); `0.8` works well in agent loops. Also supported: `top_p`, `top_k`, `stop`, `frequency_penalty`, `presence_penalty`, `seed`.
</ParamField>

***

## Response

<ResponseField name="choices[].message.content" type="string">
  The action or answer: the constrained JSON object (structured-output mode) or the assistant text. `null` when the model responded with `tool_calls` only.
</ResponseField>

<ResponseField name="choices[].message.reasoning" type="string">
  The thinking trace, present when thinking is enabled. Read it for visibility; never feed it back. Not carried between turns, see [Reasoning](/models-api/build-an-agent/core-concepts#reasoning).
</ResponseField>

<ResponseField name="choices[].message.tool_calls" type="array">
  Present in native function-calling mode only. Each call carries an `id` and a `function` object with `name` and a JSON-encoded `arguments` string.
</ResponseField>

<ResponseField name="choices[].finish_reason" type="string">
  `stop`, `length` (hit `max_tokens` or the model ceiling), or `tool_calls`.
</ResponseField>

<ResponseField name="usage" type="object">
  `prompt_tokens`, `completion_tokens`, `total_tokens` for the request. `prompt_tokens_details.cached_tokens` is the part of the prompt served from the cache, see [Prompt caching](#prompt-caching). `prompt_tokens_details` is `null` when nothing was.
</ResponseField>

***

## Prompt caching

When a prompt starts with the same tokens as a recent request to the same model, such as a fixed system prompt or the growing history of an agent loop, the server reuses its work on that part. There is nothing to enable. The reused part is reported as `usage.prompt_tokens_details.cached_tokens` and billed at the cached input price. The rest of the prompt and all output are billed at the listed rates. Each model's cached input price is on the [Models](/models-api/introduction#models) page and in `GET /v1/models` as `pricing.input_cache_read`.

Caching is best effort. Keep the stable part of the prompt at the start, and expect the count to vary between requests.

***

## Examples

<CodeGroup>
  ```python Python theme={"system"}
  import os
  from openai import OpenAI

  client = OpenAI(
      base_url="https://api.hcompany.ai/v1/",
      api_key=os.environ["HAI_API_KEY"],
  )

  resp = client.chat.completions.create(
      model="holo4-27b",
      messages=[{"role": "user", "content": "In one sentence, what is a computer-use agent?"}],
      reasoning_effort="medium",
      extra_body={"chat_template_kwargs": {"enable_thinking": True}},
  )

  print(resp.choices[0].message.content)
  ```

  ```typescript TypeScript theme={"system"}
  import OpenAI from "openai";

  const client = new OpenAI({
    baseURL: "https://api.hcompany.ai/v1/",
    apiKey: process.env.HAI_API_KEY,
  });

  const resp = await client.chat.completions.create({
    model: "holo4-27b",
    messages: [{ role: "user", content: "In one sentence, what is a computer-use agent?" }],
    reasoning_effort: "medium",
    ...({ chat_template_kwargs: { enable_thinking: true } } as any),
  });

  console.log(resp.choices[0].message.content);
  ```

  ```bash cURL theme={"system"}
  curl https://api.hcompany.ai/v1/chat/completions \
    -H "Authorization: Bearer $HAI_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "holo4-27b",
      "messages": [{"role": "user", "content": "In one sentence, what is a computer-use agent?"}],
      "reasoning_effort": "medium",
      "chat_template_kwargs": {"enable_thinking": true}
    }'
  ```
</CodeGroup>

### Streaming

<CodeGroup>
  ```python Python theme={"system"}
  stream = client.chat.completions.create(
      model="holo4-27b",
      messages=[{"role": "user", "content": "In one sentence, what is a computer-use agent?"}],
      stream=True,
  )

  for chunk in stream:
      delta = chunk.choices[0].delta
      if delta.content:
          print(delta.content, end="", flush=True)
  ```

  ```typescript TypeScript theme={"system"}
  const stream = await client.chat.completions.create({
    model: "holo4-27b",
    messages: [{ role: "user", content: "In one sentence, what is a computer-use agent?" }],
    stream: true,
  });

  for await (const chunk of stream) {
    const delta = chunk.choices[0]?.delta;
    if (delta?.content) process.stdout.write(delta.content);
  }
  ```
</CodeGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.