Headless API
The custom chat interface lets you connect your genie to any application you build and control. Your application talks to your genie directly over a REST API, called a Headless API, instead of routing users through Slack, Microsoft Teams, or Workato GO. The custom chat interface includes API endpoints to manage conversations and send and receive genie messages. This API is separate from the Agent Studio Developer API, which lets you create, configure, and maintain genies.
HEADLESS API ENDPOINTS REQUIRE A CUSTOM CHAT INTERFACE
Headless API endpoints are only compatible with the custom chat interface option.
PRIVATE BETA
Headless APIs are in private beta and only available to selected customers. Contact your Customer Success Manager to learn more.
Headless API prerequisites
Headless API has the following prerequisites:
- A client role with the following permissions:
- Genies: Read and write permissions for CRUD operations on genies, skills, knowledge bases, user groups, and guardrails
- Genie client: Read and write permissions for CRUD operations on clients and client-genie assignments
- An API client attached to the preceding client role
- A folder ID where the genie is stored. Use the following API endpoint to obtain your folder ID:
curl -s "https://app.workato.com/api/folders" \
-H "Authorization: Bearer $DEV_API_TOKEN"Headless API base URLs
Headless API is available in all data centers where Agentic is supported. The base URLs follow the same per-data-center scheme as the app host. For example:
| Data center | Dev API base URL | Headless API base URL |
|---|---|---|
| US | https://www.workato.com | https://genie-api.workato.com |
Refer to the data center overview for more information.
Authentication
The Headless API provides two access methods that address different integration scenarios. Authentication is enforced at the API gateway layer, with no runtime dependency on the Workato system. Token configuration is synced to the gateway asynchronously and verification is performed in-process. API tokens are per-environment. This means you must have a unique API token for each environment. Refer to How to generate an API token for more information.
Headless API supports the following access methods:
API key:
Authorization: Bearer <api_key>
X-IDP-User-Id: <idp_user_id>OAuth 2.0 (PKCE):
Authorization: Bearer <access_token>Authorization methods comparison
Refer to the following authorization method comparison and use case recommendation tables to determine which authorization method to use:
| Comparison point | API key (token-based) | OAuth 2.0 (PKCE) |
|---|---|---|
| How it works | The builder's backend holds a static API key and asserts user identity through the X-IDP-User-Id header. | End users authenticate directly through Workato Identity and delegate to the customer's IdP through SAML SSO. |
| End user visibility | Workato is invisible to end users. | End users see an IdP login step. |
| Required headers | Authorization: Bearer <api_key> and X-IDP-User-Id: <idp_user_id> | Authorization: Bearer <access_token> |
| Client credentials | The API key is static and is shown only once. | client_id only — PKCE replaces the client secret. |
| User identity managed by | The builder maps user IDs to Workato IdP user IDs. | IdP directly, such as Okta, Azure AD, OneLogin, and more. |
| Prerequisites | End users provisioned in Workato Identity through IAM API or the Workato UI. | One-time SAML federation between Workato Identity and the customer's existing IdP. Workato Identity acts as a broker. It doesn't replace the customer's IdP. |
| Best for | Server-side integrations, QA, automated testing, internal tools. | User-facing apps requiring SSO, centralized access governance, and per-user permissions. |
Recommended authorization method by scenario
| Scenario | Recommended |
|---|---|
| QA / automated testing | API key |
| Security red-teaming through CI/CD | API key |
| Internal tool with builder-controlled authorization | API key |
| Custom chat UI where Workato should be invisible | API key |
| Employee portal with company SSO | OAuth 2.0 |
| Multi-tenant app serving multiple organizations | OAuth 2.0 |
| Deployment requiring IdP-governed access control | OAuth 2.0 |
| Mobile or single-page app or public client | OAuth 2.0 |
Quick reference
List conversations
Retrieve a list of the authenticated user's conversations. Results are listed by the created_at timestamp in descending order.
GET /api/v1/genies/:genie_handle/chat/conversationsURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
Query parameters
| Name | Type | Description |
|---|---|---|
| limit | integer optional | Number of conversations to return. Defaults to 50. |
| cursor | string optional | Pagination cursor from a previous response. |
Response
| Name | Type | Description |
|---|---|---|
| list[] | array | List of conversations. |
| list[].conversation_id | string | Conversation ID. |
| list[].topic | string | Conversation topic. |
| list[].last_updated_at | string | Timestamp of the last update. |
| list[].created_at | string | Timestamp when the conversation was created. |
| total_count | integer | Total number of conversations. |
| cursor | string | Cursor for the next page of results. |
Create a conversation
Create a new conversation with the genie.
POST /api/v1/genies/:genie_handle/chat/conversationsURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
Response
| Name | Type | Description |
|---|---|---|
| conversation_id | string | ID of the new conversation. |
Get a conversation
Retrieve the details and current state of a conversation.
GET /api/v1/genies/:genie_handle/chat/conversations/:conversation_idURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
| conversation_id | string required | Conversation ID. |
Response
| Name | Type | Description |
|---|---|---|
| updated_at | string | Timestamp of the last update. |
| state | string | Current state of the conversation. One of idle, ai_running, skill_processing, or awaiting_approval. |
| last_event | object | The most recent event in the conversation. |
Get messages
Retrieve message history for a conversation. Results are listed by the created_at timestamp in descending order.
GET /api/v1/genies/:genie_handle/chat/conversations/:conversation_id/messagesURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
| conversation_id | string required | Conversation ID. |
Query parameters
| Name | Type | Description |
|---|---|---|
| cursor | string optional | Pagination cursor from a previous response. |
| limit | integer optional | Number of messages to return. Defaults to 100. |
Response
| Name | Type | Description |
|---|---|---|
| conversation_id | string | Conversation ID. |
| messages[] | array | List of messages. |
| messages[].message_id | string | Message ID. |
| messages[].source | string | Message origin. One of user or genie. |
| messages[].content | string | Text content of the message. |
| messages[].genie_run_id | string | ID of the request/response cycle the message belongs to. |
| messages[].created_at | string | Timestamp of the message in RFC3339 format. |
| total_count | integer | Total number of messages in the conversation. |
| cursor | string | Cursor for the next page of results. |
Send a message
Send a user message and receive a real-time stream of events as the genie processes and responds.
POST /api/v1/genies/:genie_handle/chat/conversations/:conversation_id/messagesURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
| conversation_id | string required | Conversation ID. |
Payload
| Name | Type | Description |
|---|---|---|
| message | string required | The user's message. Maximum 12 KB. |
| file_id | string optional | ID of a previously uploaded file to attach to this message. |
| stream | boolean optional | Set to true to receive an SSE stream in the response. Defaults to false. |
Response
When stream is false, returns HTTP 202 with the following body:
| Name | Type | Description |
|---|---|---|
| conversation_id | string | Conversation ID. |
| genie_run_id | string | ID of the genie run. Use this to reconnect to the stream. |
When stream is true, the response is a stream of Server-Sent Events. The stream closes when the genie finishes processing, indicated by the processing.finished event.
Reconnect to a stream
Reopen the SSE stream for a specific genie run. Use this to recover events after a disconnection. Pass the ID of the last successfully received event in the Last-Event-ID header.
GET /api/v1/genies/:genie_handle/chat/conversations/:conversation_id/genie-runs/:genie_run_idURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
| conversation_id | string required | Conversation ID. |
| genie_run_id | string required | Genie run ID, as returned by Send a message. |
Headers
| Name | Type | Description |
|---|---|---|
| Last-Event-ID | string optional | ID of the last successfully received event. The stream replays events from this point. |
Response
The response is a stream of Server-Sent Events.
Get events
Retrieve recent events. Use this as a fallback to manually fetch missed events after a disconnection. Events are returned oldest first and are available for 24 hours.
GET /api/v1/genies/:genie_handle/chat/conversations/eventsURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
Query parameters
| Name | Type | Description |
|---|---|---|
| since_created_at | string optional | Lower bound (inclusive) on event creation time, as an RFC3339 timestamp (for example, 2026-05-26T12:00:00.123456Z). Pass next_since_created_at from the previous response to fetch the next page. |
| conversation_id | string optional | Filter events by conversation ID. |
| limit | integer optional | Page size. Defaults to 100. |
Response
| Name | Type | Description |
|---|---|---|
| events[] | array | Array of events, ordered oldest first. |
| events[].conversation_id | string | Conversation ID. |
| events[].genie_handle | string | Genie handle (string identifier, for example gin-AaDX9axF-CNtgQn-B6). |
| events[].genie_run_id | string | ID of the genie run this event belongs to. |
| events[].type | string | Event type. |
| events[].event_id | string | UUIDv7 identifying this event. |
| events[].seq_num | integer | Per-genie-run monotonic sequence number. |
| events[].created_at | string | Gateway-side timestamp when the event was persisted, in RFC3339 format. |
| next_since_created_at | string | Continuation cursor. When present, pass this value as since_created_at on the next request to fetch the next page. |
Approve or reject a skill
Approve or reject a skill confirmation request. Use this when the genie emits a skill.confirmation_required event.
POST /api/v1/genies/:genie_handle/chat/conversations/:conversation_id/skill_approval/:call_idURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
| conversation_id | string required | Conversation ID. |
| call_id | string required | Call ID from the skill.confirmation_required event. |
Payload
| Name | Type | Description |
|---|---|---|
| resolution | string required | One of approved or rejected. |
| rejection_reason | string optional | Reason for rejection. Only used when resolution is rejected. |
Get a runtime connection link
Get an authentication link for a runtime connection. Use this when the genie emits a runtime_connection.auth_required event.
POST /api/v1/genies/:genie_handle/chat/runtime_connection/:runtime_connection_attempt_id/linkURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
| runtime_connection_attempt_id | string required | Server-generated ID for the pending runtime-connection authorization attempt, as delivered in the runtime_connection.auth_required SSE event. |
Response
| Name | Type | Description |
|---|---|---|
| status | string | One of auth_required or authorized. When authorized, the connection is already complete and no user action is needed. |
| auth_link.url | string | Authentication URL to present to the user. Only present when status is auth_required. |
| auth_link.expires_at | string | Expiration time of the authentication URL in RFC3339 format. Only present when status is auth_required. |
| auth_link.connector_name | string | Human-readable connector label (for example, Salesforce, Google Drive). Only present when status is auth_required. |
Reject a runtime connection
Reject a runtime connection request.
POST /api/v1/genies/:genie_handle/chat/runtime_connection/:runtime_connection_attempt_id/rejectURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
| runtime_connection_attempt_id | string required | Server-generated ID for the pending runtime-connection authorization attempt, as delivered in the runtime_connection.auth_required SSE event. |
Payload
| Name | Type | Description |
|---|---|---|
| reason | string optional | Reason for rejection. |
Upload a file
Upload a file to attach to a subsequent message. Returns a file_id to pass in the file_id parameter of Send a message.
POST /api/v1/genies/:genie_handle/chat/conversations/:conversation_id/uploadURL parameters
| Name | Type | Description |
|---|---|---|
| genie_handle | string required | Genie handle. |
| conversation_id | string required | Conversation ID. |
Payload
| Name | Type | Description |
|---|---|---|
| file | file required | File data (multipart/form-data). |
Response
| Name | Type | Description |
|---|---|---|
| file_id | string | ID of the uploaded file. |
SSE events
You can send a message with stream: true to get a response with a stream of SSE covering the full lifecycle of the genie's response.
Events are persisted for 24 hours and are retrievable through the Get events endpoint.
Base event shape
Every event shares the following base fields:
| Field | Type | Description |
|---|---|---|
conversation_id | string | Conversation ID. |
genie_handle | string | Genie handle (string identifier, for example gin-AaDX9axF-CNtgQn-B6). |
genie_run_id | string | ID of the genie run this event belongs to. |
type | string | Event type. |
event_id | string | UUIDv7 identifying this event. |
seq_num | integer | Per-genie-run monotonic sequence number. |
created_at | string | Gateway-side timestamp when the event was persisted, in RFC3339 format. |
Event types
| Event | Additional fields | Description |
|---|---|---|
processing.started | — | Genie has begun processing the request. |
processing.finished | — | Genie has completed processing. |
agent.message |
| The genie's response message. |
skill.running |
| A skill has started executing. |
skill.completed |
| A skill completed successfully. |
skill.failed |
| A skill execution failed. |
skill.confirmation_required |
| A skill requires user confirmation before executing. Use call_id with Approve or reject a skill. |
runtime_connection.auth_required |
| A skill requires the user to authenticate a connection. Use runtime_connection_attempt_id with Get a runtime connection link. |
runtime_connection.auth_success | — | Runtime connection authentication succeeded. |
runtime_connection.auth_failed |
| Runtime connection authentication failed. |
Limitations
Headless API endpoints have the following limitations:
- Action Board isn't supported
- App Events aren't supported
Last updated: