> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jelou.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Run workflows from an external agent

> Connect an agent that runs outside Jelou (Copilot Studio, Amazon Bedrock Agents, your own runtime or any tool-calling LLM) so it can run published workflows, such as onboarding or account opening, step by step and without exposing personal data.

An **external agent** is an AI agent that runs outside Jelou: Microsoft Copilot Studio, Amazon Bedrock Agents, your own runtime or any model with tool calling. This guide shows how that agent can **start workflows published in Brain Studio**, such as an onboarding or an account opening that asks for an ID number, email and address, and let the workflow lead the conversation with the customer until it finishes.

> [!NOTE]
>
> In this guide, **skill** and **workflow** mean the same thing: a published flow in your project that can be run from outside.

## How it works

Your agent decides **when** to run a workflow. The workflow decides **what to ask** and collects the data.

**1. The customer writes to your agent**

For example: *"I want to open a savings account"*.

**2. Your agent starts the workflow**

It calls Jelou over MCP or the REST API with the customer's identifier. Jelou replies with an `executionId` and runs the workflow in the background.

**3. The workflow talks to the customer**

The workflow sends its messages to the customer's WhatsApp: it asks for the ID number, shows buttons or opens a form. **Jelou routes the customer's replies to the workflow.** Meanwhile, your agent doesn't reply.

**4. The workflow ends and the conversation returns to your agent**

Once the execution reaches a final state (completed, failed or cancelled), your agent takes the conversation back.

