State Machine
Each conversation instance follows a defined set of state transitions. States ensure predictable behavior and full observability.
States
| State | Description |
|---|---|
CREATED | Instance created, agent has not yet sent first message |
ACTIVE | Agent is actively processing the conversation |
WAITING_FOR_REPLY | Message sent to contact, awaiting their response |
WAITING_FOR_AGENT | Contact replied, awaiting agent processing |
HEARTBEAT_SCHEDULED | Follow-up timer is pending |
PAUSED | Instance suspended by operator command |
QUEUED | Waiting for another instance on the same contact to complete |
NEEDS_HUMAN_INTERVENTION | Agent has flagged for human review |
COMPLETED | Conversation finished successfully |
ABANDONED | Contact did not respond after max follow-ups |
FAILED | Unrecoverable error or manual cancellation |
Transitions
Normal Flow
CREATED → ACTIVE → WAITING_FOR_REPLY → WAITING_FOR_AGENT → ACTIVE → ...The conversation cycles between ACTIVE, WAITING_FOR_REPLY, and WAITING_FOR_AGENT until the agent calls end_conversation().
ACTIVE → WAITING_FOR_REPLY— agent sends a message (message_sent)WAITING_FOR_REPLY → WAITING_FOR_AGENT— contact replies (contact_replies)WAITING_FOR_AGENT → ACTIVE— agent finishes processing (agent_processes_reply)WAITING_FOR_AGENT → WAITING_FOR_REPLY— agent sends another message before finishing (message_sent)ACTIVE → WAITING_FOR_AGENT— contact replies while agent is active (contact_replies)
Heartbeat Flow
WAITING_FOR_REPLY → HEARTBEAT_SCHEDULED → WAITING_FOR_REPLYWhen a contact does not respond within the configured interval, the heartbeat fires. If max follow-ups are exceeded:
HEARTBEAT_SCHEDULED → ABANDONEDQueuing
Only one active instance per contact. Additional instances enter QUEUED:
QUEUED → CREATED (when prior instance reaches terminal state)Pause / Resume
Any non-terminal state can be paused. The previous state is recorded for resumption:
ACTIVE → PAUSED → ACTIVE
WAITING_FOR_REPLY → PAUSED → WAITING_FOR_REPLYHuman Intervention
The agent can flag a conversation for human review:
ACTIVE → NEEDS_HUMAN_INTERVENTION → ACTIVE (via relay resume or relay send)Error Handling
Unrecoverable errors transition to FAILED:
ACTIVE → FAILED (unrecoverable_error)Cancellation
Any non-terminal state can be cancelled:
(any non-terminal) → FAILED (reason: "cancelled")Complete Transition Table
| From State | Event | To State |
|---|---|---|
CREATED | agent_sends_first_message | ACTIVE |
CREATED | contact_has_active_instance | QUEUED |
CREATED | pause | PAUSED |
CREATED | cancel | FAILED |
QUEUED | prior_instance_terminal | CREATED |
QUEUED | pause | PAUSED |
QUEUED | cancel | FAILED |
ACTIVE | message_sent | WAITING_FOR_REPLY |
ACTIVE | contact_replies | WAITING_FOR_AGENT |
ACTIVE | end_conversation | COMPLETED |
ACTIVE | request_intervention | NEEDS_HUMAN_INTERVENTION |
ACTIVE | unrecoverable_error | FAILED |
ACTIVE | pause | PAUSED |
ACTIVE | cancel | FAILED |
WAITING_FOR_REPLY | contact_replies | WAITING_FOR_AGENT |
WAITING_FOR_REPLY | heartbeat_fires | HEARTBEAT_SCHEDULED |
WAITING_FOR_REPLY | end_conversation | COMPLETED |
WAITING_FOR_REPLY | pause | PAUSED |
WAITING_FOR_REPLY | cancel | FAILED |
WAITING_FOR_AGENT | message_sent | WAITING_FOR_REPLY |
WAITING_FOR_AGENT | agent_processes_reply | ACTIVE |
WAITING_FOR_AGENT | end_conversation | COMPLETED |
WAITING_FOR_AGENT | pause | PAUSED |
WAITING_FOR_AGENT | cancel | FAILED |
HEARTBEAT_SCHEDULED | followup_sent | WAITING_FOR_REPLY |
HEARTBEAT_SCHEDULED | max_followups_exceeded | ABANDONED |
HEARTBEAT_SCHEDULED | pause | PAUSED |
HEARTBEAT_SCHEDULED | cancel | FAILED |
PAUSED | resume | (previous state) |
PAUSED | cancel | FAILED |
NEEDS_HUMAN_INTERVENTION | resume | ACTIVE |
NEEDS_HUMAN_INTERVENTION | manual_send | ACTIVE |
NEEDS_HUMAN_INTERVENTION | pause | PAUSED |
NEEDS_HUMAN_INTERVENTION | cancel | FAILED |
Terminal States
These states are final. No further transitions are possible:
COMPLETED— conversation ended successfullyABANDONED— contact unresponsive after max follow-upsFAILED— error or manual cancellation