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_body{
"status": "<string>",
"started_at": {},
"finished_at": {},
"error": {},
"error_code": {},
"outcome": {},
"answer": {},
"metrics": {},
"new_events": [
{}
],
"first_index": {},
"pending_tool_calls": [
{}
]
}Sessions
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.
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_body{
"status": "<string>",
"started_at": {},
"finished_at": {},
"error": {},
"error_code": {},
"outcome": {},
"answer": {},
"metrics": {},
"new_events": [
{}
],
"first_index": {},
"pending_tool_calls": [
{}
]
}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, or 204 No Content if no new events arrive within the wait.
Polling rules
changesis a delta: each call returns only what is new sincefrom_index.204means no new events yet, not that there is no answer.- The
answerrides the page that delivers the final events, andstatuscan readcompletedbefore you reach that page. Keep advancingfrom_indexuntil the session is terminal and a poll returns no further events. - To skip the loop for a finished run, read
latest_answer, or use the SDK helper.
Path parameters
string
required
The session ID.
Query parameters
integer
default:"0"
Event index to start from. Pass the next unread index to resume where you left off.
integer
Maximum number of events to return. Minimum
0.boolean
default:"true"
Whether to include event details in the response.
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.Response
string | null
When the agent started executing.
string | null
When the session reached a terminal state.
string | null
Short, stable error message if the session failed or timed out. Branch on
error_code, not on this text.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.string | null
The agent’s self-assessed outcome, reported with its final answer:
success, partial, infeasible, or blocked. See Outcomes.string | object | null
The agent’s final result once produced: a string, or an object matching the agent’s
answer_format. See Polling rules for when it lands. Mirrored on the session’s latest_answer.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.object[]
Events since
from_index, each in the event shape (type, data, timestamp).integer | null
Index of
new_events[0]. Your next from_index is first_index plus the number of new_events. It equals from_index in the cloud. A local session keeps a window of recent events, so it can be higher there when older events were dropped. null from servers that predate it: count from from_index then.object[]
The custom tool calls the agent is waiting on, each
{ tool_name, args, id }, as on status. Empty unless the status is awaiting_tool_results, or paused while calls are pending.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,
});
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": "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" }
],
"first_index": 0,
"pending_tool_calls": []
}
Long-polling pattern
The SDK helper runs the loop for you: it ends onstatus (authoritative), streams events from changes, advances from_index, and handles 204 responses.
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.
Was this page helpful?