Send events
POST /v1/sessions/{session_id}/events
Sends a message, a tool approval or an interrupt to a session.
Sends are not idempotent. If a send times out or its result is unknown, read the session’s events before you send it again.
The server checks every event against the session before any of them acts, then applies them in order. If an event is refused while they are applied, because the session changed in the meantime, the earlier events stay accepted and the error message says how many were. Don’t resend those.
Each event gets a receipt with a sevt_receipt_ ID and processed_at: null. The stored event, with its real
ID, then appears on the stream and in the history. The receipt ID is not the stored event’s ID, so match the
stored event on the stream rather than by ID.
Before a session starts, it accepts exactly one user.message per request. Send more only after the stream
shows session.status_running. A user.message sent while a turn runs is added to that turn instead of
starting a new one.
A user.tool_confirmation also needs the tasks:approve capability and a
personal key.
The key must have the tasks:create capability. For what each capability allows, see Keys.
Path parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
session_id | string | Yes | The ID of the session to send events to. |
Body
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
events | array<object> | Yes | The events to send, applied in order. | |
events[].type | string | Yes | The event type. One of user.message, user.tool_confirmation or user.interrupt. | |
events[].content | array<object> | No | One or more {type: "text", text} blocks, up to 100,000 characters in all. Required for user.message. | |
events[].tool_use_id | string | No | The ID of the agent.tool_use event to answer, from stop_reason.event_ids on the session.status_idle event. Required for user.tool_confirmation. | |
events[].result | string | No | One of allow (approves this one call) or deny. Required for user.tool_confirmation. | |
events[].deny_message | string | null | No | Text stored with your answer. The receipt echoes it whenever you send it. The stored user.tool_confirmation event carries it only for a deny. Its length is limited only by the 10 MiB request body limit. |
Response
Returns 200 with a data array that holds one receipt for each event you sent.
{ "data": [ { "type": "user.message", "content": [ { "type": "text", "text": "Now chart the top three." } ], "id": "sevt_receipt_219b3afe-157c-452d-981b-c71353fb80bc", "processed_at": null } ]}Errors
This endpoint can return these errors as well as the common errors.
| Status | error.type | Cause and fix |
|---|---|---|
| 400 | invalid_request_error | An event is malformed, or its text is empty or over 100,000 characters in all. Fix the event the message names. |
| 400 | invalid_request_error | The session is archived. Archived sessions take no events. Create a new session. |
| 400 | invalid_request_error | The session has not started and the request holds more than one user.message, or the first turn is still starting. Send one message, then wait for session.status_running before you send the next. |
| 400 | invalid_request_error | A tool_use_id is unknown, repeated or already answered. Send only IDs from the latest stop_reason.event_ids that you have not answered. |
| 400 | invalid_request_error | An event has a type this endpoint does not accept: user.tool_result, user.custom_tool_result, user.define_outcome or system.message. Send only user.message, user.tool_confirmation or user.interrupt. |
| 400 | invalid_request_error | An event carries session_thread_id. Sessions have one thread. Remove session_thread_id and retry. |
| 400 | invalid_request_error | A user.tool_confirmation follows a user.interrupt in the same request. Send the confirmation before the interrupt, or in a separate request. |
| 403 | permission_error | The key does not act for the session’s owner, or the owner is no longer an active member. Use the owner’s personal key or the service account key that created the session. |
| 403 | permission_error | A user.tool_confirmation was sent with a key that is not personal, or that lacks tasks:approve. Use a personal key with tasks:approve. |
| 404 | not_found_error | No session with this ID exists, or this key can’t read it. Check the session ID. |
| 409 | invalid_request_error | The session’s state changed while the request was processed. The tool call was already answered, or the session is stopping or moving. Read the events, then decide whether to send again. |
Example
# A messagecurl -sS -X POST "$CONTEXT_BASE_URL/v1/sessions/dd13d728-93e3-440a-874c-4efeb58c97e0/events" -H "x-api-key: $CONTEXT_API_KEY" \ -H "content-type: application/json" \ -d '{"events": [{"type": "user.message", "content": [{"type": "text", "text": "Now chart the top three."}]}]}'
# Approve the waiting tool call (needs tasks:approve)curl -sS -X POST "$CONTEXT_BASE_URL/v1/sessions/dd13d728-93e3-440a-874c-4efeb58c97e0/events" -H "x-api-key: $CONTEXT_API_KEY" \ -H "content-type: application/json" \ -d '{"events": [{"type": "user.tool_confirmation", "result": "allow", "tool_use_id": "sevt_29_0"}]}'
# Stop the current turncurl -sS -X POST "$CONTEXT_BASE_URL/v1/sessions/dd13d728-93e3-440a-874c-4efeb58c97e0/events" -H "x-api-key: $CONTEXT_API_KEY" \ -H "content-type: application/json" -d '{"events": [{"type": "user.interrupt"}]}'sid = "dd13d728-93e3-440a-874c-4efeb58c97e0"client.beta.sessions.events.send(sid, events=[{"type": "user.message", "content": [{"type": "text", "text": "Now chart the top three."}]}])client.beta.sessions.events.send(sid, events=[{"type": "user.tool_confirmation", "result": "allow", "tool_use_id": "sevt_29_0"}])client.beta.sessions.events.send(sid, events=[{"type": "user.interrupt"}])const sid = "dd13d728-93e3-440a-874c-4efeb58c97e0"await client.beta.sessions.events.send(sid, { events: [{ type: "user.message", content: [{ type: "text", text: "Now chart the top three." }] }] })await client.beta.sessions.events.send(sid, { events: [{ type: "user.tool_confirmation", result: "allow", tool_use_id: "sevt_29_0" }] })await client.beta.sessions.events.send(sid, { events: [{ type: "user.interrupt" }] })