---
title: "List Private Chat Events"
method: GET
path: "/worker/{worker_id}/private-chat/events"
tags: ["workers"]
---

# List Private Chat Events

`GET /worker/{worker_id}/private-chat/events`

The caller's private-chat feed on this worker.

Addressed by the parent, never by the chat's own id — that row is storage. The owner
comes from the session, so this cannot read anyone else's. A chat nobody has started
is an empty feed with null ``stream_cursors``, not a 404, so a client renders a
composer and opens no socket without branching on existence.

## Path parameters

- `worker_id` string, required

## Query parameters

- `limit` integer — Number of events per page
- `before_occurred_at` string, date-time, nullable — Cursor timestamp
- `before_source` 'resident_agent_event' | 'transcript_summary_events' | 'chatbot_messages' — Feed-layer discriminator for which table a feed row came from. This is a feed concept, distinct from the cursor-stream ``source`` protocol: the feed renders persisted rows (including non-streamable ``chatbot_messages``), it does not tail cursor streams. These values are the canonical vocabulary bound into ``worker_feed_dao._FEED_QUERY``.
- `before_id` string, nullable — Cursor tie-break id
- `search` string, nullable — Case-insensitive substring search over chat message text

## Response `200`

Successful Response

- ListWorkerEventsResponse
  - `events` union[], required
    - union
      - ResidentAgentEventItem
        - `source` 'resident_agent_event'
        - `id` string, required
        - `worker_id` string, required
        - `occurred_at` string, date-time, required
        - `event_type` string, required
        - `data` union, required
          - ToolAction — A synchronous tool call the agent made — its rendered ``tool_response`` stored verbatim because the renderer must reproduce it byte-identically. Covers every tool that returns a tool_response synchronously: the platform's send tool / ``react_to_slack`` / ``private_feedback``, plus the defensive case where the LLM emits a name outside the registered set (the workflow synthesizes a ``"Tool 'X' does not exist..."`` error response). Differentiated by ``tool_name`` — no need for per-tool variants because the data is identical. Stays separate from ``InvestigationCall`` (async result) and ``WaitEntry`` (response derived from semantic fields).
            - `kind` 'tool_action'
            - `occurred_at` string, date-time, required
            - `tool_call_id` string, required
            - `tool_name` string, required
            - `tool_response` WorkflowMessage, required
              - …
            - `result_ts` string, nullable
            - `request_summary` string, nullable
          - InvestigationCall — The agent dispatched an ``investigate`` child workflow. Fire-and-forget and **immutable**: the LLM gets back a ``stub_response`` immediately, and the actual report arrives later as its own ``InvestigationFinished`` event (paired back by ``tool_call_id``). The result is never mutated onto this entry, so the action log is append-only and rebuilds faithfully from the DB.
            - `kind` 'investigation'
            - `occurred_at` string, date-time, required
            - `tool_call_id` string, required
            - `investigation_id` string, required
            - `system_chat_id` string
            - `question` string
            - `stub_response` WorkflowMessage, required
              - …
          - InvestigationFinished — The async result of an ``InvestigationCall`` landed — its own append-only event (never mutated onto the call), so the result persists durably and rebuilds from the DB. Paired back to its call by ``tool_call_id`` — follow-ups reuse the parent's ``investigation_id``, so the call id is the unambiguous key. ``occurred_at`` is the arrival time; readers pair it with its call to render the ``## Investigation finished`` block.
            - `kind` 'investigation_finished'
            - `occurred_at` string, date-time, required
            - `tool_call_id` string, required
            - `investigation_id` string, required
            - `result_user_message` WorkflowMessage, required
              - …
          - WaitEntry — The agent called ``wait``; the workflow paused until new deltas arrived. Started and ended timestamps are kept separately so the renderer can reconstruct the "Waited Xm." tool response from semantic fields alone — no need to store the rendered string. ``timer_expired`` is set when the self-scheduled deadline fired with no other wake reason, mirroring the existing "Timer expired." suffix.
            - `kind` 'wait'
            - `started_at` string, date-time, required
            - `ended_at` string, date-time, required
            - `tool_call_id` string, required
            - `seconds` integer, nullable
            - `timer_expired` boolean
          - WorkerStatusUpdate — The worker's status top-line, emitted when it declares one on ``wait`` (Feature.WORKER_STATUS). A first-class action-log event so it streams to the /workers UI in real time; the persist path also projects the latest value onto the ``resident_agent_workflow`` row for the cheap list read. Distinct from ``WaitEntry`` (wait timing) — the status is fully known at dispatch and doesn't wait for the wait to end.
            - `kind` 'worker_status_update'
            - `occurred_at` string, date-time, required
            - `status` 'critical' | 'attention' | 'working' | 'stable', required — The worker's present-tense status top-line, shown as a pill on the /workers list. Only these agent-picked values are ever stored in the `status` column; a null column means none has been emitted yet (the UI renders that as `starting`), and the terminal `stopped` state is derived from `stopped_at` — neither is stored here, so there is no second source of truth. The agent-facing definitions live on the ``status`` field in ``prompts.py`` (the only copy the model reads); keep this summary in sync with it. Values: CRITICAL: Immediate coordinated response is warranted because user-facing impact is severe or escalating. ATTENTION: A specific person or team should act now, but immediate coordinated incident response is not warranted. STABLE: Resting floor — no specific person or team needs to act now and no coordinated incident response is warranted. WORKING: Legacy — no longer emittable, see ``EmittableWorkerStatus``.
            - `message` string, nullable
          - SystemReminder — A ``[system reminder]`` line queued by the workflow itself (e.g. the message-budget soft-warning) and drained into the next LLM turn as a user message. Distinct from Slack events because the source is internal, not the channel. The renderer emits these last in each turn's drain bundle so they're the freshest context before the LLM's response.
            - `kind` 'system_reminder'
            - `fired_at` string, date-time, required
            - `text` string, required
          - ScheduledWake — A scheduled directive accepted by the Worker for a normal turn.
            - `kind` 'scheduled_wake'
            - `occurred_at` string, date-time, required
            - `occurrence_id` string, uuid, required
            - `schedule_id` string, uuid, required
            - `schedule_type` 'one_shot' | 'recurring', nullable
            - `directive` string, required
            - `title` string
            - `created_by` string, nullable
            - `created_at` string, date-time, nullable
            - `source_thread_id` string, nullable
          - AssistantTurn — The LLM's response for one turn, plus the drain boundary that fed it. ``drained_through`` is the workflow-clock timestamp at which the drain that fed this turn happened — before the LLM call. The renderer uses it to batch events: an event is drained into this turn iff its ``observed_at`` / ``fired_at`` / ``occurred_at`` is ``> prev_turn.drained_through`` and ``<= this_turn.drained_through``. Tool calls inside ``message.tool_calls`` pair with matching ``SlackSend`` / ``SlackReact`` / ``InvestigationCall`` (stub) / ``WaitEntry`` entries by ``tool_call_id``; the renderer emits each tool response right after this assistant message in the same order as ``tool_calls``. ``new_transcriptions`` holds file transcriptions produced during this turn's drain (the workflow runs upload + transcription as part of the drain step). The renderer passes them to ``format_deltas`` so the ``<file_transcriptions>`` XML block lands co-located with the chat lines that reference the files — mirroring the existing per-batch emission.
            - `kind` 'assistant_turn'
            - `drained_through` string, date-time, required
            - `occurred_at` string, date-time, required
            - `message` WorkflowMessage, required
              - …
            - `new_transcriptions` FileTranscriptionResult[]
              - …
            - `llm_call_id` string, nullable
      - ChatbotMessageEventItem
        - `source` 'chatbot_messages'
        - `id` string, required
        - `worker_id` string, required
        - `occurred_at` string, date-time, required
        - `event_type` string, required
        - `data` union, required
          - SlackChatMessageSnapshot
            - `surface` ChatSurfaceSnapshot, required — The surface and channel where a message was observed.
              - …
            - `message_id` string, required
            - `thread_id` string, nullable, required
            - `sent_at` string, date-time, required
            - `edited_at` string, date-time, nullable
            - `sender` ChatDeltaActor, required — The normalized identity of a message sender or mutation actor.
              - …
            - `message` string, nullable
            - `files` FilePart[]
              - …
            - `reactions` ReactionEntity[]
              - …
            - `subtype` string, nullable
            - `permalink` string, nullable
            - `deleted_at` string, date-time, nullable
            - `platform` 'slack'
            - `rich_content` SlackRichContentEntity — Slack Block Kit content stored with a normalized message.
              - …
            - `user_id_to_display_name` object
            - `user_id_to_profile_photo_url` object
            - `channel_id_to_display_name` object
          - TeamsChatMessageSnapshot
            - `surface` ChatSurfaceSnapshot, required — The surface and channel where a message was observed.
              - …
            - `message_id` string, required
            - `thread_id` string, nullable, required
            - `sent_at` string, date-time, required
            - `edited_at` string, date-time, nullable
            - `sender` ChatDeltaActor, required — The normalized identity of a message sender or mutation actor.
              - …
            - `message` string, nullable
            - `files` FilePart[]
              - …
            - `reactions` ReactionEntity[]
              - …
            - `subtype` string, nullable
            - `permalink` string, nullable
            - `deleted_at` string, date-time, nullable
            - `platform` 'teams'
            - `rich_content` TeamsRichContentEntity — Teams body and Adaptive Card content stored with a normalized message.
              - …
          - WebChatMessageSnapshot
            - `surface` ChatSurfaceSnapshot, required — The surface and channel where a message was observed.
              - …
            - `message_id` string, required
            - `thread_id` string, nullable, required
            - `sent_at` string, date-time, required
            - `edited_at` string, date-time, nullable
            - `sender` ChatDeltaActor, required — The normalized identity of a message sender or mutation actor.
              - …
            - `message` string, nullable
            - `files` FilePart[]
              - …
            - `reactions` ReactionEntity[]
              - …
            - `subtype` string, nullable
            - `permalink` string, nullable
            - `deleted_at` string, date-time, nullable
            - `platform` 'web'
      - TranscriptSummaryEventItem
        - `source` 'transcript_summary_events'
        - `id` string, required
        - `worker_id` string, required
        - `occurred_at` string, date-time, required
        - `event_type` string, required
        - `data` TranscriptEvent, required — Canonical state for one observed call-transcript window. The off-channel counterpart to `SlackEvent`: it lives in `ConversationState.transcript_log`, not `slack_log`, because a call window isn't in Slack and can't be rebuilt from the Slack API on catch-up — so the full log rides the workflow input across `continue_as_new`. `observed_at` (workflow clock) is the merge key shared with `SlackEvent`, so the drain renderer can order a transcript window against Slack messages.
          - `kind` 'transcript'
          - `observed_at` string, date-time, required
          - `source_label` string, required
          - `window_start` string, date-time, required
          - `window_end` string, date-time, required
          - `points` AttributedPoint[]
            - `speaker` string, required
            - `point` string, required
          - `idempotency_key` string, uuid, required
  - `next_cursor` WorkerEventCursor — Composite keyset cursor for the newest-first feed. ``occurred_at`` is the cross-source recency key; ``source`` + ``id`` are the deterministic tie-breaks. The client echoes these back to fetch the next page.
    - `occurred_at` string, date-time, required
    - `source` 'resident_agent_event' | 'transcript_summary_events' | 'chatbot_messages', required — Feed-layer discriminator for which table a feed row came from. This is a feed concept, distinct from the cursor-stream ``source`` protocol: the feed renders persisted rows (including non-streamable ``chatbot_messages``), it does not tail cursor streams. These values are the canonical vocabulary bound into ``worker_feed_dao._FEED_QUERY``.
    - `id` string, required
  - `has_more` boolean, required
  - `stream_cursors` object, nullable

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.net/traversal/apis/fastapi.md) · [All operations](https://skmtc.net/traversal/apis/fastapi/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/traversal/fastapi/revisions/2134ebffd1ef/schema)
