Chat API

The Chat API drives multi-turn conversations with a chat-modality AI Employee. Where the Workflow API starts a single run, the Chat API maintains a session — a persistent conversation that keeps history across turns — and triggers a workflow run for each message you send. The chat service is mounted at /api/v1/chat, so every path below is that prefix plus the path shown.

All requests require a JWT or tenant API key. Examples use https://your-tenant.ema.co as the host.

The conversation model

A conversation has three pieces:

  • A session belongs to one user and one workflow (the chat AI Employee). It holds the conversation's title and history.
  • A message is one turn — user or assistant — inside a session.
  • Sending a user message triggers an asynchronous workflow run. You stream that run to receive the assistant's reply.

The typical flow: create or fetch a session → send a message (get back a run_id) → stream the run to receive the response → repeat.

Sessions

Create a session

POST https://your-tenant.ema.co/api/v1/chat/sessions
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json

{ "workflow_id": "3f7a..." }

If the chat workflow shows a form before the conversation begins, pass its values as input_params; they are persisted on the session and forwarded on every run. The response is the session:

{
  "id": "c0de...",
  "tenant_id": "a4d2...",
  "workflow_id": "3f7a...",
  "user_id": "8c1f...",
  "title": "",
  "last_activity_at": "2026-06-01T12:00:00Z",
  "created_at": "2026-06-01T12:00:00Z"
}

title starts empty and is filled in with an LLM-generated summary as the conversation develops.

Find or reuse a session

  • List: GET /api/v1/chat/sessions?workflow_id={id} — the caller's sessions for a workflow, newest activity first.
  • Get or create the current session: GET /api/v1/chat/sessions/current?workflow_id={id} — returns the most recent session for the user + workflow, auto-creating one if none exist. Handy for "resume where I left off."
  • Get one: GET /api/v1/chat/sessions/{session_id}.
  • Rename: PATCH /api/v1/chat/sessions/{session_id} with { "title": "..." }.
  • Delete (soft): DELETE /api/v1/chat/sessions/{session_id}.

A session is only accessible to its owner; another user's session returns 404.

Messages

Send a message

POST /api/v1/chat/sessions/{session_id}/messages stores the user message, triggers a workflow run with the full conversation history, and returns immediately.

POST https://your-tenant.ema.co/api/v1/chat/sessions/c0de.../messages
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json

{ "content": "How do I reset my password?" }

The response is 202 Accepted with the IDs you need to follow the turn:

{
  "message_id": "11aa...",
  "assistant_message_id": "22bb...",
  "run_id": "9b2c..."
}
  • message_id — the stored user message.
  • assistant_message_id — the ID the assistant's reply will be written to. Use it when submitting feedback on that reply.
  • run_id — the workflow run that will produce the reply. Connect to its stream to receive the answer.

The assistant message is written asynchronously by the chat service when the run completes.

Receive the response

Connect to the workflow run's SSE stream to receive the assistant's reply in real time. For an internal user that is the workflow endpoint; for an external chatbot visitor the chat service proxies it so the visitor's token never reaches the workflow service:

GET https://your-tenant.ema.co/api/v1/chat/runs/9b2c.../stream
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Accept: text/event-stream

Each event is data: <json>; terminate on run_completed, run_failed, or run_paused.

List messages

GET /api/v1/chat/sessions/{session_id}/messages returns every message in the session, oldest first. Assistant messages may carry sources (deduplicated citations), response_buttons (clickable suggested replies), and a feedback object once feedback is submitted.

Dry run

Pass "is_dry_run": true in the send-message body to run the turn through the dry-run path — no Tool side effects and the assistant turn is not persisted. The builder's chat simulator uses this.

Per-turn impersonation. When the EMU_CHAT_IMPERSONATION_ENABLED environment flag is on, you may send a per-turn user_context object to personalize the run as a different identity (and set impersonation_request: true for audit clarity). PII leaves in user_context are tokenized before the value reaches the workflow or is stored. When the flag is off, supplying either field returns 403. The flag is off by default in production.

Human-in-the-loop in chat

If a turn pauses for a human (for example, an approval inside the workflow), the run reaches run_paused. Respond through the chat-service proxy so the response routing matches the chat session:

POST https://your-tenant.ema.co/api/v1/chat/hitl/{request_id}/respond
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json

{ "response": { "type": "choice", "value": "approve" } }

Chat validates that the request belongs to the session's workflow and tenant, then forwards it to the workflow service. The response payload shapes are the same as the Workflow API HITL endpoint.

Feedback

Let users rate an assistant message:

  • Submit (L1): POST /api/v1/chat/messages/{message_id}/feedback with { "sentiment": "positive" | "negative", "category": "..." }. Idempotent — resubmitting overwrites the prior sentiment and category. The response may include a run_id when submitting feedback triggers a feedback workflow; subscribe to that run's stream to receive any follow-up.
  • Add free text (L2): PATCH /api/v1/chat/messages/{message_id}/feedback with { "feedback_text": "..." }. Requires that L1 feedback was already submitted.

Embed config

For rendering a chat surface, GET /api/v1/chat/workflows/{workflow_id} returns the chat-embed display config — the workflow id and name, the welcome message and welcome buttons, and feedback-pill settings — so a chat front end never has to call the workflow service directly.

External and partner entry points

Two server-to-server endpoints let trusted external systems converse on behalf of their own end users. These are covered in full on the triggers page:

  • POST /api/v1/chat/external/trigger — trigger a chat message from an external channel, resolving the end user from a bearer token.
  • POST /api/v1/chat/external/chat — partner-API chat where the caller supplies the end-user identity and user_context directly.

See Triggering AI Employees for when to use each.

What's next

  • Triggering AI Employees — chat triggers alongside API, schedule, document, and external triggers.
  • Workflow API — the run, stream, and HITL endpoints that back each chat turn.
  • AI Employee API — build and publish the chat AI Employee you're conversing with.

Last updated: Jul 3, 2026