Get session changes
curl --request GET \
--url https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes \
--header 'Authorization: Bearer <token>'import requests
url = "https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodySessions
Get session changes
Long-poll for real-time session updates.
GET
/
api
/
v2
/
sessions
/
{id}
/
changes
Get session changes
curl --request GET \
--url https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes \
--header 'Authorization: Bearer <token>'import requests
url = "https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://agp.eu.hcompany.ai/api/v2/sessions/{id}/changes")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_bodyReturns a stream of changes (events, status transitions, agent actions) that have occurred since your last request. Uses long polling: the server holds the connection open until new changes are available or the timeout expires.
Returns
Long-poll
200 with a SessionChanges object, or 204 No Content if no new events arrive within the wait period.
changes is a delta: each call returns only what’s new since from_index, and 204 when nothing new has arrived yet. Note that status can read completed while events (including the answer) are still unread on later pages, so keep advancing from_index until the session is terminal and a poll returns no further events. To skip the loop entirely and just read a finished run’s result, use latest_answer; the SDK helper drains for you.
Path parameters
string
required
The session ID.
Query parameters
integer
default:"0"
Event index to start from. Use this to resume from where you left off.
integer
Maximum number of events to return.
boolean
default:"true"
Whether to include event details in the response.
integer
default:"0"
How long the server should hold the connection waiting for changes, up to
25 seconds. The default 0 returns immediately. Set 20 to 25 for efficient long polling.Response
Response
{
"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": "holo3-122b-a10b",
"input_tokens": 21166,
"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" }
]
}
| Field | Type | Description |
|---|---|---|
status | string | Current session status. |
started_at | string | null | ISO 8601 timestamp when the agent started executing. |
finished_at | string | null | ISO 8601 timestamp when the session reached a terminal state. |
error | string | null | Short, stable error message if the session failed or timed out. Branch on error_code, not on this text. |
error_code | string | null | Machine-readable failure category when the session failed or timed out: environment_error, no_answer, answer_validation, timeout, or internal. See Read how the run ended. |
outcome | string | null | The agent’s self-assessed task outcome, reported with its final answer: success, partial, infeasible, or blocked. See Read how the run ended. |
answer | string | object | null | The agent’s final result once produced; null otherwise. A string by default, or an object matching the agent’s answer_format JSON Schema when one was set. It rides the page that delivers the final events, so keep polling until the session is terminal and drained. For a cursor-independent read, the same value is mirrored on the Session object’s latest_answer. |
metrics | object | Usage and cost rolled up to the moment of the response: steps, total_cost, input_cost, output_cost, reasoning_cost, and cost_per_model[] (each entry carries per-model tokens and costs, including reasoning_tokens). Cost fields are in USD and are null when a model’s price is unavailable, so null-check before summing. |
new_events | array | Events since from_index, each following the event shape (type, data, timestamp). |
Examples
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"
from hai_agents import Client
client = Client()
changes = client.sessions.get_session_changes(
session_id,
from_index=0,
wait_for_seconds=25,
)
import { HaiAgentsClient } from "hai-agents";
const client = new HaiAgentsClient();
const changes = await client.sessions.getSessionChanges({
id: sessionId,
fromIndex: 0,
waitForSeconds: 25,
});
Long-polling pattern
Long polling is more efficient than repeated status checks because the server only responds when something actually changes. The SDK ships a helper that runs the loop for you: it drives termination offstatus (authoritative) while streaming events from changes, resuming from_index and handling the 204 no-change responses automatically.
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.
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.
changes to follow a run: it returns new events and the final answer with near-instant latency and far fewer calls than fixed-interval polling. Reach for status only when you want a cheap, one-off liveness check.Last modified on September 11, 2026
Was this page helpful?