![Sequence diagram: the customer writes to your agent, your agent starts the workflow in Jelou and receives an executionId, the workflow asks for the ID number and opens a secure form while your agent stays silent, and when it finishes the conversation returns to your agent](https://mintcdn.com/jelouai/RsS2oQfVkEw3sI-F/assets/images/agentes-ia/agentes-externos-flujo_en.png?fit=max\&auto=format\&n=RsS2oQfVkEw3sI-F\&q=85\&s=be08ac7e62f2b891f9d1157cab19574e)

> [!WARNING]
>
> The most common mistake is an agent that keeps replying while the workflow waits for the customer's answer. The customer gets two replies at once and the workflow loses the answer it was waiting for. While a workflow is active for a customer, your agent must stay silent.

## Choose how to connect

| Option | Use it if… | What your agent gets |
| - | - | - |
| **MCP** | Your agent speaks MCP: Copilot Studio, Amazon Bedrock Agents, Claude, OpenAI Agents, etc. | Each published workflow appears as a tool. The call waits until the workflow needs an answer or finishes. |
| **REST API** | Any backend or agent that makes HTTP calls. | You start the workflow with one call and check its result by `executionId`. |
| **Custom channel** | Your app **is** the channel (web, mobile app) and you don't use WhatsApp. | Jelou sends the workflow's messages to your `webhookUrl`. See [Custom channel](https://docs.jelou.ai/en/api/custom-channel/introduction.md). |

The MCP and REST API options in this guide are for **WhatsApp** conversations.

## Requirements, from zero

**1. A published workflow**

Build and publish the workflow in Brain Studio (for example, *Account opening*). Test it first from the bot's own WhatsApp to confirm it works on its own.

**2. A WhatsApp channel connected to the project**

The workflow sends its messages through the project's WhatsApp bot. Write down that channel's **`botId`**.

**3. An API key with the right scopes**

Create a key in [API keys](https://docs.jelou.ai/en/guides/configuracion/claves-api.md) with these scopes:

| Scope | For |
| - | - |
| `workflows:read` | Listing the project's workflows |
| `workflows:execute` | Starting workflows (and using MCP) |
| `executions:read` | Checking an execution's result |

Always call through `https://gateway.jelou.ai`: that's where the key's scopes are enforced. The key goes in the `x-api-key` header.

**4. The identifiers**

* **`brainId`**: the project's identifier.
* **`skillId`**: the workflow's identifier (you get it when listing workflows).
* **`referenceId`**: the customer's WhatsApp identifier, usually their number **without `+`** (for example, `50712345678`).

**5. Get the customer's messages to your agent**

If your agent is the one answering the bot's WhatsApp, Jelou needs to forward that bot's incoming messages to it. The Jelou team sets this redirect up: request it with the bot and your agent's URL. While a workflow is active, your agent hands each customer message back to Jelou **without replying**, so it reaches the workflow. The Jelou team tells you how to hand them back when setting up the redirect.

## Option A: MCP

Connect your agent as an MCP client to your project's workflow server:

```
https://gateway.jelou.ai/workflows/mcp/{brainId}
```

Authenticate with the `x-api-key` header. The server exposes:

* **One tool per published workflow** in the project, with the workflow's inputs as parameters and a `_context` field for the customer.
* **`workflow_reply`**, to pass the customer's answer to the workflow.

### Step by step

**1. Start the workflow**

Call the workflow's tool with the customer in `_context`:

```json theme={null}
{
  "_context": { "referenceId": "50712345678", "channel": "WHATSAPP" }
}
```

The call **waits** until the workflow needs an answer from the customer or finishes, for up to about 120 seconds. The response looks like this:

```json theme={null}
{
  "status": "pending_input",
  "executionId": "8f2c…",
  "outbox": [ { "type": "TEXT", "text": "To get started, could you share your ID number?" } ],
  "cursor": 1
}
```

**2. Don't repeat the outbox**

`outbox` holds the messages the workflow **already delivered** to the customer on WhatsApp. Your agent reads them to know what was said, but **doesn't send them again** or paraphrase them.

**3. Wait for the customer's real answer**

If `status` is `pending_input`, wait for the customer's next message and pass it through unchanged:

```json theme={null}
{ "text": "8-123-4567", "_context": { "referenceId": "50712345678" } }
```

Never invent answers or fill in data on the customer's behalf. The server finds the active execution from the `referenceId`, so you don't need to send the `executionId`.

**4. Repeat until a final state**

| `status` | What to do |
| - | - |
| `pending_input` | Wait for the customer's answer and call `workflow_reply(text)` |
| `running` | The workflow is still processing: call `workflow_reply` with `text: ""` to keep waiting |
| `completed` / `failed` / `cancelled` | Final. The conversation returns to your agent |

> [!NOTE]
>
> If the response reports a conflict (`claim.conflict`, `thread_busy`), there's already an execution in progress for that customer. **Don't retry or start another one.** Wait for the current one to finish.

## Option B: REST API

**1. List the project's workflows**

```bash theme={null}
curl "https://gateway.jelou.ai/workflows/v1/skills?brainId=YOUR_BRAIN_ID&limit=100" \
  -H "x-api-key: YOUR_API_KEY"
```

Use each workflow's `id` as the `skillId`. Each workflow's description is what your agent should read to decide when to use it.

**2. Start the workflow**

```bash theme={null}
curl -X POST "https://gateway.jelou.ai/workflows/v2/skills/YOUR_SKILL_ID/execute" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "channel": "WHATSAPP",
    "origin": "API",
    "botId": "YOUR_BOT_ID",
    "referenceId": "50712345678",
    "message": { "type": "text", "text": "I want to open a savings account" }
  }'
```

**channel** `string` *required*

The conversation's channel. Use `WHATSAPP`.

**origin** `string` *required*

Where the execution comes from. Use `API`.

**botId** `string` *required*

The project's WhatsApp bot. With `botId` and `referenceId`, Jelou loads the real customer and the workflow writes to them.

**referenceId** `string` *required*

The customer's WhatsApp number without `+`.

**message** `object`

What the customer asked for, in their own words. **Don't include personal data here** (see [Personal data](#personal-data)).

Response:

```json theme={null}
{
  "status": "success",
  "message": "…",
  "data": { "executionId": "8f2c…" }
}
```

> [!WARNING]
>
> This call has no idempotency key. If it fails with a 5xx error, **don't retry it automatically**: the execution may have started, and a retry would open a second request for the same customer. Check the result with the `executionId` first if you have it, or tell the customer about the error.

**3. Let the workflow lead**

Jelou sends the workflow's messages to the customer's WhatsApp and **routes their replies to the workflow**. Your agent doesn't forward them through the API. While the workflow is active:

* don't reply to the customer's messages;
* hand them back to Jelou unchanged (see the last step of [Requirements](#requirements-from-zero));
* keep the `executionId` and mark the conversation as *held by a workflow*.

**4. Detect when it finishes**

Check the result:

```bash theme={null}
curl "https://gateway.jelou.ai/workflows/v1/executions/EXECUTION_ID/result" \
  -H "x-api-key: YOUR_API_KEY"
```

It returns `{ "executionId", "status", "output" }`. `output` is empty until the execution finishes.

| `status` | Meaning |
| - | - |
| `STARTED`, `PROCESSING` | In progress |
| `WAITING` | Waiting for the customer (an answer or a form) |
| `COMPLETED` | Finished successfully. The conversation returns to your agent |
| `CANCELED`, `CRASHED` | Ended without completing. The conversation returns to your agent |

Poll with increasing waits (for example, every few seconds) and set your own maximum. If the customer abandons the workflow, don't wait forever to take the conversation back.

> [!NOTE]
>
> Today only executions started over MCP can be **cancelled**. An execution started over the REST API ends on its own, when it completes or when its wait times out.

## Forms (webviews) inside the workflow

Many personal-data workflows open a **secure form** instead of asking for each field in chat. For example, an account-opening form with email, address and economic activity. Jelou opens the form with the execution's `executionId`. When the customer submits it, the form returns the result to Jelou and the workflow continues.

**Your agent does nothing at this step:** it doesn't intercept the form or forward its data. If the workflow seems stuck in `WAITING` after sending a form, the customer probably hasn't completed it yet.

## Personal data

Onboarding and account-opening workflows handle sensitive data: ID numbers, email, address, income. The recommended design is for **that data to travel only between the customer and the workflow**, never through your agent.

> **Don't pass personal data in `message`**
>
> Send only what the customer asked for (*"I want to open an account"*). The workflow asks for the ID number and other data itself, with its own validations. If your agent already knows a value, don't forward it as free text: the workflow will ask for it or take it from its own sources.

> **Fix identity on the server, not in the model**
>
> The `referenceId`, the `botId` and any customer identifier must come from your server's session, **never from what the model writes**. If the model could choose the `referenceId`, it could start a workflow for someone else. In Copilot Studio or Bedrock agents, pass these values from the action's configuration or the session context, not as parameters the model fills in.

> **Treat the outbox and results as sensitive data**
>
> The MCP `outbox` and an execution's `output` can contain customer data. Don't log them, don't show them on dashboards and don't forward them to other systems unless you need to.

> **What your agent sends on its own skips Jelou's filters**
>
> Messages sent by the workflow go through Jelou's data controls. Messages your agent sends directly to the customer don't: apply your own redaction rules before sending them.

> **Least privilege on the API key**
>
> Give the key only the scopes your agent uses (`workflows:read`, `workflows:execute`, `executions:read`). Store it as a secret in your platform and rotate it if it's exposed.

## Full example: account opening

**1. The customer: “I want to open a savings account”**

Your agent recognises the intent and does **not** ask for data itself.

**2. Your agent starts the workflow**

Over MCP (the workflow's tool with `_context.referenceId`) or REST (`POST /v2/skills/{skillId}/execute`). The `referenceId` comes from the server's session. Mark the conversation as held.

**3. The workflow asks for the ID number**

The message reaches the customer's WhatsApp. Your agent doesn't repeat it.

**4. The customer answers with their ID number**

Jelou delivers the answer to the workflow. With MCP, your agent passes it through unchanged with `workflow_reply`. Your agent doesn't store or interpret it.

**5. The workflow opens the data form**

The customer fills in email and address in the secure form. The form returns the result to Jelou.

**6. The workflow finishes**

`status` becomes `COMPLETED` (REST) or `completed` (MCP). Your agent takes the conversation back: *"Can I help with anything else?"*

![Example WhatsApp conversation: the customer asks to open an account, the agent replies, the workflow asks for the ID number and opens the secure form, confirms the request and the agent takes the conversation back](https://mintcdn.com/jelouai/RsS2oQfVkEw3sI-F/assets/images/agentes-ia/agentes-externos-whatsapp_en.png?fit=max\&auto=format\&n=RsS2oQfVkEw3sI-F\&q=85\&s=2653979f455942622d7e39112a693187)

## Troubleshooting

| Symptom | Likely cause | Fix |
| - | - | - |
| `401 Unauthorized` | Missing or invalid key, or a missing scope | Check `x-api-key` and the key's scopes |
| `404 User or company could not be found` | `botId` or `referenceId` don't match | Use the `botId` of the project's WhatsApp channel and the number without `+` |
| The customer gets two replies | Your agent replied while a workflow was active | Keep the agent silent until the execution reaches a final state |
| Two requests are created for the same customer | Automatic retry or double start | Don't blindly retry a 5xx. With MCP, respect `claim.conflict` |
| The execution stays in `WAITING` | The customer hasn't replied or hasn't completed the form | Wait and apply your own maximum. Don't start another execution |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.