Event-driven agents: reacting to messages and alerts
An event-driven agent starts from a message, alert, or schedule. How to handle retries, limit its tools, and protect private data from malicious input.

On this page
An event-driven agent starts work when something happens, such as a new support ticket arriving, an alert firing, or a scheduled check becoming due. It may finish the job immediately or pass it to another process. This fits monitoring and triage even when no user is waiting for an answer. Reliable delivery, retries, and tool permissions decide whether it can run without close supervision.
TL;DR A message, alert, or schedule starts the agent. It can act, save a result, or trigger more work. Save progress so the job can recover after a failure. Make repeated attempts safe, set a spending limit, and control which data and tools incoming messages can access.
What an event-driven agent is
The agent receives an event and decides what, if anything, to do. It might classify a ticket, update a record, or publish a follow-up event. A queue may redeliver after a failure, so the handler needs a durable way to distinguish finished work from work that only started.
The same event handler can run as a short-lived process for each message. What makes the design event-driven is the trigger and delivery contract, not whether a process stays alive or a webhook sends an acknowledgment.

How event-driven agents work
Think of a queue as the handoff point. A producer records an event, a worker receives it, and the worker acknowledges it according to the queue's delivery rules. Webhooks can also invoke a handler directly and receive a synchronous acknowledgment. Publishing a new event is useful when another component needs to react, but it is not required for every handler.
The common moves are receiving an event, deciding whether action is needed, and recording the outcome. A handler may call a model or tools, then optionally emit a follow-up event. Anthropic's guidance favors adding autonomy only when a simpler workflow cannot meet the task, so keep the agent's decision and tool permissions narrow.
What it looks like in code
This ticket-triage example publishes a follow-up event, so it uses an outbox. The handler distinguishes completed events from those that only started. It passes the event ID to side-effecting tools, and commit_result atomically stores the outbox entry and marks the event complete. A separate publisher delivers the result.
for event in bus.subscribe("tickets"):
if completed(event.id):
bus.ack(event)
continue
if not should_act(event):
mark_complete(event.id)
bus.ack(event)
continue
checkpoint(event.id, "started")
result = agent.run(event, idempotency_key=event.id)
commit_result(event.id, "triaged", result)
bus.ack(event)On replay, a started event runs again; only completed can be acknowledged without more work. The outbox prevents a crash between saving the result and publishing it from losing the downstream event. Tool side effects still need their own idempotency keys or compensating logic. This example assumes mark_complete also writes durably before acknowledgment.
When event-driven is the right call
Reach for this pattern when an event is the natural trigger. The work can be brief or long-running; the important question is whether it needs to start when something changes, even if no user asks at that moment.
Monitoring fits: watch a stream of logs, metrics, or alerts and react when something crosses a line. Triage fits: new tickets, emails, or pull requests land continuously and each needs to be read, classified, and routed. Reactive pipelines fit: a file lands in a bucket, a payment settles, a build finishes, and the agent picks it up and moves it to the next stage.
These tasks react to changes rather than a request for an immediate answer. For a chat question, a direct request-response flow is simpler. For a ticket created through a webhook, acknowledge receipt and queue model work if it cannot safely finish within the webhook request.
When it breaks
It breaks when delivery and recovery are afterthoughts. The handler can be short-lived and stateless, but the workflow still needs durable progress if an event may be retried or split across steps. Depending on the queue or webhook contract, a crash can cause redelivery, a failed response, or lost work if acknowledgment happened too early.
A crash after started should leave the event available for redelivery. The handler runs it again unless the durable completion record exists. A side effect made before the crash may already have happened, which is why the event ID must travel into side-effecting tools or a compensating action must be defined.
Unattended decisions can also go unnoticed. A wrong ticket classification may be stored or passed to another system without a person reading each result. Sample outcomes and alert on failure rates so an error does not hide in a stream of successful deliveries.
The security constraint that decides the architecture
Unattended execution raises the stakes of prompt injection. If an attacker can shape an incoming event, the agent may act on malicious instructions before anyone reviews the result. Other agent patterns can also run without a human, so the risk comes from the permissions and trust boundaries on this event path, not from its name.
The frame to reason with is Simon Willison's lethal trifecta: private data, exposure to untrusted content, and the ability to communicate externally. As Willison lays it out, an agent with all three can be induced to leak data if it treats injected text as an instruction. Check the actual event path and tool permissions; being event-driven does not automatically give an agent all three capabilities.
Meta's practical AI agent security recommends no more than two of the three capabilities in one session without a human in the loop, guidance it calls the agents rule of two. Removing one capability blocks this particular exfiltration path. It does not remove every possible attack.
Do not rely on filtering as the only boundary. A 2025 joint paper from OpenAI, Anthropic, and Google DeepMind evaluated 12 published prompt-injection defenses under adaptive attack and reported attack success above 90 percent against most tested defenses. That result is about the tested setups, not every detector. Decide which data and tools each event path can reach, and enforce those permissions outside the model.

