Embed Integration API
Agent chat and workflow execution endpoints for third-party websites and iframe embedding
The Embed Integration API enables embedding Clouisle Agent conversations and workflow executions into third-party portals, iframe widgets, or cookie-less client applications. The base path is /api/v1/embed.
Authentication & Security Policies
All embed endpoints exclusively require API Key authentication (prefixed with clou_). Cookie sessions are not accepted. Credentials can be passed using either of the following mechanisms:
- Header:
Authorization: Bearer clou_... - Query Parameter:
?token=clou_...
Resolution order is the token query parameter first, then the Authorization header; both must start with clou_, otherwise the request is treated as missing credentials and returns 401 (code 2000, embed_api_key_required).
Beyond authentication, the target must be usable for embedding: an Agent or Workflow must be in published status and have embed_config.enabled = true, otherwise the API returns 404 (agent_not_found / workflow_not_found) or 403 (embed_not_enabled).
Browser Embed Pages
Alongside the REST namespace, the product ships two pages you can drop straight into an <iframe> (these do not use the /api/v1 prefix):
| Page | Purpose | Query Parameters |
|---|---|---|
/embed/agent/{agent_id} | Full sign-in-free Agent chat UI (reuses the public chat page) | token=clou_... (required), mode=fullscreen or bubble (default fullscreen) |
/embed/workflow/{workflow_id} | Workflow run UI (reuses the run-detail page) | token=clou_... (required) |
<iframe
src="https://your-domain.com/embed/agent/550e8400-e29b-41d4-a716-446655440000?token=clou_xxx&mode=bubble"
style="width: 420px; height: 640px; border: 0"
></iframe>To avoid exposing the API key in the URL, the pages accept credentials over postMessage: after loading, the iframe posts { "type": "clouisle:ready" } to the parent, which replies with { "type": "clouisle:token", "token": "clou_..." }. The token query parameter still works as a fallback; with neither the Agent page stays in its loading state and the workflow page shows invalidToken.
The iframe also emits two events so the host page can react: { "type": "clouisle:conversation", "conversationId": "..." } when a conversation is created/switched, and { "type": "clouisle:close" } when the user clicks close.
Embed-mode limits: conversation history lives in browser localStorage (key clouisle:embed:history:agent:{agent_id}) rather than a login session, and message editing, regeneration, and version switching are unavailable in embed mode (the frontend disables them).
Domain Whitelist Verification (allowed_domains)
When an Agent or Workflow has allowed_domains configured in its embed_config:
- The server checks the
OriginorRefererrequest header against the allowed domains list (supports wildcard subdomains such as*.example.com). - If the request origin does not match the whitelist, the server returns
403 Forbidden(code: 3000,embed_domain_not_allowed). - Direct requests with no origin header or items with empty whitelists are allowed.
- Workflow event streams (
GET /workflows/runs/{run_id}/stream) are exempt from origin verification.
Agent Embed Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/embed/agents/{agent_id}/info | Retrieve public metadata, config, and variables for the embed widget |
| POST | /api/v1/embed/agents/{agent_id}/chat/stream | Stream chat message via Server-Sent Events (SSE) |
| POST | /api/v1/embed/agents/{agent_id}/chat/runs | Create a durable async Agent run (returns 202 Accepted) |
| GET | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/stream | Subscribe to replay and live SSE events of a durable run |
| GET | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id} | Query durable run status |
| GET | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/events | Replay buffered run events by sequence |
| POST | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/inputs | Send steering or follow-up inputs during execution |
| POST | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/answers | Submit structured answers required by ask_user tool calls |
| POST | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/stop | Collaboratively terminate an active run |
| GET | /api/v1/embed/agents/{agent_id}/conversations/{conversation_id}/messages | Get message history of an embed conversation |
| POST | /api/v1/embed/agents/{agent_id}/upload/file | Upload attachment files for embed sessions |
1. Get Embed Agent Info
GET /api/v1/embed/agents/{agent_id}/info HTTP/1.1
Authorization: Bearer clou_api_keyResponse (200 OK)
{
"code": 0,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Support Assistant",
"description": "Answers customer questions regarding returns and billing",
"icon": "🤖",
"avatar_url": null,
"opening_message": "Hello! How can I assist you today?",
"suggested_questions": [
"How do I request a refund?",
"Where can I find my invoice?"
],
"variables": [],
"enable_attachments": true,
"attachment_config": {
"max_file_count": 5,
"max_file_size_mb": 10
},
"hide_tool_calls": false,
"hide_message_actions": false,
"hide_reasoning": false,
"embed_config": {
"enabled": true,
"allowed_domains": ["https://example.com"]
}
},
"msg": "success"
}2. Stream Chat Message
POST /api/v1/embed/agents/{agent_id}/chat/stream HTTP/1.1
Authorization: Bearer clou_api_key
Content-Type: application/json
{
"message": "What is the return window?",
"conversation_id": "conv-uuid-optional",
"variables": {}
}Returns a text/event-stream SSE response whose events are identical to the main chat stream endpoint (message_start, content_delta, rag_start/rag_context, reasoning_start/reasoning_delta/reasoning_end, tool_call/tool_result, media_result, compression_start/compression_end, output_truncated, iteration_cap_reached, message_end, error) — see SSE Streaming Events.
3. Create Durable Run
For disconnected resilience and long-running tools, use the durable run workflow:
POST /api/v1/embed/agents/{agent_id}/chat/runs HTTP/1.1
Authorization: Bearer clou_api_key
Content-Type: application/json
{
"message": "Generate Q3 sales analysis",
"conversation_id": null,
"variables": {}
}Response (202 Accepted)
{
"code": 0,
"data": {
"run_id": "run-uuid-1234",
"conversation_id": "conv-uuid-5678",
"status": "queued",
"stream_url": "/embed/agents/550e8400-e29b-41d4-a716-446655440000/chat/runs/run-uuid-1234/stream"
},
"msg": "success"
}Clients subscribe to GET /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/stream?after_sequence=N to stream events with deduplication and reconnection capability.
4. Run Control and Query Parameters
| Method | Path | Query Parameters / Body |
|---|---|---|
| GET | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/stream | after_sequence (default 0; negative clamped to 0) — replays buffered events with a higher sequence, then follows the live stream |
| GET | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/events | after_sequence (default 0) — non-streaming read of buffered events |
| GET | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id} | none — returns RunOut (status, error_code, pending_tool_*, …) |
| POST | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/inputs | JSON: delivery (steer/follow_up/auto, default auto), content, attachments, request_id (idempotency key) |
| POST | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/answers | JSON: tool_call_id (required), answers (ID → answer), skipped |
| POST | /api/v1/embed/agents/{agent_id}/chat/runs/{run_id}/stop | no body — cooperatively stops the run; the stream then receives run_end with status: "stopped" |
| GET | /api/v1/embed/agents/{agent_id}/conversations/{conversation_id}/messages | none — returns the conversation's visible messages (each with version_count); the conversation must belong to the API key's user, otherwise 404 (6210) |
| POST | /api/v1/embed/agents/{agent_id}/upload/file | multipart/form-data with field name file and query parameter category (default documents) |
Run status values, the single run_end, and the resumption strategy are the same as the main chat API — see Durable Runs and Control in the Agent Chat API and SSE Streaming Events.
Workflow Embed Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/embed/workflows/{workflow_id}/info | Retrieve workflow embed details and input schema |
| POST | /api/v1/embed/workflows/{workflow_id}/run | Execute an embedded workflow |
| GET | /api/v1/embed/workflows/runs/{run_id}/stream | Stream workflow node execution progress via SSE |
1. Run Workflow
POST /api/v1/embed/workflows/{workflow_id}/run HTTP/1.1
Authorization: Bearer clou_api_key
Content-Type: application/json
{
"inputs": {
"query": "Summarize customer feedback",
"format": "markdown"
}
}Response (200 OK)
{
"code": 0,
"data": {
"run_id": "wf-run-uuid-1234",
"stream_url": "/api/v1/workflows/runs/wf-run-uuid-1234/stream"
},
"msg": "success"
}The returned stream_url points at the main workflow stream route (/api/v1/workflows/runs/{run_id}/stream), which targets signed-in sessions; embed clients should instead subscribe to GET /api/v1/embed/workflows/runs/{run_id}/stream?from_sequence=N.
2. Stream Workflow Run Progress
GET /api/v1/embed/workflows/runs/{run_id}/stream?from_sequence=0| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
from_sequence | integer | No | 0 | Replay events after this sequence, then follow live events; used to resume after a disconnect |
Note the naming difference from Agent runs: workflow streams use from_sequence, Agent runs use after_sequence. This route does not perform the domain whitelist check (every other workflow embed endpoint does), but it does require the run to have been triggered by the API key's user, otherwise it returns 404 (workflow_run_not_found).
3. Workflow Embed Info
GET /api/v1/embed/workflows/{workflow_id}/infoReturns id, name, description, icon, variables (input parameter definitions), and embed_config. The workflow must be published with embedding enabled, otherwise the call returns 404 (workflow_not_found) or 403 (embed_not_enabled).
Error Codes
| Error Code | Identifier | Description |
|---|---|---|
2000 | UNAUTHORIZED | Missing API key, credentials without the clou_ prefix, or an invalid/expired key (embed_api_key_required) |
3000 | PERMISSION_DENIED | Embedding not enabled (embed_not_enabled), or the request origin is not in allowed_domains (embed_domain_not_allowed) |
4000 | NOT_FOUND | Workflow does not exist or is not published (workflow_not_found); workflow run does not exist or was not triggered by this API key's user (workflow_run_not_found) |
6200 | AGENT_NOT_FOUND | Agent does not exist or is not published (agent_not_found) |
6210 | CONVERSATION_NOT_FOUND | Conversation does not exist or does not belong to the API key's user |
How is this guide?