> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.rightbrain.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.rightbrain.ai/_mcp/server.

# Triggers & runs

> Triggers start Rightbrain runs through API calls, webhooks, schedules, Gmail, and Slack; every run is observable, streamable, and metered.

A **trigger** is what starts a run. A **run** is a single execution of a [Task](/docs/concepts/tasks) or an [agent](/docs/concepts/agents). Triggers are the front door; runs are the record of what happened. Together they're how Rightbrain moves from "a thing I built" to "a thing that operates."

## Triggers

Anything that can fire an event can start a run. A trigger targets **a Task or an agent**.

| Trigger         | How it fires                               |
| --------------- | ------------------------------------------ |
| **API call**    | A direct `POST .../run` request.           |
| **Webhook**     | An inbound HTTP call to a public endpoint. |
| **Schedule**    | A cron expression runs it on a timer.      |
| **Gmail inbox** | A new email in a connected inbox.          |
| **Slack**       | A message in a connected workspace.        |

### Webhook triggers

A webhook trigger has a public invoke endpoint:

```
POST /api/v1/public/webhook/{project_id}/{endpoint_id}
```

Authentication is either **`hmac_sha256`** (the caller signs the payload with a shared secret, sent in the configured signature header) or **`bearer_token`** (a bearer token). The auth method is fixed at creation — to switch methods, create a new trigger. Webhook triggers support optional payload mapping and idempotency, and you can rotate the secret or regenerate the endpoint.

HMAC authentication supports `prefix`, `timestamp_kv`, `plain`, and `standard_webhooks` signature formats. Standard Webhooks v1 uses a Base64-encoded HMAC over the message ID, timestamp, and raw body. Its default headers are `webhook-signature`, `webhook-timestamp`, and `webhook-id`, with a default timestamp tolerance of 300 seconds. The message ID also becomes the idempotency key when no payload path supplies one.

Configure `idempotency_key_path` when the sender may retry deliveries. Rightbrain remembers the extracted key for `idempotency_ttl_seconds`, preventing the same event from starting duplicate work during that window. Keep the same event ID when retrying.

For a task target, a duplicate returns `409` with `error.code: "DUPLICATE_REQUEST"` and `error.original_task_run_id`. For an agent target, a duplicate returns `202` with `is_duplicate: true` and the original `event_id`. These responses do not start another run. A bad signature returns `401` with `error.code: "AUTH_FAILED"`.

> **Note**
>
> Response behavior depends on the target. A **task**-targeted webhook runs synchronously and returns `200` with the run result in the body. An **agent**-targeted webhook returns `202` with the trigger event ID and processes the run in the background. Either way the invocation is recorded as a trigger event you can list and inspect.

### Schedule and inbox triggers

* **Schedule** — a cron trigger that fires on a recurring timer.
* **Gmail inbox** — fires an agent (or Task) when mail arrives in a connected Gmail inbox, passing the message through as input.

Each trigger type records its own **events**, so you can see every firing and its outcome.

## Runs

A run captures one execution end to end: inputs, tool calls, output, token counts, timing, and credits.

### Statuses

Agent runs move through `running` → `waiting_for_human` (paused for an [approval](/docs/production/approvals)) → `completed` or `failed`. Task runs are request/response — they return the `TaskRun` directly.

### Streaming (agents)

Agent runs stream **Server-Sent Events** so you can render progress live — incremental text, each tool call and its result, approval pauses, and a terminal `done` event. See [Run agents via API](/docs/api/run-agents) for the full event reference and how to handle the stream.

### Files in and out

Runs handle files through a **runtime file registry**. Agents accept uploaded input files (multipart or base64), pass them between tools within a single run, and track generated files with provenance (whether a file was an input or was generated, and by which Task). A paused run resumes with its file manager intact. Files are retrievable per run:

```
GET .../task-agent/{agent_id}/run/{run_id}/file/{file_name}
```

### Observability and usage

Every run is recorded for [observability](/docs/production/observability). Run and event endpoints expose the execution source, failure metadata, and per-model telemetry. Runs are metered in **credits**, and token, credit, timing, and usage reports are available per Task, per agent, and project-wide. Sensitive audit events are recorded in the tamper-evident [audit log](/docs/production/observability).

## Signed webhook example

This creates an agent webhook, maps the incoming `message`, and sends one event. Set `RB_API_KEY`, `ORG_ID`, `PROJECT_ID`, and `AGENT_ID` in your environment. The management API uses your API key; the public invoke endpoint uses the separate webhook secret returned at creation.

#### Create the webhook

Run `node webhook.mjs` with Node.js 22+. The script creates an agent-targeted webhook, then signs and sends one event. The management request uses your API key; the response provides the webhook's own secret and invoke URL.

`payload_mapping` takes `message` from the incoming JSON. `idempotency_key_path` identifies retries of the same event. Store the returned `auth_secret` securely for future deliveries; do not log it.

**`webhook.mjs (lines 3-25)`**

```javascript webhook.mjs (lines 3-25)
const base = "https://app.rightbrain.ai/api/v1";
const projectPath = `/org/${process.env.ORG_ID}/project/${process.env.PROJECT_ID}`;
const authorization = `Bearer ${process.env.RB_API_KEY}`;

const created = await fetch(`${base}${projectPath}/trigger/webhook`, {
  method: "POST",
  headers: { Authorization: authorization, "Content-Type": "application/json" },
  body: JSON.stringify({
    name: "Signed agent webhook",
    target_type: "task_agent",
    target_id: process.env.AGENT_ID,
    auth_method: "hmac_sha256",
    auth_config: {
      signature_header: "X-Signature",
      signature_format: "plain",
      encoding: "hex",
    },
    payload_mapping: { message: "$.message" },
    idempotency_key_path: "$.event_id",
  }),
});
if (!created.ok) throw new Error(`Create webhook: HTTP ${created.status}`);
const trigger = await created.json();
```

