> ## 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.

# Get session changes

> Long-poll a session for new events, status, and the final answer since an event index, with the polling rules and the SDK wait helper.

export const ChangesLoop = () => {
  const stroke = {
    fill: "none",
    stroke: "currentColor",
    strokeWidth: 1.75,
    strokeLinecap: "round",
    strokeLinejoin: "round"
  };
  const S = c => ({
    className: c,
    ...stroke
  });
  const icons = {
    stream: c => <svg viewBox="0 0 24 24" {...S(c)}><path d="M4 6h16M4 12h16M4 18h10" /></svg>,
    pulse: c => <svg viewBox="0 0 24 24" {...S(c)}><path d="M22 12h-4l-3 9L9 3l-3 9H2" /></svg>
  };
  const Card = ({icon, title, sub, children}) => <div className="flex shrink-0 flex-col self-center rounded-xl border border-zinc-200 bg-white p-5 dark:border-zinc-800 dark:bg-zinc-950">
      <div className="flex items-center gap-2.5">
        <span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-200">{icon("h-5 w-5")}</span>
        <div>
          <div className="whitespace-nowrap font-mono text-[15px] font-semibold leading-6 text-zinc-900 dark:text-zinc-100">{title}</div>
          <div className="whitespace-nowrap text-sm text-zinc-500 dark:text-zinc-400">{sub}</div>
        </div>
      </div>
      {children}
    </div>;
  const Arrow = ({top}) => <div className="flex min-w-[90px] flex-1 flex-col items-stretch justify-center gap-1 px-3 text-center text-xs leading-4 text-zinc-500 dark:text-zinc-400">
      <span className="whitespace-nowrap">{top}</span>
      <div className="flex items-center text-zinc-400 dark:text-zinc-600">
        <span className="h-px flex-1 bg-current" />
        <svg className="-ml-px h-3 w-2 shrink-0" viewBox="0 0 8 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M1 1.5 6 6l-5 4.5" /></svg>
      </div>
    </div>;
  const Chip = ({children}) => <span className="whitespace-nowrap rounded-md bg-zinc-100 px-2 py-0.5 font-mono text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300">{children}</span>;
  const Line = ({chip, children}) => <div className="flex items-center gap-2 whitespace-nowrap"><Chip>{chip}</Chip>{children}</div>;
  return <div className="not-prose my-8 overflow-x-auto">
      <div className="flex min-w-[560px] flex-col pb-2">
        <div className="flex items-stretch">
          <Card icon={icons.stream} title="GET /changes" sub="long poll, up to 25s">
            <div className="mt-4 flex flex-col items-start gap-2 text-sm text-zinc-600 dark:text-zinc-400">
              <Line chip="200">new events, advance <span className="font-mono text-[13px]">from_index</span></Line>
              <Line chip="204">nothing new yet</Line>
            </div>
          </Card>

          <Arrow top="then check" />

          <Card icon={icons.pulse} title="GET /status" sub="the only source of truth">
            <div className="mt-4 flex flex-col items-start gap-2 text-sm text-zinc-600 dark:text-zinc-400">
              <Line chip="settled">drain, read the answer</Line>
              <Line chip="running">keep polling</Line>
            </div>
          </Card>
        </div>

        <div className="relative mx-20 h-7 rounded-b-xl border-b border-l border-r border-zinc-300 dark:border-zinc-700">
          <svg className="absolute -left-[6.5px] -top-1 h-2 w-3 text-zinc-400 dark:text-zinc-600" viewBox="0 0 12 8" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M1.5 7 6 2l4.5 5" /></svg>
          <div className="absolute -bottom-2 left-0 right-0 flex justify-center"><span className="whitespace-nowrap bg-white px-2 text-xs leading-4 text-zinc-500 dark:bg-zinc-950 dark:text-zinc-400">back to /changes</span></div>
        </div>
      </div>
    </div>;
};

Long-polls a session for what changed since an event index: new events, status, and the final answer. The server holds the request open until something new arrives or `wait_for_seconds` expires.

