Skip to main content
GET
/
api
/
v2
/
sessions
/
{id}
/
events
List events
curl --request GET \
  --url https://agp.eu.hcompany.ai/api/v2/sessions/{id}/events \
  --header 'Authorization: Bearer <token>'
import requests

url = "https://agp.eu.hcompany.ai/api/v2/sessions/{id}/events"

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}/events', 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}/events",
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}/events"

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}/events")
.header("Authorization", "Bearer <token>")
.asString();
require 'uri'
require 'net/http'

url = URI("https://agp.eu.hcompany.ai/api/v2/sessions/{id}/events")

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
Returns a paginated list of events for a session. Use /changes for live tailing; this endpoint is for historical pagination. Auth is optional, so public shares are supported. Returns a paginated list of TrajectoryEvent objects.

Path parameters

id
string
required
The session ID.

Query parameters

page
integer
default:"1"
Page number (1-based).
size
integer
default:"50"
Items per page. Maximum: 200.
sort
string
default:"timestamp"
Sort order. Options: timestamp, -timestamp.
type
string
Filter by event type, e.g. AgentEvent or MetricsUpdateEvent. See Event shape for the full list.

Event shape

Every event, on both this endpoint and /changes, is the same envelope:
Event envelope
{ "type": "AgentEvent", "data": { "...": "..." }, "timestamp": "2026-06-01T15:14:05Z" }
FieldTypeDescription
typestringThe event type (see below).
dataobjectType-specific payload.
timestampstringISO 8601 time the event was recorded.

Event types (type)

The most common event types are below. data is an open JSON object whose shape varies by type, so treat this list as representative rather than exhaustive (other types such as DelayAgentStartEvent, AgentRunStatusChangeEvent, and LiveViewUrlEvent may appear).
typedataEmitted when
RequestStartEvent{}The session request is received.
RequestStartDispatchedEvent{ "status": "..." }The run is dispatched for execution.
AgentStartedEvent{}The agent begins executing.
AgentEventthe step event (see below)The agent observes, decides, acts, or answers.
MetricsUpdateEvent{ "metrics": {...} }Usage and cost are rolled up. metrics carries steps, total_cost, input_cost, output_cost, reasoning_cost, and cost_per_model[]. Cost fields are in USD and null when a model’s price is unavailable.
ActiveStateChangeEvent{ "state": "..." }The agent’s active state changes (running, idle, or awaiting_tool_results; the latter also carries pending_tool_calls for custom tools).
AgentCompletionEvent{ "reason": "..." }The run ends (e.g. "finished").
AgentErrorEvent{ "error": "...", "trace": "...", "info": ... }The run fails; the session moves to failed.

Agent activity (AgentEvent.data)

The agent’s step-by-step trace lives inside AgentEvent. Its data is the step event; data.kind tells you what happened:
data.kindPayload (under data)Meaning
policy_eventreasoning_content, content, tool_reqs[], validation_errors[]The agent’s decision: its reasoning, message, and the action(s) it chose.
observation_eventtype, text, image, metadata (see Observation shapes)What the agent perceived this step.
tool_resulttool_req (tool_name, args, id), resultThe outcome of an action; result is opaque JSON.
answer_eventanswerThe final answer. Same value as /changes answer.
error_eventerror, origin, tool_req?A recoverable error during the step (e.g. a failed or invalid action).
message_eventcaller_id, content[]A user or agent message.
flow_eventflow, originA control-flow signal such as pause, resume, or force_answer, including the ones you trigger via the session controls.
Each tool_reqs[] entry (and tool_result.tool_req) is { tool_name, args, id }. policy_event.content and reasoning_content may be null. Parsing a stream means switching on the outer type, then for AgentEvent, on data.kind:
AgentEvent
{
  "type": "AgentEvent",
  "timestamp": "2026-06-01T15:14:05Z",
  "data": {
    "kind": "observation_event",
    "type": "web",
    "text": null,
    "image": { "type": "url", "source": "https://.../screenshot-7f3a.png", "media_type": "image/png" },
    "metadata": {
      "url": "https://example.com",
      "title": "Example Domain",
      "text": "# Example Domain\nThis domain is for use in illustrative examples...",
      "tabs": ["0"],
      "current_tab": "0",
      "viewport_size": [1200, 1200],
      "page_size": [1200, 3000],
      "scroll_position": [0, 0]
    }
  }
}

Observation shapes

An observation_event is flat: type names the environment that produced it, image is the screenshot (if any), and metadata is that environment’s snapshot, shaped by type (an open object, so switch on type to read it). The Browser emits one of two types, depending on the environment’s mode. The page text lives in metadata, not in the top-level text: read metadata.text for web and metadata.page_markdown (or page_html) for textual_web. The top-level text carries only occasional step notices (e.g. [page unchanged since previous observation]) and is null for most observations. web is emitted in visual mode: a screenshot (top-level image) plus page metadata.
metadata fieldTypeDescription
urlstringCurrent page URL.
titlestringPage title.
textstringVisible page rendered as markdown. Empty unless the mode sets markdown: true.
tabsarrayOpen tab IDs.
current_tabstringActive tab ID.
viewport_sizearrayViewport [width, height] in pixels.
page_sizearrayFull page [width, height] in pixels.
scroll_positionarrayScroll offset [x, y] in pixels.
cursor_positionarrayCursor [x, y] in pixels, or null.
textual_web is emitted in text mode: paginated page text under metadata, no screenshot.
metadata fieldTypeDescription
urlstringCurrent page URL.
titlestringPage title.
tabsarrayOpen tab IDs.
current_tabstringActive tab ID.
modestring"markdown" or "html".
page_markdownstringFull page rendered as markdown.
page_htmlstringFull page HTML.
text_offsetintegerStart character offset of the current chunk.
chunk_sizeintegerCharacters per chunk.
chunk_numberintegerCurrent chunk, 1-based.
total_chunksintegerTotal chunks for the page.

Images

The image on an observation_event, and any image inside message_event.content[], is { source, type, media_type }. type is url for platform-served screenshots (fetch source with your API key) or base64 for inline images. Images embedded in a tool_result.result or an answer_event.answer stay inline as base64 within that opaque payload, never as URLs.

Examples

curl "https://agp.eu.hcompany.ai/api/v2/sessions/$SESSION_ID/events?size=10" \
  -H "Authorization: Bearer $HAI_API_KEY"
from hai_agents import Client

client = Client()

page = client.sessions.list_session_events(session_id, size=10)
for event in page.items:
    print(event.type, event.timestamp)
import { HaiAgentsClient } from "hai-agents";

const client = new HaiAgentsClient();

const page = await client.sessions.listSessionEvents({ id: sessionId, size: 10 });
for (const event of page.items) {
  console.log(event.type, event.timestamp);
}