> Source: https://builder.ema.ai/v2/api-reference/chat-api
> Title: Chat API

# Chat API

The Chat API drives multi-turn conversations with a chat-modality AI Employee. Where the [Workflow API](/builder/v2/api-reference/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](/builder/v2/api-reference/authentication). 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

```http
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:

```json
{
  "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.

```http
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:

```json
{
  "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:

```http
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.

> [INFO]
> **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:

```http
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](/builder/v2/api-reference/workflow-api#human-in-the-loop-hitl).

## 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](/builder/v2/api-reference/triggers) for when to use each.

## What's next

-   [Triggering AI Employees](/builder/v2/api-reference/triggers) — chat triggers alongside API, schedule, document, and external triggers.
-   [Workflow API](/builder/v2/api-reference/workflow-api) — the run, stream, and HITL endpoints that back each chat turn.
-   [AI Employee API](/builder/v2/api-reference/ai-employee-api) — build and publish the chat AI Employee you're conversing with.