**Returns** `200` with the changes object [below](#response), or `204 No Content` if no new events arrive within the wait.

## Polling rules

* `changes` is a delta: each call returns only what is new since `from_index`. `204` means no new events yet, not that there is no answer.
* The `answer` rides the page that delivers the final events, and `status` can read `completed` before you reach that page. Keep advancing `from_index` until the session is [terminal](/agents-api/sessions/overview#lifecycle) *and* a poll returns no further events.
* To skip the loop for a finished run, read [`latest_answer`](/agents-api/sessions/retrieve#response), or use the [SDK helper](#long-polling-pattern).

## Path parameters

<ParamField path="id" type="string" required>
  The session ID.
</ParamField>

## Query parameters

<ParamField query="from_index" type="integer" default="0">
  Event index to start from. Pass the next unread index to resume where you left off.
</ParamField>

<ParamField query="limit" type="integer">
  Maximum number of events to return. Minimum `0`.
</ParamField>

<ParamField query="include_events" type="boolean" default="true">
  Whether to include event details in the response.
</ParamField>

<ParamField query="wait_for_seconds" type="integer" default="0">
  How long the server holds the request waiting for changes, `0` to `25` seconds. `0` returns immediately, and `20` to `25` gives efficient long polling.
</ParamField>

## Response

<ResponseField name="status" type="string">
  Current session [status](/agents-api/sessions/overview#lifecycle).
</ResponseField>

<ResponseField name="started_at" type="string | null">
  When the agent started executing.
</ResponseField>

<ResponseField name="finished_at" type="string | null">
  When the session reached a terminal state.
</ResponseField>

<ResponseField name="error" type="string | null">
  Short, stable error message if the session failed or timed out. Branch on `error_code`, not on this text.
</ResponseField>

<ResponseField name="error_code" type="string | null">
  Failure category when the session failed or timed out: `environment_error`, `no_answer`, `answer_validation`, `timeout`, `insufficient_credit`, or `internal`. See [Session failures](/agents-api/errors#session-failures).
</ResponseField>

<ResponseField name="outcome" type="string | null">
  The agent's self-assessed outcome, reported with its final answer: `success`, `partial`, `infeasible`, or `blocked`. See [Outcomes](/agents-api/errors#outcomes).
</ResponseField>

<ResponseField name="answer" type="string | object | null">
  The agent's final result once produced: a string, or an object matching the agent's `answer_format`. See [Polling rules](#polling-rules) for when it lands. Mirrored on the session's [`latest_answer`](/agents-api/sessions/retrieve#response).
</ResponseField>

<ResponseField name="metrics" type="object">
  Usage and cost so far: `steps`, `total_cost`, `input_cost`, `output_cost`, `reasoning_cost`, and `cost_per_model[]` (per-model `name`, `input_tokens`, `cached_tokens`, `output_tokens`, `reasoning_tokens`, and the four costs). Costs are in USD and `null` when a model's price is unavailable, so null-check before summing.
</ResponseField>

<ResponseField name="new_events" type="object[]">
  Events since `from_index`, each in the [event shape](/agents-api/sessions/events#event-shape) (`type`, `data`, `timestamp`).
</ResponseField>

## Examples

<CodeGroup>
  ```bash cURL theme={"system"}
  curl "https://agp.eu.hcompany.ai/api/v2/sessions/$SESSION_ID/changes?from_index=0&wait_for_seconds=25" \
    -H "Authorization: Bearer $HAI_API_KEY"
  ```

  ```python Python theme={"system"}
  from hai_agents import Client

  client = Client()

  changes = client.sessions.get_session_changes(
      session_id,
      from_index=0,
      wait_for_seconds=25,
  )
  ```

  ```typescript TypeScript theme={"system"}
  import { HaiAgentsClient } from "hai-agents";

  const client = new HaiAgentsClient();

  const changes = await client.sessions.getSessionChanges({
    id: sessionId,
    fromIndex: 0,
    waitForSeconds: 25,
  });
  ```
</CodeGroup>

```json Response theme={"system"}
{
  "status": "running",
  "started_at": "2026-05-07T14:30:02Z",
  "finished_at": null,
  "error": null,
  "error_code": null,
  "answer": null,
  "outcome": null,
  "metrics": {
    "steps": 2,
    "total_cost": 0.0093874,
    "input_cost": 0.0084664,
    "output_cost": 0.000921,
    "reasoning_cost": 0.0,
    "cost_per_model": [
      {
        "name": "holo4-27b",
        "input_tokens": 21166,
        "cached_tokens": 0,
        "output_tokens": 307,
        "reasoning_tokens": 0,
        "input_cost": 0.0084664,
        "output_cost": 0.000921,
        "reasoning_cost": 0.0,
        "total_cost": 0.0093874
      }
    ]
  },
  "new_events": [
    { "type": "AgentEvent", "data": { "...": "..." }, "timestamp": "2026-05-07T14:30:05Z" }
  ]
}
```

## Long-polling pattern

The SDK helper runs the loop for you: it ends on `status` (authoritative), streams events from `changes`, advances `from_index`, and handles `204` responses.

<ChangesLoop />

<CodeGroup>
  ```python Python theme={"system"}
  from hai_agents import wait_for_session

  result = wait_for_session(client, session_id, wait_for_seconds=25)

  for event in result.events:
      print(event.type)
  print(result.status, result.answer)

  # client.run_session(...) creates the session and runs this loop in one call.
  ```

  ```typescript TypeScript theme={"system"}
  import { waitForSession } from "hai-agents";

  const result = await waitForSession(client, { id: sessionId, waitForSeconds: 25 });

  for (const event of result.events) {
    console.log(event.type);
  }
  console.log(result.status, result.answer);

  // client.runSession(...) creates the session and runs this loop in one call.
  ```
</CodeGroup>


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