Notify event envelope
All notify clients POST the same envelope to POST /api/sessions/:id/notify. The backend treats payload as opaque JSON, while extracting top-level metadata for validation and OTel-backed notification records. Requests that include agent_id, agent_name, source, or event_type are rejected.
Envelope fields
session_id(string, required)occurred_at(RFC3339 string, optional)payload(object, required)raw(string, optional; raw JSON argument from Codex)event_id(string, optional; stable id for dedupe)
Payload requirements:
type(string, required)
Example: Codex notify payload
{
"session_id": "Codex 1",
"occurred_at": "2026-01-28T20:17:42Z",
"payload": {
"type": "agent-turn-complete",
"turn_id": "turn-42",
"model": "gpt-5"
},
"raw": "{\"type\":\"agent-turn-complete\",\"turn_id\":\"turn-42\",\"model\":\"gpt-5\"}",
"event_id": "codex-notify:turn-42"
}Example: Plan event
{
"session_id": "Codex 1",
"occurred_at": "2026-01-28T20:17:42Z",
"payload": {
"type": "plan-L1-wip",
"plan_file": ".gestalt/plans/gestalt-notify.org",
"heading": "Route session events via gestalt-notify",
"state": "wip",
"level": 1
},
"event_id": "manual:plan-L1-wip:gestalt-notify"
}Flow event mapping
Notify payloads are normalized into Flow trigger events. The canonical event type is derived from payload.type:
new-planorplan-new->plan-newplan-*->plan-updateprogressorplan-update->plan-updatestartorwork-start->work-startwork-progress->work-progressfinishorwork-finish->work-finishgit-commitorcommit->git-commitagent-turn*->agent-turn-completeprompt-voice*->prompt-voiceprompt-text*->prompt-text- everything else ->
agent-turn-complete
Flow fields include:
type: canonical event typetimestamp: RFC3339 time (fromoccurred_ator server time)session_idandsession.idnotify.type: originalpayload.typenotify.event_id:event_idwhen providednotify.<key>for scalar payload keys (strings/bools/numbers)- top-level aliases for the same payload keys (for example
summary,plan_file,task_title)
Template tokens follow the same keys, for example:
{{summary}}, {{plan_file}}, {{plan_summary}}, {{task_title}}, {{task_state}}, {{git_branch}}, {{session_id}}, {{timestamp}}, {{event_id}}, {{notify.summary}}, {{notify.type}}, {{notify.event_id}}.
OTel log attribute contract
For every successfully parsed notify request (after session and agent validation), the backend emits one INFO log entry with message notify event accepted. This entry is visible through /api/logs and /api/logs/stream.
Stable attributes:
gestalt.category=notificationgestalt.source=notifytype(canonical notify event type)notify.type(original payloadtype)notify.event_id(when provided)session.idandsession_idnotify.dispatch(queued,flow_unavailable,temporal_unavailable, orfailed)- scalar payload aliases as both top-level keys and
notify.<key>
Guarantee:
- Logging is additive and best-effort (no API response changes).
- If a notify request is accepted but dispatch later degrades (for example Temporal unavailable), the notify log entry is still emitted with
notify.dispatchset to the outcome.
Example: Flow automation for a new plan
{
"version": 1,
"triggers": [
{
"id": "plan-created",
"label": "New plan created",
"event_type": "plan-new",
"where": {
"session.id": "Codex 1"
}
}
],
"bindings_by_trigger_id": {
"plan-created": [
{
"activity_id": "toast_notification",
"config": {
"level": "info",
"message_template": "{{summary}}"
}
},
{
"activity_id": "spawn_agent_session",
"config": {
"agent_id": "coder",
"message_template": "New plan summary: {{notify.plan_summary}}"
}
}
]
}
}