InboxItem rows. Most are created by automations: one TaskAutomation per trigger type per studio, each with either a simple list of task blocks or a node flow. A flow that waits or branches is tracked by an AutomationFlowRun.
The JSON columns on this page are documented in Automation JSON.
InboxItem
Table inbox_items. One task or notification for the coaching team.
Unique on
(studioId, autoKey). Indexes: (studioId, status), (studioId, status, priority, createdAt), coachId, clientId, (clientId, status), GIN on coachIds.
autoKey
The task generator (apps/core-api/src/modules/tasks/task-generator.ts) builds a key from the trigger type and the entity it fired for, then inserts. The unique constraint makes the same trigger firing twice for the same entity a no-op.
The suffix distinguishes the several tasks one trigger can fan out into. A task created by a flow node ends with
:fx:<nodeId>.
InboxItemType
The types an automation can be configured for are
TASK_TRIGGER_TYPES in task-automations/task-automations.schema.ts. That list leaves out MESSAGE, CHURN_RISK, MISSED_CHECK_IN, MISSED_STREAK and MANUAL.
InboxItemStatus
OPEN, SNOOZED (with snoozeUntil), DONE, DISMISSED. The scheduler also closes automated tasks whose condition no longer holds.
TaskAutomation
Table task_automations. A studio’s configuration for one trigger type.
Unique on
(studioId, type). Index on studioId.
TaskAssigneeMode
TaskAutomationTask
Table task_automation_tasks. One task block under an automation. A trigger fans out into up to MAX_TASKS_PER_AUTOMATION (5) tasks.
Unique on
(automationId, ordinal).
The column default for
formTypes lists three form types. The Zod default in automationTaskBody lists all four, including PDF_SIGNATURE. Rows written through the API get four. Rows inserted any other way get three.AutomationFlowRun
Table automation_flow_runs. One execution of an automation’s flow for one trigger entity.
Unique on
(studioId, type, entityKey). This makes starts idempotent, exactly like the autoKey contract: the same trigger firing again for the same entity does not start a second run.
Indexes: (status, resumeAt), which the sweeper scans, and (studioId, status).
Lifecycle
Two things resume a waiting run. A delayed BullMQ job on theflow-run queue is the fast path. An hourly repeatable job on the flow-sweep queue is the durable path: it picks up runs whose resumeAt has passed, so a wait survives a Redis flush. On failure the position is preserved.
FlowNodeExecution
Table flow_node_executions. The retry and dedup ledger for a run.
Unique on
(runId, nodeId). The executor inserts the row first and treats a unique violation (P2002) as “already executed”. A crash followed by a retry can therefore never send the same push, WhatsApp message or webhook twice.
AutomationHook
Table automation_hooks. A webhook an automation platform registered for one studio event.
Index:
(studioId, event, active).
A row here is a live subscription, not configuration a coach maintains. Make creates it when a scenario’s instant trigger is switched on and deletes it when the trigger is switched off. See Outbound webhooks.
AutomationRule
Table automation_rules. The original automation model.
The
automation module at /v1/web/automation is plain CRUD over this table. Its schema accepts any object for trigger and action (z.record(z.string(), z.unknown())). The automations that actually run today are TaskAutomation rows, driven by the task scheduler and the flow executor.