---
title: "Send one message per event · Agent Wranglers"
url: https://agentwranglers.se/playbook/notifications/channels-and-preferences/one-message-per-event
updated: 2026-10-20
licence: "All rights reserved"
---

# Send one message per event

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Channels and preferences](https://agentwranglers.se/playbook/notifications/channels-and-preferences)
- Rule: Deliver each event to a person on one channel first, and move to the next channel only if they have not seen it. Never send the same news on every channel at once.
- How: Give each event and recipient a key, such as `order-1042:shipped:user-77`, with a unique constraint, as in [send each message once](https://agentwranglers.se/playbook/email/automation-and-agents/send-once). Order channels from least to most intrusive, such as in-app, then push, then email. Schedule the next channel after a wait, such as 15 minutes, and cancel it when the in-app item is read. Only critical types may use several channels at once.
- Check: Trigger a shipment and read it in the app within the wait. One in-app item exists, and no push or email is sent. Trigger the same event twice and the count stays at one.

Example:

```js
const ESCALATION_WAIT_MS = 15 * 60 * 1000;

export async function deliver(event, userId) {
  const key = `${event.id}:${userId}`;
  const [first, ...rest] = await channelsFor(userId, event.type);
  if (!(await db.deliveries.insertIfNew({ key, channel: first }))) return;
  rest.forEach((channel, step) =>
    queue.add('escalate', { key, channel }, { delay: ESCALATION_WAIT_MS * (step + 1) }));
}
// The escalate worker skips the job when the in-app item for this key is already read.
```

```python
from datetime import timedelta

ESCALATION_WAIT = timedelta(minutes=15)

def deliver(event, user_id):
    key = f"{event.id}:{user_id}"
    first, *rest = channels_for(user_id, event.type)
    if not db.deliveries.insert_if_new(key=key, channel=first):
        return
    for step, channel in enumerate(rest, start=1):
        queue.enqueue_in(ESCALATION_WAIT * step, escalate, key, channel)
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers notifications playbook: https://agentwranglers.se/playbook/notifications/channels-and-preferences/one-message-per-event`.

- Previous: [Let people choose per type and channel](https://agentwranglers.se/playbook/notifications/channels-and-preferences/notification-preferences)
- Next: [Hold messages during quiet hours](https://agentwranglers.se/playbook/notifications/channels-and-preferences/quiet-hours)
- [Channels and preferences](https://agentwranglers.se/playbook/notifications/channels-and-preferences)
