---
title: External triggers
summary: How Holon agents wait for and receive external events through webhook wake endpoints and callback URLs.
order: 25
---

# External Triggers

External triggers let Holon agents wait for events from outside the runtime
— for example, a CI pipeline completing, a GitHub webhook, or a timer firing.
Every agent receives a default ingress callback URL at startup.  The agent
waits on it, and resumes when the external system delivers an event.

## The trigger model

Holon treats external events as first-class runtime concepts. The model has
three parts:

1. **Default ingress** — Every agent receives a default ingress callback URL
   with a capability secret at startup.
2. **Wait on the trigger** — The agent calls `WaitFor` with `wake=external`
   and the external object reference. The runtime records the wait and yields
   the turn.
3. **External delivery** — When the external system POSTs to the callback URL,
   the runtime wakes the agent and delivers the payload.

## Delivery modes

Triggers support two delivery modes:

| Mode | Behavior |
|------|----------|
| `enqueue_message` | The external payload is enqueued as a normal message into the agent's queue |
| `wake_hint` | The callback acts as a wake signal only; the agent resumes and checks the external state itself |

Use `enqueue_message` when the external system carries the full event content
(e.g., a webhook payload). Use `wake_hint` when the agent needs to actively
poll or check the external state after being woken (e.g., a CI status check).

## Callback endpoints

Every agent has a **single default ingress capability** created at startup.
The default ingress is provisioned as either a `wake_hint` or `enqueue_message`
mode callback URL, depending on how the agent is configured.  A trigger always
has one delivery mode and one `trigger_url`.

| Mode | URL pattern | Effect |
|------|-----------|--------|
| `wake_hint` | `/callbacks/wake/:token` | Wakes the agent from `WaitFor(external)` |
| `enqueue_message` | `/callbacks/enqueue/:token` | Enqueues a message (the POST body) |

The callback token is a **capability secret** — treat it like a password.
Anyone who knows the token can wake your agent or enqueue messages into it.
Tokens are generated by the runtime, are cryptographically random, and are
returned to the agent's execution environment context (not persisted to
transcripts or logs by default).

### Example: Wake callback

```bash
curl -X POST http://localhost:8787/callbacks/wake/CALLBACK_TOKEN
```

A minimal POST (even with an empty body) wakes the agent. The runtime
validates the token and resumes the waiting agent.

### Example: Enqueue callback

```bash
curl -X POST http://localhost:8787/callbacks/enqueue/CALLBACK_TOKEN \
  -H "Content-Type: application/json" \
  -d '{"text": "CI build #42 completed: success"}'
```

The payload is enqueued into the agent as a message. The agent processes it on
its next turn.

## The agent's WaitFor cycle

When an agent expects an external event:

1. The agent uses its default ingress capability (exposed in the execution
   environment context)
2. The agent calls `WaitFor(wake=external, resource=<object>)`
3. The runtime records the wait, yields the turn, and the agent sleeps
4. The external system delivers the event via the callback URL
5. The runtime wakes the agent and resumes from the WaitFor point

The `resource` parameter in WaitFor identifies what the agent is waiting on
(e.g., `github:owner/repo#123` for a PR, or a URL for an HTTP endpoint). This
is for the agent's own tracking; the runtime does not interpret it.

## Security model

Callback tokens are capability secrets. The runtime stores only the hashed
token, not the raw value — once a token is issued, the raw token is returned
to the agent's execution context and is never persisted to logs or storage.
Store it securely on the caller side.

Tokens can be revoked with `CancelExternalTrigger`. Revoking a trigger
invalidates its URL immediately; future requests to that URL are rejected.

### Reset callback token

The `POST /control/agents/:agent_id/external-triggers/:id/reset-callback`
endpoint rotates the callback token for an existing trigger. The old token
is invalidated immediately and a new token is returned. Use this when a
token has been exposed or as a periodic security rotation.

## Default ingress capability

Every agent has a default external ingress capability created at startup:

- A callback URL is available without explicit creation (the mode is
  determined by the agent's configuration)
- The default trigger ID and callback token are exposed in the agent's
  execution environment context

This default trigger is sufficient for most use cases.  Use
`CreateExternalTrigger` only when you need a different delivery mode than the
default, or for compatibility diagnostics.  Do not treat creating multiple
triggers as the ordinary workflow.

## Cancelling triggers

Revoke a trigger when it's no longer needed:

```
CancelExternalTrigger { external_trigger_id: "trigger_abc123" }
```

This invalidates the callback URLs immediately. Any future requests to those
URLs will be rejected.

### Agent stop auto-revocation

When an agent is stopped (`holon agent stop`), all of its external triggers
are automatically revoked. This prevents orphaned callback URLs from
remaining active after the agent is no longer running. If you restart the
agent later, new triggers with fresh tokens are provisioned.

## Integration patterns

### CI/CD pipeline

Configure your CI system to POST to the wake callback on pipeline completion.
The agent uses its default ingress, starts a build, waits on the trigger, and resumes
when the build reports its status.

### GitHub webhooks

Point a GitHub webhook at the enqueue callback. The agent receives repository
events (issues, PRs, pushes) as enqueued messages and processes them on its
next turn.

### Scheduled timers

Holon's built-in `Timer` CLI command (`holon timer --after-ms 60000`) can
deliver events through the callback system, allowing agents to sleep and wake
on a schedule.

## See also

- [Integration guide](/guides/integration.md) — HTTP control plane endpoint reference
- [Runtime model](/concepts/runtime-model.md) — How triggers fit into the agent execution loop
- [WaitFor tool](/reference/model-tool-schema-inventory.md) — The WaitFor tool that pairs with triggers
