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_typeStarted by
uiA person running the AI Employee from the builder or run surface.
apiA direct call to the run endpoint authenticated with a tenant API key. (A JWT-authenticated call to the same endpoint is recorded as ui.)
scheduleA recurring schedule firing.
webhookAn inbound webhook configured on the workflow.
feedbackA user submitting feedback that runs a feedback workflow.
documentA document arriving in a knowledge base that a document trigger watches.
app_publicA run started from a published App's public surface (Frontier App Builder).
emailA 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, otherwise 409.
  • 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. Returns 409 if 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}/enable and .../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:

ReferenceTypeDescription
message_refobjectA handle to the stored message. Pass this to an Email Agent to read the message content.
subjectstringThe subject line.
senderstringThe sending address.
recipients.toarray of stringsThe To addresses.
recipients.ccarray of stringsThe Cc addresses.
received_attimestampWhen the message was received.
conversation_idstringThe provider's thread identifier, shared by messages in the same conversation.
attachmentsarray of objectsAttachment metadata: name, content type, size, and whether the part is inline. Metadata only, not the file contents.
outcome_refobjectA 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:

ReferenceTypeDescription
body.textstringThe plain-text body.
body.htmlstringThe HTML body, when the message has one.
body.snippetstringA short preview of the body.
attachmentsarray of objectsAttachment metadata, including a filename, a content type, a size, and a checksum.
thread_idstringThe 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

Last updated: Aug 27, 2026