Skip to main content

Conversational interface via a REST API

Build chat widgets and apps on a thunk workflow using the conversational REST API or MCP server

Conversational interface via a REST API

Use the conversational REST API when you want to build a chat-style experience (a widget, mobile app, partner portal, or internal tool) on top of an existing thunk workflow. The integration uses the same workflow work item and conversation thread the agent already understands — without email. The same API is also available as an MCP server for MCP clients.

This is different from Inbound requests via a REST API, which starts structured workflow work items. The conversational API is for turn-by-turn chat on a workflow that has a conversation input.

Precondition. The thunk workflow must have exactly one conversation-type input property. Without that, the chat UI and conversational REST API are not available for the thunk.

What you can do

Typical integrator flow:

  1. Confirm the workflow has exactly one conversation input property

  2. Create a thunk API key (same keys as the workflow REST API)

  3. Open the OpenAPI docs for this thunk’s conversational endpoints, or copy the MCP server URL

  4. Start a conversation (first user message) and keep the returned conversation id

  5. Send follow-up messages — either wait for the reply in the same request, or poll messages / subscribe to the event stream

  6. End the workflow when the conversation is finished (POST /{convoId}/end, or End conversation in the hosted Chat UI)

Optional: attach files on a turn (when User file attachments is enabled on the conversation property), upload files for structured file inputs before start, and (when Structured data is enabled on the conversation property) register structured-data schemas the assistant may return with messages.

1: Confirm the conversation input

In the thunk designer, open the workflow structure and check that there is exactly one input property whose type is Conversation. That property is the chat channel for each workflow work item.

If you need a quick product check: go to Run → Inbound Requests → Chat UI. When the conversation input is present, the Chat UI button is enabled and opens the hosted chat app for the thunk. When it is missing, the Chat UI button stays disabled and an info banner explains that the chat UI is enabled only if the workflow has a conversation input property.

Steps that should not reply to the end user

Chat workflows often include steps that do internal work — looking up an order, calling a business system, or updating structured fields — without talking to the person in the chat.

For those steps, open the step’s AI Instructions and remove the end-user conversation property (typically EndUserConvo) from Output properties. When that conversation is not listed under Output properties, the AI agent will not message the end user on that step. Later steps can still reply if they list the conversation in their Output properties.

Keep the conversation property in Output properties on steps that should greet the user, ask a question, share a status update, or otherwise reply in the chat. You may still list the conversation under Input properties if the step needs to read the thread without sending a message.

This is separate from Conversation property settings such as AI messaging, Disable replies, Message writing instructions, AI file attachments, User file attachments, or Step error message on a Conversation property.

Conversation property settings (chat)

Open the conversation input property in the workflow structure to control what the AI and end users can do in the chat:

  • AI file attachments — when enabled, the AI agent can attach files to outgoing messages.

  • User file attachments — when enabled (the default), end users can attach files in the hosted Chat UI and through the conversational REST API (POST /uploadFiles, and attachments on POST /start, POST /…/sendMessage, or POST /…/sendMessageAndWait). When disabled, the attach control is hidden in the Chat UI and those API calls return an error: "File attachments are not supported on this conversation".

  • Structured data — when enabled, integrators can register JSON schemas the assistant may return with messages (see below).

  • Step error message — optional text sent to the end user if a step that lists this conversation under Output properties ends with an error. Leave blank to use the default: There was an error handling your message. Steps that do not list the conversation under Output properties do not send it.

Use GET /info at the API root to read structuredDataEnabled and userFileAttachmentsEnabled for the thunk.

2: Create an API key

API keys are shared with the structured workflow REST API. They are not created on the Chat UI tab.

  1. Go to Run → Inbound Requests → API Channel

  2. Open API Keys

  3. Create a key and copy it immediately — it is shown only once

For screenshots and revoke steps, see Inbound requests via a REST API (Generate an API Key).

Keys use the thk_… prefix. Send them to the public gateway as:

X-API-Key: {api-key}

Base URL for customer integrations:

https://postern.thunk.ai/api/thunk/{thunk_id}/convo

