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

# Trigger Agents from Incoming Webhooks

> Start a Scout agent when an external system sends JSON to a URL. Map the payload into a prompt, and optionally require a Scout API key.

An incoming webhook starts an agent when another system sends JSON to a URL. Scout turns that JSON into a prompt with a template you write, then runs the agent in a new session. Use it for events you already emit elsewhere: a new support ticket, a form submission, a CRM update, or a deploy notification.

You configure it from the agent's triggers.

## Create an incoming webhook

Open the agent and choose **Incoming webhook** from **Add triggers**. The panel on the right is where you name the webhook, write the prompt template, and choose whether callers must send an API key.

![The Scout agent editor with the Incoming webhook panel open. The form shows Name set to Support intake, a prompt template, Active turned on, Secure with API Key turned off, and a warning that the webhook URL is sensitive.](https://mintcdn.com/scout-171d217e/I_neaM0rQ4tdt_tk/images/agents/agent-incoming-webhook.png?fit=max\&auto=format\&n=I_neaM0rQ4tdt_tk\&q=85\&s=97d9b3a848644674857ecc0a1709d792)

**1. Open triggers**

Open the agent in Scout Studio. Click **Add triggers**. If the agent already has triggers, click the **+** beside the trigger count.

**2. Choose Incoming webhook**

Select **Incoming webhook**.

**3. Name the webhook and write a prompt template**

Enter a **Name**, such as `Support intake`. In **Prompt template**, describe the task and pull fields from the incoming JSON with `{{ payload.field }}`.

**4. Choose who can call it**

Leave **Secure with API Key** off when the URL itself should authorize the caller. Turn it on when the caller can send a Scout API key that is allowed to run this agent.

**5. Save and copy the URL**

Click **Save**. Scout shows the **Invocation URL**. Copy it and store it in the system that will call the agent.

The **Active** switch is on for a new webhook. A paused webhook keeps its URL and rejects calls until you turn **Active** back on and save.

You can add more than one incoming webhook to the same agent. Each one has its own name, template, URL, and security setting. A daily [schedule](https://docs.scoutos.com/agents/scheduling.md) can sit beside them.

> [!WARNING]
>
> With **Secure with API Key** off, anyone who has the invocation URL can run the agent. Treat that URL like a secret. Share it only with the system that calls it.

## Prompt templates

The request body is available as `payload`. Reference its fields in the prompt template, such as `{{ payload.ticket.id }}`.

```text theme={null}
Handle {{ payload.event }} for ticket {{ payload.ticket.id }}.
Subject: {{ payload.ticket.subject }}
```

A body like this:

```json theme={null}
{
  "event": "ticket.created",
  "ticket": { "id": "T-123", "subject": "Cannot sign in" }
}
```

becomes the agent message:

```text theme={null}
Handle ticket.created for ticket T-123.
Subject: Cannot sign in
```

A few patterns cover most payloads:

| Template | Result |
| - | - |
| `{{ payload.customer.name }}` | One nested field |
| `{{ payload \| tojson }}` | The full JSON body as a string |

Scout checks the template when you save it. Reference fields on `payload`.

When a call arrives, Scout renders the template before the agent starts. If a referenced field is missing, Scout responds with `422` and does not start a session. The detail is `Unable to render prompt template: required payload value is missing`.

The rendered text is the user message in the new session. Open the session in **History** to see exactly what the agent received.

## Call the webhook

Send a `POST` with `Content-Type: application/json`. Copy the URL from **Invocation URL**. It has this shape:

```text theme={null}
POST https://api.scoutos.com/agents/hooks/{webhook_id}
Content-Type: application/json
```

```bash theme={null}
curl -X POST "https://api.scoutos.com/agents/hooks/YOUR_WEBHOOK_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "ticket.created",
    "ticket": { "id": "T-123", "subject": "Cannot sign in" }
  }'
```

Scout answers `202 Accepted` once the template renders and the run is scheduled:

```json theme={null}
{
  "status": "accepted",
  "session_id": "a1b2c3d4-e5f6-7890-ab12-cd34ef567890"
}
```

The agent keeps running after that response. Use `session_id` to find the session in **History**. Scout schedules one run for each accepted request.

The body can be any valid JSON value. Objects are the usual case. Send an array or a string when that matches the event, and reference it from `payload` in the template.

## Secure the webhook with an API key

Turn on **Secure with API Key** when the caller can store a credential. Scout then requires a bearer token for a key that is allowed to run this agent:

```text theme={null}
Authorization: Bearer YOUR_SCOUT_API_KEY
```

```bash theme={null}
curl -X POST "https://api.scoutos.com/agents/hooks/YOUR_WEBHOOK_ID" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SCOUT_API_KEY" \
  -d '{ "message": "New signup from ada@example.com" }'
```

Create the key in **Settings → API Keys** and assign a role that can run the agent. See [API Keys](https://docs.scoutos.com/settings/api-keys.md) and [Roles](https://docs.scoutos.com/settings/roles.md).

| Caller state | Response |
| - | - |
| No valid Scout credential | `401 Unauthorized` |
| Credential cannot run this agent | `403 Forbidden` |

Editing the webhook, including this switch, keeps the same invocation URL.

## Pause, update, and delete

Open the agent, open **Triggers**, and select the webhook by name. The row shows **Active** or **Paused**.

* **Pause.** Turn **Active** off and save. The URL and template stay in place. Calls receive `404 Not Found` until you turn **Active** on again.
* **Update.** Change the name, template, or API key requirement and save. Callers keep using the same URL.
* **Delete.** Use the trash icon and confirm **Delete incoming webhook?** That URL stops accepting calls. Create a new webhook when you need a replacement URL, including after a URL leak.

You need permission to update the agent to create, view, or change its incoming webhooks.

## Review runs

Each call starts a new session on the agent's active revision. When a [traffic split](https://docs.scoutos.com/agents/agent-versioning.md) is on, Scout assigns the session the same way it assigns any other interaction.

In **History**, the session shows the rendered prompt as the user message. Webhook sessions carry a tag that starts with `webhook_trigger:`. Select that tag to list other runs from the same webhook. Match one HTTP call to one session with the `session_id` from the `202` response.

Open **Logs** on that session to see tool calls and errors. A failure after Scout has already returned `202` shows up there. See [Observability](https://docs.scoutos.com/agents/observability.md).

## Responses

| Status | Meaning |
| - | - |
| `202 Accepted` | The payload rendered and Scout scheduled a run. The body includes `status` and `session_id`. |
| `401 Unauthorized` | **Secure with API Key** is on, and the request has no valid Scout credential. |
| `403 Forbidden` | The credential is valid and cannot run this agent. |
| `404 Not Found` | The URL is unknown, the webhook is paused, or the agent is no longer available. |
| `415 Unsupported Media Type` | `Content-Type` is something other than `application/json`. |
| `422` | The body is not valid JSON, or the template cannot render. A missing field includes the detail `required payload value is missing`. |

Error bodies use this shape:

```json theme={null}
{ "detail": "Agent incoming webhook not found" }
```

## Incoming webhooks and workflow webhooks

Both accept an HTTP event from another system. They start different kinds of work.

| | **Agent incoming webhook** | **Workflow webhook** |
| - | - | - |
| Starts | An agent session | A workflow run |
| Payload | A prompt rendered from `payload` | The raw body as `{{ inputs.webhook_body }}` |
| URL | `https://api.scoutos.com/agents/hooks/{webhook_id}` | `https://hooks.scoutos.com/v1/workflows/{workflow_id}/webhook/{token}` |
| Configure | Agent triggers | Workflow trigger |

Use an agent webhook when the next step depends on judgment. Use a [workflow webhook](https://docs.scoutos.com/workflows/running-workflows.md#webhook-triggers) when every step is already defined.

## Best practices

**Send a sample before you go live.** Save the webhook, POST one realistic payload, and read the session in **History** before you point production traffic at the URL.

**Name the fields you need.** A template that reads `{{ payload.ticket.id }}` returns `422` when that field is missing, and Scout does not start the agent.

**Include the raw event when the agent should see it.** Add `{{ payload | tojson }}` to the template so the model can read fields you did not spell out.

**Prefer an API key when the caller can hold one.** Turn on **Secure with API Key** for services you operate. Use the URL alone for callers that can only store a URL.

**Pause while you change the template.** Turn **Active** off, update the prompt, send a test, then turn it back on. The caller's URL stays the same.

**Replace a leaked URL by deleting the webhook.** A new webhook gets a new URL. Pause alone leaves the old URL ready to work again.

## Common questions

**Do I need a workflow?**
No. Add the webhook on the agent from **Add triggers**.

**Can one agent have several webhooks?**
Yes. Give each caller its own webhook so you can pause, rewrite, or delete one URL without affecting the others.

**Does a later event continue the previous session?**
Each request starts a new session. Put any context the agent needs in that request's JSON.

**What does the agent run?**
The rendered prompt, on the agent's active revision, with the tools and instructions already configured on the agent.

**Can I rotate the URL without deleting the webhook?**
Saving changes keeps the current URL. Delete the webhook and create another one to issue a new URL.

**Where do I see a failed run?**
Find `session_id` in **History**. The `202` response means Scout scheduled the run. Tool failures and model errors appear on that session in **Logs**.


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