Skip to main content
GET
/
api
/
v1
/
agents
/
{agentId}
/
tasks
/
{taskId}
/
events
Subscribe to a task's event stream (SSE).
curl --request GET \
  --url https://openapi.beeos.ai/api/v1/agents/{agentId}/tasks/{taskId}/events \
  --header 'Authorization: Bearer <token>'
"<string>"

Authorizations

Authorization
string
header
required

Pass a user JWT or a oag_ User API Key on the Authorization: Bearer <token> header. Both are validated by openapi-gateway against the Auth service.

Both credential types are user-scoped: every key (and every JWT) is bound to exactly one owner, and every route grants the caller full access to that owner's own resources. Cross-tenant access is denied by owner-ACL inside the handlers — there is no per-route scope vocabulary on this API.

Removed in v1.1.0: the legacy agents:* / tasks:* / files:* / instances:* scope set has been dropped together with the 403 insufficient_scope error. Existing oag_ keys automatically gain full owner-level access and do not need to be re-issued. SDK calls that previously passed scopes to createAPIKey should drop the argument. See the changelog at the bottom of this spec for the full migration note.

Path Parameters

agentId
string
required
Maximum string length: 128
taskId
string
required
Maximum string length: 128

Query Parameters

since
integer<int64>

Replay cursor. Matches the offset field on the last received event — clients persist this value and pass it back on reconnect to resume without gaps or duplicates (Last-Event-ID semantics; the wire spelling stays since for backward compatibility with the pre-v2 SSE clients).

Special values:

  • omitted / 0 — full history replay (per MS Subscribe contract). Useful for late-attaching SSE clients that want the full task transcript.
  • <offset> — resume strictly AFTER the given offset. The first event the client receives has offset > since.

The wire contract guarantees that the offset field on emitted events is strictly monotonically increasing per channel but is NOT guaranteed contiguous (ADR-0022 §1.2). A producer-side storage failure may leave a small hole — e.g. you may observe ... 40, 42, ... with 41 missing. Clients MUST treat offset > since as the resume invariant, never offset == since + 1. Per ADR-0022 the retention window is 24 h after last-touch (durable rows) and 24 h or 10 000 entries (ephemeral chunk stream, whichever is tighter); outside that window SSE replies with a backfill_truncated frame before resuming.

Required range: x >= 0

Response

SSE stream of channel-message envelopes.

Server-Sent Events stream.

Per-event JSON payload format (v3 envelope fields body, state, stop_reason, updated_at are populated whenever the underlying row was written via the v3 path; legacy v1 / v2 rows leave them empty — consumers should fall back to payload.text

  • type to detect terminality):
event: message
data: {
"type": "agent_message_chunk", // see below
"message_id": "msg-uuid",
"offset": 5, // monotonic per-channel offset
"in_reply_to": "msg-source-uuid",
"publisher_id": "agent-identity",
"payload": { ... }, // opaque, per-type
"body": "Hello, world", // v3 cumulative text (optional)
"state": "streaming", // v3 lifecycle (optional)
"stop_reason": "", // v3 terminal reason (only set when state != streaming)
"created_at": "2026-05-14T18:00:00.000Z",
"updated_at": "2026-05-14T18:00:00.123Z"
}

Backfill truncation frame — emitted ONCE when the server caps the historical replay window (see since=<offset> semantics above). Field shape matches TaskSSEBackfillTruncated. Clients should resume from oldest_redis_offset after seeing this frame:

event: backfill_truncated
data: {
"since": 100,
"oldest_redis_offset": 1500,
"hint": "stream evicted entries older than oldest_redis_offset"
}

Terminal events:

event: end
data: { "reason": "task_terminal" } # task only: auto-close after agent_reply / agent_reply_error / agent.refuse / agent_busy
event: end
data: { "reason": "stream_closed" } # MS closed the upstream stream
event: end
data: { "reason": "channel_closed" } # conversation only: caller (or peer) deleted the conversation

end.reason enum:

reasonsurfacewhen
task_terminaltaskterminal reply auto-closed the SSE
stream_closedtask + conversationMS closed the upstream stream
channel_closedconversationthe underlying channel was deleted

Well-known message type values:

typemeaning
chat_messagecaller's prompt
agent_thought_chunkagent reasoning (incremental)
agent_message_chunkagent reply delta (incremental)
agent_reply_deltaterminal-bound streaming delta
agent_replyterminal success reply
agent_reply_errorterminal failure reply
agent.input_requiredpause awaiting user.continue
agent.auth_requiredpause awaiting user.auth_grant
user.continuecaller resume of input_required
user.auth_grantcaller resume of auth_required
chat_cancelcaller-initiated cancel