Skip to main content
A conversation is the foundational unit of interaction in Feather. Whether a user chats through your app, speaks to a voice assistant, sends a text, or emails you, Feather represents that session as a single, consistent conversation object. This unified model means your analytics, audit logs, and escalation workflows behave the same way regardless of channel.

The conversation object

Every conversation carries a consistent set of fields:
channel and surface are assigned by Feather, not set by you. Creating a conversation over the API always produces a chat conversation; SMS, voice, and email conversations are created by their channel adapters.

Session types

Feather supports three session types so you can test and simulate behavior without affecting production data:

live

A real conversation with a real end user. Counts toward usage and appears in production analytics. Programmatic (API-key) callers can only create live sessions.

test

A developer session that runs the full assistant stack but is excluded from production dashboards. Created from the dashboard (or the test-chat endpoint).

simulation

A scripted run used by simulation suites to evaluate assistant revisions before you activate them.
To exercise an assistant programmatically without polluting analytics, use a dedicated non-production organization or the dashboard test chat. POST /v1/conversations from an API key always creates a live session.

Status lifecycle

A conversation moves through a defined set of statuses: Voice and queued sessions add transient states such as queued, ringing, pending_dispatch, paused, and transferred. Terminal states are closed, expired, error, and superseded. Only non-terminal statuses accept a new turn.
When a turn response comes back with session_status of awaiting_approval or waiting_for_human, stop sending new turns and poll GET /v1/conversations/{id}/messages until the status returns to active or the conversation closes.

Turns

Each exchange within a conversation is a turn. You submit a turn with a single user_message, and the assistant produces a reply. Submit a turn with POST /v1/conversations/{id}/turns:
Request
The response includes the assistant’s text, the session_status, and message_seqs (the sequence numbers of the messages this turn produced). Ordering is by message seq, not by timestamp.
Set include_evidence: true on the turn request to receive the knowledge-base chunks used to generate the reply. Each evidence entry identifies the source document and chunk. Callers need the CONVERSATIONS_READ permission to see the full evidence view.
POST /v1/conversations/{id}/turns/stream returns a Server-Sent Events stream. You receive delta events (token chunks), an optional member event when a team member becomes active, a final complete event with the full turn result, and an error event on failure.
A conversation processes one turn at a time. Submitting a turn while another is in flight returns 409 (retryable) — wait for the first to finish.

Channels

All four channels map to the same conversation model. The channel determines how messages arrive and how responses are delivered — the session logic is identical.

Chat

Create a conversation with POST /v1/conversations and submit turns via the API. Powers your web/mobile app and the embeddable webchat widget.

SMS

Feather receives inbound SMS on your configured phone number and sends outbound replies. Each number maps to a conversation per end user.

Voice

Inbound and outbound calls are transcribed in real time and answered via TTS. Voice conversations support mid-call transfer to a human.

Email

Inbound emails create or continue a conversation thread; Feather replies from your connected mailbox with full thread history as context.
See the Channels concept for how the channel, surface, and identity model fit together.

Threads and identity

Every conversation is associated with an end_user_id. You control how a conversation resolves to an end user when you create it:
  • end_user_id — reference an existing end user directly. (An unknown ID returns 404.)
  • org_external_end_user_id — pass your own stable identifier for the user (a customer ID or an anonymous browser token). Feather get-or-creates an end user for it and bridges it to any existing verified contact (email or phone) for the same person.
  • Neither — Feather resolves the authenticated caller as the end user.

external_id — chat thread keys

By default there is one open conversation per end user on a given surface. Pass an external_id to run multiple concurrent threads for the same user: a new value opens a new thread, and reusing a value resumes that thread (which makes creation idempotent under retries). external_id is unique per organization for the life of the row.
For anonymous webchat visitors, send a stable per-visitor token in org_external_end_user_id (for example, a persisted browser ID). A fresh value on every page load mints a new identity and loses conversation history and memory.

Human-in-the-loop states

Two statuses represent human-in-the-loop (HITL) scenarios. Both are managed through the dedicated HITL API, not through the conversation endpoints.

waiting_for_human

Set when an assistant requests a handoff (the request_human_handoff platform tool, a workflow handoff, or a policy handoff action). The conversation appears under GET /v1/hitl/handoffs. A human relays messages with POST /v1/hitl/handoffs/{id}/messages and ends the handoff with POST /v1/hitl/handoffs/{id}/resolve.

awaiting_approval

Set when a policy with the require_approval action pauses a tool call. The pending item appears under GET /v1/hitl/approvals. A reviewer decides with POST /v1/hitl/approvals/{id}/decide ({"decision": "approve"} or {"decision": "deny"}). On approval the tool call proceeds; on denial the turn is blocked and a safe message is returned.

Next steps

Run a conversation

A hands-on walkthrough of creating a conversation, submitting turns, streaming, and polling.

Conversations API reference

Full reference for conversation, turn, and read endpoints.