3: Open the OpenAPI documentation

Each thunk exposes its own conversational OpenAPI document because optional data fields on start match that thunk’s workflow inputs.

  1. Go to Run → Inbound Requests → Chat UI

  2. Click View OpenAPI Documentation

That opens Swagger UI for this thunk’s conversational endpoints (paths such as /start, /{convoId}/sendMessage, /{convoId}/sendMessageAndWait, /{convoId}/messages, /{convoId}/end, and /{convoId}/events).

Authorize in Swagger is not your thunk API key. The OpenAPI Authorize control expects a Bearer JWT (a product session token). Use that only for interactive try-it-out. For real integrations through postern.thunk.ai, send X-API-Key: thk_… on the request — do not paste the thunk API key into the Bearer Authorize dialog. (This differs from the workflow REST OpenAPI docs, where Authorize accepts the API key.)

Connect as an MCP server

The same conversational API is also an MCP server, so an MCP client (Claude Desktop, another agent, or an integration host) can call the tools without using REST.

On Run → Inbound Requests → Chat UI, copy MCP Server URL. It looks like:

https://postern.thunk.ai/api/thunk/{thunk_id}/convo/mcp

Authenticate the same way as REST: send X-API-Key: {api-key} on each request.

The tools match the REST routes: start, send_message, send_message_and_wait, list_messages, get_message, get_convo_info, get_api_info, upload_files, get_structured_data_schemas, set_structured_data_schemas, and end_conversation. Conversation ids that REST puts in the path are tool arguments (convoId).

There is no event-stream tool. To send a follow-up and wait for the assistant in one call, use send_message_and_wait with timeoutMs (at most five minutes). You can still use send_message and poll get_convo_info until agentWorking is false, then list_messages (optionally with since). The REST GET /{convoId}/events stream is still available if you want push updates from HTTP.

4: Start a conversation

POST /start creates the workflow conversation and sends the first user message. The response includes a convoId used for every later call.

Minimal shape:

{
  "conversation": {
    "type": "message",
    "message": "Hello — I need help with my request."
  }
}

You may also send:

  • data — other workflow input fields (not the conversation property)

  • name — label for the new conversation (optional; server assigns a default if omitted)

  • displayName — label for the human on this turn (optional)

  • conversation.attachments — optional file attachments on the first message (see below)

Example with curl:

curl -X POST \
  'https://postern.thunk.ai/api/thunk/{thunk_id}/convo/start' \
  -H 'accept: application/json' \
  -H 'X-API-Key: {api-key}' \
  -H 'Content-Type: application/json' \
  -d '{
    "conversation": {
      "type": "message",
      "message": "Hello — I need help with my request."
    }
  }'

A successful response looks like:

{
  "convoId": "{conversation-id}"
}

5: Send messages and read replies

Send a follow-up

POST /{convoId}/sendMessage stores the user turn and returns a messageId:

curl -X POST \
  'https://postern.thunk.ai/api/thunk/{thunk_id}/convo/{convoId}/sendMessage' \
  -H 'accept: application/json' \
  -H 'X-API-Key: {api-key}' \
  -H 'Content-Type: application/json' \
  -d '{
    "message": "Here are more details."
  }'

Optional fields: displayName, attachments.

Send and wait for the reply

POST /{convoId}/sendMessageAndWait stores the user turn and waits until the assistant has replied and is idle, or until timeoutMs (required, at most five minutes). The response is HTTP 200 in both cases:

curl -X POST \
  'https://postern.thunk.ai/api/thunk/{thunk_id}/convo/{convoId}/sendMessageAndWait' \
  -H 'accept: application/json' \
  -H 'X-API-Key: {api-key}' \
  -H 'Content-Type: application/json' \
  -d '{
    "message": "Here are more details.",
    "timeoutMs": 60000
  }'

A successful response looks like:

{
  "userMessageId": "{message-id}",
  "status": "replied",
  "messages": [
    {
      "id": "{assistant-message-id}",
      "content": "Thanks — I can help with that.",
      "author": "assistant",
      "createdOn": "2026-09-11T00:00:00.000Z"
    }
  ]
}