#### Sign the exact request bytes

Serialize the payload once, then compute its HMAC-SHA256 using the webhook secret. The configured `plain` format expects lowercase hex with no prefix.

Re-serializing the JSON, or adding whitespace or a newline after signing, changes the signature. For retries, preserve the event ID and body. Running this whole script again creates a new trigger and event.

**`webhook.mjs (lines 28-34)`**

```javascript webhook.mjs (lines 28-34)
const body = JSON.stringify({
  event_id: randomUUID(), // Reuse this ID when retrying this event.
  message: "Create a product listing for headphones.",
});
const signature = createHmac("sha256", trigger.auth_secret)
  .update(body, "utf8")
  .digest("hex");
```

#### Send the event

Send the signed body to `endpoint_url` with the signature in `X-Signature`. An agent webhook returns `202` with an event ID: this acknowledges receipt, not completed execution.

The script prints the trigger and event IDs for inspection. A task-targeted webhook behaves differently: it returns the run result synchronously.

**`webhook.mjs (lines 36-43)`**

```javascript webhook.mjs (lines 36-43)
const invoked = await fetch(trigger.endpoint_url, {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Signature": signature },
  body, // Send exactly the bytes that were signed.
});
if (!invoked.ok) throw new Error(`Invoke webhook: HTTP ${invoked.status}`);
const accepted = await invoked.json();
console.log({ triggerId: trigger.id, eventId: accepted.event_id, status: accepted.status });
```

#### Inspect the execution

Set `TRIGGER_ID` and `EVENT_ID` to the printed IDs, then poll the event. Inspect `status`, `error_code` and `error_message`. A completed agent event includes `task_agent_run_id` for the [agent run API](/docs/api/run-agents).

`request_data.mapped_input` lets you check the mapping. Store request data according to your application's privacy requirements. When testing is finished, disable the temporary trigger with `POST …/trigger/webhook/{trigger_id}` and `{ "status": "disabled" }`.

**`inspect-event.sh (lines 1-3)`**

```bash inspect-event.sh (lines 1-3)
curl --fail-with-body \
  "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/trigger/webhook/$TRIGGER_ID/events/$EVENT_ID" \
  -H "Authorization: Bearer $RB_API_KEY"
```

#### Complete files

**`webhook.mjs`**

```javascript title="webhook.mjs"
import { createHmac, randomUUID } from "node:crypto";

const base = "https://app.rightbrain.ai/api/v1";
const projectPath = `/org/${process.env.ORG_ID}/project/${process.env.PROJECT_ID}`;
const authorization = `Bearer ${process.env.RB_API_KEY}`;

const created = await fetch(`${base}${projectPath}/trigger/webhook`, {
  method: "POST",
  headers: { Authorization: authorization, "Content-Type": "application/json" },
  body: JSON.stringify({
    name: "Signed agent webhook",
    target_type: "task_agent",
    target_id: process.env.AGENT_ID,
    auth_method: "hmac_sha256",
    auth_config: {
      signature_header: "X-Signature",
      signature_format: "plain",
      encoding: "hex",
    },
    payload_mapping: { message: "$.message" },
    idempotency_key_path: "$.event_id",
  }),
});
if (!created.ok) throw new Error(`Create webhook: HTTP ${created.status}`);
const trigger = await created.json();
// Store auth_secret in your secret manager for later deliveries; do not log it.

const body = JSON.stringify({
  event_id: randomUUID(), // Reuse this ID when retrying this event.
  message: "Create a product listing for headphones.",
});
const signature = createHmac("sha256", trigger.auth_secret)
  .update(body, "utf8")
  .digest("hex");

const invoked = await fetch(trigger.endpoint_url, {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Signature": signature },
  body, // Send exactly the bytes that were signed.
});
if (!invoked.ok) throw new Error(`Invoke webhook: HTTP ${invoked.status}`);
const accepted = await invoked.json();
console.log({ triggerId: trigger.id, eventId: accepted.event_id, status: accepted.status });
```

**`inspect-event.sh`**

```bash title="inspect-event.sh"
curl --fail-with-body \
  "https://app.rightbrain.ai/api/v1/org/$ORG_ID/project/$PROJECT_ID/trigger/webhook/$TRIGGER_ID/events/$EVENT_ID" \
  -H "Authorization: Bearer $RB_API_KEY"
```

With `signature_format: "prefix"`, configure the matching `signature_prefix` (for example `sha256=`) and include it in the signature header. This example explicitly selects `plain` so the signing and verification rules match.

## Related

#### [Webhooks & forwarders](/docs/api/task-forwarders)

Webhook and email forwarding for run results.

#### [Observability & audit](/docs/production/observability)

Run telemetry, failure reasons, reports, and the audit log.

#### [Run agents via API](/docs/api/run-agents)

Consume the SSE event stream.

#### [Memory & context](/docs/concepts/memory-and-context)

Control the session history carried across runs.

#### [API Reference — Triggers](/api-reference/api-reference/triggers)

Webhook, schedule, and Gmail trigger endpoints.