Triggering AI Employees via API
An AI Employee does nothing until something starts a run. This page is the map of every way a run can begin (from a button in the builder to a scheduled job to a document landing in a knowledge base) and the trigger_type each one stamps on the run so you can tell them apart in run history. Every trigger ultimately produces a workflow run; what differs is who or what kicks it off and how the inputs arrive.
Each trigger records its origin on the run. You can filter run history by it: GET /api/v1/workflow/workflows/{id}/runs?trigger_type=schedule. The recorded values are:
trigger_type | Started by |
|---|---|
ui | A person running the AI Employee from the builder or run surface. |
api | A direct call to the run endpoint authenticated with a tenant API key. (A JWT-authenticated call to the same endpoint is recorded as ui.) |
schedule | A recurring schedule firing. |
webhook | An inbound webhook configured on the workflow. |
feedback | A user submitting feedback that runs a feedback workflow. |
document | A document arriving in a knowledge base that a document trigger watches. |
app_public | A run started from a published App's public surface (Frontier App Builder). |
email | A run started by an inbound email to an email-enabled AI Employee. |
All API examples require a JWT or tenant API key and use https://your-tenant.ema.ai as the host.
Run from the API
The most direct trigger is calling the run endpoint yourself. This is the right choice for one-off automation, batch jobs, and integrating an AI Employee into your own backend.
POST https://your-tenant.ema.ai/api/v1/workflow/workflows/{id}/run
X-API-Key: ema_sk_live_2f9c...redacted
Content-Type: application/json
{ "input_params": { "subject": "Order #4821 hasn't shipped" } }
The response is 202 Accepted with the run; the run is stamped trigger_type=api. Follow it with the SSE stream or poll run details. See the Workflow API for the run lifecycle, streaming, and inspection endpoints. For a conversation rather than a single run, use the Chat API.
Run on a schedule
Attach a recurring schedule so the AI Employee runs on its own cadence. Scheduled runs are stamped trigger_type=schedule.
- Create or update:
PUT /api/v1/workflow/workflows/{id}/schedule. - Enable:
POST /api/v1/workflow/workflows/{id}/schedule/enable— requires a published version, otherwise409. - Disable:
POST /api/v1/workflow/workflows/{id}/schedule/disable. - Delete:
DELETE /api/v1/workflow/workflows/{id}/schedule.
A scheduled run carries scheduled_fire_time; if the scheduler skips a fire (for example, because a prior run is still going under an overlap policy), the resulting run records a skip_reason. For the builder-side walkthrough of adding a schedule to a Dashboard AI Employee and monitoring its runs, see Starting an AI Employee Automatically with Triggers.
Run on a new document (document triggers)
A document trigger watches a knowledge base and starts a run whenever a matching document is added, useful for "process every contract that lands here" automations. Each watch is a binding between the workflow and a knowledge-base scope. Runs started this way are stamped trigger_type=document.
Manage bindings under the workflow:
- Create a binding:
POST /api/v1/workflow/workflows/{id}/document-triggers. Returns409if an overlapping binding already exists. - List bindings:
GET /api/v1/workflow/workflows/{id}/document-triggers. - Get / update / delete one:
GET/PUT/DELETE /api/v1/workflow/workflows/{id}/document-triggers/{binding_id}. - Enable / disable:
POST /api/v1/workflow/workflows/{id}/document-triggers/{binding_id}/enableand.../disable.
POST https://your-tenant.ema.ai/api/v1/workflow/workflows/3f7a.../document-triggers
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json
{
"knowledge_base_id": "kb-9f2a...",
"folder_prefix": "contracts/",
"fire_on": "created",
"enabled": true
}
Only knowledge_base_id is required. Narrow the watch with folder_prefix (match documents whose folder path starts with it) and mime_type_allowlist. fire_on defaults to created. A disabled binding stays configured but stops firing; toggle it without deleting and recreating.
Run from an inbound email
An email-enabled AI Employee watches a mailbox and starts a run for each message that arrives. Runs started this way are stamped trigger_type=email. There is no endpoint to call: you connect a mailbox to the AI Employee, and delivery of a message is the trigger.
The run input describes the message envelope and carries a reference to the message itself. These are the fields available as {{workflow_input.*}} references:
| Reference | Type | Description |
|---|---|---|
message_ref | object | A handle to the stored message. Pass this to an Email Agent to read the message content. |
subject | string | The subject line. |
sender | string | The sending address. |
recipients.to | array of strings | The To addresses. |
recipients.cc | array of strings | The Cc addresses. |
received_at | timestamp | When the message was received. |
conversation_id | string | The provider's thread identifier, shared by messages in the same conversation. |
attachments | array of objects | Attachment metadata: name, content type, size, and whether the part is inline. Metadata only, not the file contents. |
outcome_ref | object | A reference the workflow recorded for the external side effect it performed, such as the ticket it filed. A workflow sets this by returning an outcome_ref named output; Ema stores it against the message and supplies it here on any later run of that same message, so a step can see it already acted and skip repeating the side effect. Empty on a first run, and empty if the workflow never records one. |
Reading the message body
The run input does not include the message body, so that a run never depends on carrying a large message. To read the body, add an Email Agent to the workflow and map {{workflow_input.message_ref}} to it. The Email Agent fetches the stored message and emits the parsed content:
| Reference | Type | Description |
|---|---|---|
body.text | string | The plain-text body. |
body.html | string | The HTML body, when the message has one. |
body.snippet | string | A short preview of the body. |
attachments | array of objects | Attachment metadata, including a filename, a content type, a size, and a checksum. |
thread_id | string | The provider's thread identifier. |
A downstream agent then reads the body from the Email Agent rather than from the run input. If the Email Agent is named email_agent, an agent that summarizes the message maps:
{
"summarize": {
"input_mapping": {
"subject": "{{workflow_input.subject}}",
"sender": "{{workflow_input.sender}}",
"body": "{{email_agent.output.body.text}}"
}
}
}
Reference the field names exactly. The run input has no body, from, to, or cc field. Use sender for the sending address, recipients.to and recipients.cc for the addressing arrays, and an Email Agent for the body. A reference to a field the trigger does not produce resolves to nothing, and if the input is marked optional the run still succeeds with that value missing. See Optional inputs.
The trigger and the Email Agent describe addresses differently: the run input gives sender as a plain string and recipients.to as an array of strings, while the Email Agent returns address objects with separate email and display-name fields. Pick one source per field rather than mixing them.
Run from a chat conversation
For a chat-modality AI Employee, each user message triggers a run behind the scenes. You don't call the run endpoint directly; you send a message to a session and the chat service starts the run for you:
POST https://your-tenant.ema.ai/api/v1/chat/sessions/{session_id}/messages
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json
{ "content": "What's the status of my refund?" }
The response includes the run_id; stream it to receive the reply. See the Chat API for the full session-and-message flow.
Run from an external system
Two server-to-server endpoints on the chat service let a trusted external system converse with an AI Employee on behalf of its own end users. Both authenticate with a tenant API key and forward an end-user identity into the run's user_context, so the AI Employee can personalize its answer.
External channel trigger
POST /api/v1/chat/external/trigger is for an external channel (web widget, Slack, Teams) where the end user is identified by a bearer token your system holds. The chat service resolves that token to a user, finds or creates a session, and sends the message with the resolved identity injected.
POST https://your-tenant.ema.ai/api/v1/chat/external/trigger
X-API-Key: ema_sk_live_2f9c...redacted
Content-Type: application/json
{
"bearer_token": "the-end-users-token",
"workflow_id": "3f7a...",
"channel_type": "web",
"message": "Where is my order?"
}
The response is 202 Accepted with message_id, assistant_message_id, run_id, and session_id. Optionally pass conversation_id for continuity across turns and user_context_override to supply the workflow's user_context verbatim.
Partner-API chat
POST /api/v1/chat/external/chat is for trusted partners that supply the end-user identity directly. Ema does not validate it. Authenticate with a tenant API key (X-API-Key); the key's workspace scopes the session.
POST https://your-tenant.ema.ai/api/v1/chat/external/chat
X-API-Key: ema_sk_live_2f9c...redacted
Content-Type: application/json
{
"external_user_id": "partner-user-42",
"workflow_id": "3f7a...",
"message": "Reset my PIN",
"user_context": { "email": "[email protected]", "attributes": { "tier": "gold" } }
}
Identity resolves from external_user_id (recommended) or, if absent, user_context.email; with neither, the call returns 422. Omit conversation_id for a single-shot exchange, or provide one to continue an existing conversation. The endpoint returns 202 immediately and materializes the assistant message asynchronously; poll the run status to retrieve it.
Which external endpoint? Use /external/trigger when you hold a token that identifies the end user and want Ema to resolve it. Use /external/chat when you are a trusted partner asserting the identity yourself and forwarding a user_context directly.
Run from the builder UI
Running an AI Employee from the builder or its run surface produces a run stamped trigger_type=ui. You don't call this trigger from the API — it's how interactive testing and manual runs in the product are recorded. It's listed here so the ui value in run history is unambiguous.
What's next
- Workflow API — the run lifecycle, streaming, run history, and HITL.
- Chat API — sessions and messages, which back the chat and external triggers.
- Authentication — the JWT and tenant API key each trigger requires.
- Starting an AI Employee Automatically with Triggers — set up and monitor a scheduled, webhook, or poll trigger from the AI Employee builder.