status is replied when the assistant posted at least one new message and went idle within the timeout, or timedOut otherwise. messages are only the new assistant messages from this turn. A timeout does not end the conversation — the user message is stored, and the assistant may still reply later. You can then poll GET /{convoId}/messages or GET /{convoId}/info. Optional fields are the same as sendMessage: displayName, attachments.

List messages

GET /{convoId}/messages returns a page of messages in chronological order (oldest first within the page). The first call (no pageToken) returns the most recent page; use pageToken / nextPageToken for older history.

Useful query parameters:

  • limit — page size

  • pageToken — pass the previous response’s nextPageToken to continue

  • since — only messages after this ISO timestamp (exclusive); use the same since on every call that includes pageToken

curl -X GET \
  'https://postern.thunk.ai/api/thunk/{thunk_id}/convo/{convoId}/messages' \
  -H 'accept: application/json' \
  -H 'X-API-Key: {api-key}'

You can also fetch one message with GET /{convoId}/message/{messageId}, or check conversation state with GET /{convoId}/info (agentWorking and ended).

Live updates (SSE)

GET /{convoId}/events is a long-lived Server-Sent Events stream. After a newMessage event, call GET /messages (optionally with since) to load the text. The stream also emits agentStatus when the assistant starts or stops working. When the assistant stops (agentWorking is false), the event includes respondingToMessageId — the id of the most recent user message in the conversation, the same id you get from GET /messages. That is the turn the assistant was responding to. Periodic heartbeat keepalives are safe to ignore.

6. End the workflow

When the chat is finished — the end user is done, or your app no longer needs automation — call POST /{convoId}/end. This stops the agent, skips any active and remaining workflow steps (recording an optional reason), and marks the work item complete.

Optional body:

{
  "reason": "Customer ended the chat"
}

If you omit reason, the server uses "Conversation ended".

Example:

curl -X POST \
  'https://postern.thunk.ai/api/thunk/{thunk_id}/convo/{convoId}/end' \
  -H 'accept: application/json' \
  -H 'X-API-Key: {api-key}' \
  -H 'Content-Type: application/json' \
  -d '{
    "reason": "Customer ended the chat"
  }'

A successful response looks like:

{
  "convoId": "{conversation-id}",
  "ended": true
}

If the workflow was already ended, the server returns 409 with { "message": "Conversation already ended" }.

Poll GET /{convoId}/info to read ended (and agentWorking) without listing messages.

On the hosted Chat UI (Run → Inbound Requests → Chat UI), end users can click End conversation above the message box for the same effect.

Attachments and file inputs

Message attachments. When User file attachments is enabled on the conversation property, you can send files on POST /start (conversation.attachments), POST /…/sendMessage (attachments), or POST /…/sendMessageAndWait (attachments). Each item is { "name", "data" } where data is a base64 data: URL (the same string browsers produce from FileReader.readAsDataURL). Limits: up to 5 files and 10 MiB total decoded payload per message. When you list messages, attachments include a short-lived contentPointer download URL.

If User file attachments is disabled, message attachments and POST /uploadFiles are rejected with "File attachments are not supported on this conversation". This setting is independent of AI file attachments, which controls whether the AI agent can attach files to its own outgoing messages.

Structured file inputs on start. If the workflow’s non-conversation inputs include file fields, upload first with POST /uploadFiles (requires User file attachments), then put the returned url values into data on POST /start (do not embed large base64 blobs in the start body for those fields).

Structured data (optional)

If the conversation input property has structured data enabled, you can register JSON schemas (on POST /start or via /{convoId}/structuredDataSchemas) describing payloads the assistant may attach to messages. Listed assistant messages may then include a structuredData array. Use GET /info at the API root to see whether structuredDataEnabled and userFileAttachmentsEnabled are true for the thunk. Details and schemas are in the thunk’s OpenAPI document.

Choosing conversational vs workflow REST

Goal

Use

Chat turns on a workflow conversation

This article (conversational REST API or MCP server)

Create / poll structured workflow work items

Did this answer your question?