What it costs
An event-driven agent can keep spending while events arrive, even when no user is watching. Without an action or spend cap, a noisy source can run up costs until someone intervenes.
Spend depends on event volume and how often the handler calls a model or tool. A spike can increase cost even when each event is handled by a short-lived worker.
That is why a budget and a rate limit are part of the architecture, not an add-on. Cap the actions per hour, cap the spend per window, and decide up front what happens at the cap: shed load, queue for later, or page a human. Bolt this on after the first surprise invoice and you have already paid for the lesson.
What to trace
When work runs without individual review, traces and outcome checks show where it failed:
- Events in versus events acted on. A shift in the
should_actratio can signal a source change, a classifier problem, or abuse. Investigate the cause. - Spend per window against the cap. Alert as usage approaches the limit so a spike does not surprise the operator.
- Replays and half-done events. How often an event was redelivered or checkpointed "started" but never "done." This is your recovery logic working, or silently not.
- Trifecta legs per path. Record which paths can reach private data, untrusted content, and outbound tools. A path with all three needs a permission change or human gate before unattended use.
The decision rule
| Question | If yes | If no |
|---|---|---|
| Is an event the natural trigger? | Event-driven fits | A direct request may be simpler |
| Can retries and acknowledgment recover a mid-task crash? | Check the handler under replay | Fix delivery before unattended use |
| Does one event path hold all three trifecta capabilities? | Restrict a capability or add review | Check other attack paths too |
| Are model actions and spend capped? | Monitor usage against the caps | Add limits before unattended use |
The trigger, delivery contract, and permissions decide whether this design fits. A synchronous webhook acknowledgment, short-lived worker, or optional follow-up event can all be part of it.
What to do about it
For a queued handler, test a crash after it starts but before it acknowledges the event. The replay should finish missing work without duplicating a side effect. For a webhook, define what the caller learns from the acknowledgment and where unfinished work is stored.
For paths that read untrusted content, limit access to private data and outbound tools outside the model. Review the permissions on each path rather than assuming the event architecture itself provides a boundary.
Set action and spend caps, with a defined behavior when the cap is reached. Then inspect replays, outcomes, and costs over time.
For related designs, see the eight agent patterns and the orchestrator-worker pattern for tasks that can be split into independent pieces.

Sources
- Anthropic: Building effective agents
- Simon Willison: The lethal trifecta
- Meta: Practical AI agent security
- OpenAI, Anthropic, and Google DeepMind: Prompt-injection defenses under adaptive attack
Common questions
What is an event-driven AI agent?
An event-driven AI agent starts work when a message, alert, or scheduled event arrives. It decides what to do and can act, save a result, or trigger more work. It does not need to run constantly: saved progress lets another process continue the job after a pause or failure.
How do you stop an agent from leaking private data?
Limit what each event path can access. Keep private data, untrusted input, and outbound tools from combining where possible, and enforce permissions outside the model. A 2025 joint paper from OpenAI, Anthropic, and Google DeepMind ([arXiv 2510.09023](https://arxiv.org/abs/2510.09023)) found high attack success against most defenses it tested, so filters alone are not a sufficient boundary.
What is the lethal trifecta in AI agent security?
The lethal trifecta, named by Simon Willison, is the combination of private data, exposure to untrusted content, and the ability to communicate externally. Any agent holding all three at once can be turned into an exfiltration tool, because a model can mistake injected text for a command. Removing a capability blocks that particular path, though it does not guarantee overall safety ([Willison](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/)).
What is Meta's "agents rule of two"?
It is Meta's guidance that an agent should touch no more than two of the three lethal-trifecta capabilities in a single session without a human in the loop. Private data, untrusted content, and external communication can each be safe on their own, but all three together enable exfiltration. Restricting one capability per path is a useful control ([Meta](https://ai.meta.com/blog/practical-ai-agent-security/)).
Can prompt injection make an agent leak private data?
Yes. If an agent reads untrusted content, holds private data, and can send data out, injected instructions in that content can direct it to leak the private data through the outbound channel. Limit what each path can access and enforce tool permissions outside the model.
When should you use event-driven architecture instead of request-response for agents?
Use an event-driven design when a change, schedule, or queued message is the natural trigger, as in monitoring or ticket triage. Use a direct request-response flow when the caller needs the result in that request. A webhook can acknowledge quickly and queue later work, so the two designs can meet in one system.