---
title: Automations & Auto-replies (auto-respond to messages)
---

# Automations & Auto-replies (auto-respond to messages)

## What it is

_(formerly called "Away Mode". Its sidebar entry in the **Automation** group is now labeled **"Auto-Reply & Triage"** — renamed 2026-08-10 so it no longer collides with the scheduled "Automations" family (Automation Builder / My Automations / Cron Jobs); same feature, display-name only.)_

Omniscio has **two** distinct ways to act automatically, and the one you want depends on how much logic you need (and on what triggers it — Auto-replies watch a **session's** own output, Claude **or** a non-Claude engine like Codex / Gemini / OpenCode; Automations watch incoming channel messages). **Auto-replies** is the lightweight one: simple rules (keyword / project / tag / time-of-day) match text in a session's finished output — for **any** engine — and fire **one action**. Historically that action was always a reply; now each rule picks one of **six** — **reply** (inject a message / snippet, the default), **rename the session**, **archive the session**, **start a new session** (spawns a fresh Claude — billable per fire), **add an inbox note** to yourself, or **organize the session** (set its title, a library tag, and an inbox color together). A rule can also carry an optional **project scope** so it only fires for sessions in one project ("this text **in this repo**"). See [Auto-reply actions](#auto-reply-actions-beyond-a-plain-reply) below. **Automations** is the heavy one: a full pipeline engine where rules can use AND/OR conditions (with `contains`, `matches_regex`, `matches_glob`, `equals` operators) or delegate to AI classification, and each rule runs an ordered chain of up to **10 actions** — reply, label, archive, summarize with AI, forward, **forward to email** (Gmail only, max 20/hour), notify, **spawn a CLI session** (with template-interpolated prompt + optional fuzzy project routing — see [Spawning a CLI session from a message](#spawning-a-cli-session-from-a-message-cli_session-action) below), **send a message to an existing session**, **archive a Claude session** (see [Archiving a Claude session](#archiving-a-claude-session-cli_archive_session-action) below), **run a recipe** (triggers a full recipe orchestration run with approval gates and cost caps), **send an outbound HTTP request** (with an optional stored credential — see below), or whitelist. Both auto-respond to Gmail/SMS/Slack/Telegram traffic and both share the same template vars (`{{sender}}`, `{{body}}`, `{{channel}}`, `{{subject}}`). Most users start with Auto-replies and graduate to Automations only when they need multi-step workflows.

Automations support **four trigger types**:

- **`on_message`** — fires when an incoming/outgoing message arrives on a channel (the original behavior described above).
- **`manual`** — never fires automatically; you run it on demand from the UI button. (Cron-like timing in Omniscio is the separate Cron Jobs feature, not an automation trigger.)
- **`on_session_state`** — fires when a condition about Claude Code session **statuses** becomes true. Concrete example: _"once every session in project X is `ended`, send a follow-up prompt to project Y."_ You pick a **scope** (one project / all projects / a hand-picked subset of sessions / a single session), the **statuses** that count as "matched" (e.g. `ended`, `error`, `archived`), a **firing mode** (`one_shot` self-disables after firing, `recurring` re-arms every time the condition flips back), and a **cooldown** (60s minimum in the UI). The watcher polls every 30 seconds — there can be up to a 30-second delay between the condition becoming true and the action chain firing.
- **`on_pm_event`** — fires when a **PM board event** occurs (item created, status changed, assigned, overdue, column changed). You pick an **event type**, a **board scope** (`all` boards or a `specific` board+column), and the rule fires through the normal channel automation dispatcher. The PM event channel bridge receives events from the sync pipeline (fire-and-forget), matches them against `on_pm_event` rules, and dispatches with a 5-second per-rule cooldown. Three **PM-specific v2 actions** are available: `pm.update-linked-status` (update a column on the triggering item), `pm.add-comment` (add a comment to the triggering item), and `pm.create-item` (create a new item on any board). Gated on BOTH `automationsEnabled` AND `pmAutomationsEnabled`. Four **preset templates** ship: Overdue Task Nudge, Status Change Update, Comment on New Items, Assignment Notification. In-development (same `pm-automations` feature gate). See [mission-control.md — PM event channel bridge](mission-control.md#pm-event-channel-bridge).

## Where to find it

### How to use it

1. **Decide which you need.** If the goal is "send a canned reply when a message mentions X" — Auto-replies. If the goal is "auto-archive newsletters, forward client mail to Slack, and AI-summarize everything else" — Automations.
2. **Open the Automations virtual project.** Click **Automations** in the left sidebar's Integrations group — it lives next to **Quick Replies**. The sub-sidebar uses the same layout as a regular project: two collapsible sections at the top — **RULES (N)** and **AUTO-REPLIES (N)** with chevron + uppercase label headers — followed by the standard **SESSIONS / SNOOZED / ARCHIVED** sections beneath. The two collapsibles default to expanded; each lists the matching rules as standard sidebar rows (the same shared interactive row style used elsewhere, e.g. the Quick Replies sidebar) you can click to jump to that category's list view. Clicking a section header toggles its expansion AND switches the main panel to that list. The Auto-replies list view also has a **sticky filter input** at the top (case-insensitive substring match against trigger keyword + reply text + snippet label); when active, the sidebar header shows the match count as `Auto-replies (5 of 22)` so you can tell at a glance how many rules survived the filter. The standard SESSIONS section shows your AI edit sessions as normal session rows with full snooze + archive support. Each rule list is a single wide column where every row collapses to a header (rule name + condition meta + last-fired/run-count); clicking a row's header toggles an inline editor underneath (accordion behavior — opening row B collapses row A). The list header carries a **+ New rule** button that opens the editor in a centered modal for creating fresh rules.
3. **Configure an Auto-replies rule.** Open the **AUTO-REPLIES** sidebar section (or click any auto-reply row) to switch the main panel to the Auto-replies list, then click **+ New rule** in the list header. Pick condition type (keyword / project / tag / time), enter the match value (for **keyword** / **tag**, the value is matched as one whole phrase — a case-insensitive substring of the agent's output (text the agent places inside a fenced code block, inline code, or a `>` blockquote is ignored, so a session that merely **quotes** your trigger — discussing it, pasting a log, echoing a header — won't fire the rule) — **not** a comma-separated list of alternatives), then choose **what happens when it matches** from the action dropdown — **Reply** (free-text message or a snippet from your Quick Replies library), **Rename the session**, **Archive the session**, **Start a new session** (a prompt + optional target project — billable), **Add an inbox note** (title + optional body), or **Organize the session** (any combination of a title, a library tag, and an inbox color). Each action reveals only its own fields. Optionally fill **Only in project** to scope the rule to one project. Save. The first matching rule for a finishing session wins. There is no global on/off master toggle — auto-replies fire whenever an enabled rule matches; toggle individual rules off in their list row to silence them. Non-reply rows show a small action badge (`rename` / `archive` / `start session` / `inbox note` / `organize`) so you can tell them apart at a glance.
4. **Configure an Automation.** Open the **RULES** sidebar section (or click any rule row) to switch the main panel to the Rules list, then click **+ New rule** in the list header. Pick the **trigger type** (message / manual / session state / PM board event) → fill the rest of the form: name, scope, condition (for message triggers), actions. Expand the **Actions** accordion to chain up to 10 in order. Rules fire in list order until one matches (unless the action is `pass`, which lets the next rule run too). Rules created in the virtual project land as `approved` immediately; rules created via the CLI server (by an outside AI) land as `pending` and show an amber left-border badge until you approve them.
5. **For session-state triggers**, the form swaps the channel/condition cards for two new ones: **Scope** (radio: this project / all projects / pick sessions / single session) and **Conditions** (checkbox grid for `running`/`needs_you`/`ended`/`error`/`archived`/`paused`/etc., plus the `one_shot` vs `recurring` mode toggle and cooldown spinner). Action choices are filtered — message-only actions (`auto_respond`, `label`, `archive`, `summarize`, `forward`, `forward_email`) are hidden because there's no incoming message to act on; session/notification actions stay visible, including the **Send to CLI Session** action that pushes a templated prompt into a specific existing session and the **Archive Session** action — which on a session-state trigger can archive exactly the sessions the rule matched (see [Archiving a Claude session](#archiving-a-claude-session-cli_archive_session-action)).
6. **Spawn an AI edit session.** Click the standard **+** launch button next to the SESSIONS header in the sidebar — the same button regular projects use. Omniscio starts a Claude session pre-seeded with this page, your current rule inventory as JSON, a REST cheatsheet for `/automation/*`, and a short-lived in-app token. Writes authorized with that token skip the CLI approval inbox and land as approved immediately — so "add a rule that archives newsletters from substack.com" turns into a live rule in seconds. The session shows up as a normal row under SESSIONS with full snooze + archive support; click it to focus the SessionPanel, click the standard close button to return to the rule lists.
7. **Test before letting it run.** Each rule has a small preview that echoes what it would have done against the most recent message matching the condition. Use this to catch regex mistakes before a real message arrives. Session-state triggers don't have a preview — set them to `recurring` first, watch the trigger count, and switch to `one_shot` once you trust them.
8. **Watch the analytics.** Both surfaces show trigger counts so you can tell if a rule is mis-firing (e.g., catching too much) or dead (matched zero times in a week).

### Configure via CLI

Agents can create, update, and test automation rules via the CLI server at `127.0.0.1:19519/automation/*` (see the [omniscio-control skill](/.claude/skills/omniscio-control/automations.md)). Rules created via CLI require one-tap approval in the Omniscio inbox before they fire — that's the user-in-the-loop guarantee. The skill documents all 13 endpoints, the rule schema, the idempotency key convention, and worked curl examples.

## How it behaves

### Auto-reply actions (beyond a plain reply)

An auto-reply rule matches text in a session's finished output — Claude **or** a non-Claude engine (Codex, Gemini, OpenCode, …) — and fires **exactly one** action. Pick it from the editor's **When it matches** dropdown:

| Action                   | What it does                                                                                                                                                                                                                                                                                                                                                              | Still pings you afterward? |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| **Reply** _(default)_    | Sends a message back into the session — free text or a Quick Reply snippet. This is the legacy behavior; every pre-existing rule is a reply.                                                                                                                                                                                                                              | No (the turn is handled)   |
| **Rename the session**   | Renames the session to a title you set (exactly like renaming it by hand).                                                                                                                                                                                                                                                                                                | Yes                        |
| **Archive the session**  | Stops the session's process and archives it. The archive chip reads **"Auto-reply rule (auto-archive)."**                                                                                                                                                                                                                                                                 | No (it's gone)             |
| **Start a new session**  | Spawns a fresh Claude session with a prompt you set, in the matched session's project (or one you pick). **Billable — it starts a real session each time it fires.**                                                                                                                                                                                                      | Yes                        |
| **Add an inbox note**    | Drops a dismissible note (title + optional body) in your inbox. Repeats from the same rule+session coalesce into one note.                                                                                                                                                                                                                                                | Yes                        |
| **Organize the session** | Tidies the session in one shot — sets any combination of a new **title**, a **library tag**, and an **inbox color** (the colored bar that shows on the session in the inbox and sidebar). Fill in just the parts you want; at least one is required. Each part is applied independently and best-effort, so if (say) a tag was deleted the title and color still get set. | Yes                        |

Every rule may also set an **Only in project** scope so it fires only for sessions in that one project.

**Loop-safety for the billable actions:** the two actions that spend money or re-drive the agent are loop-guarded. **"Start a new session"** is guarded three ways — a session that an auto-reply rule itself spawned can never trigger another spawn (so a spawned session echoing the trigger word won't cascade); the same rule can't fire twice for the same session within a short window (so a duplicated turn-completion can't double-spawn); and each rule can spawn at most **50 sessions per day**, past which it pauses until local midnight and drops a deduped inbox card — a backstop against a mis-scoped rule that spawns across a whole fleet. **"Reply"** is guarded too — it spends a billable Claude turn every time it fires: (1) a rule only matches the turn that just **finished** (`session.turnOutputBuffer`), never a keyword that already scrolled out of an earlier turn; and (2) consecutive replies to turns that make **no progress** — a "nothing to do" / status-only reply, via `detectBackgroundAck` — are capped at `MAX_CONSECUTIVE_NO_PROGRESS_AUTO_REPLIES` (3), after which the reply is skipped and the session surfaces to you with a kinded `auto-reply-loop-capped` breadcrumb. A substantive turn — the agent doing real work — resets that counter, so a productive hands-free autonomous run never trips it; the cap is shared by Claude and non-Claude sessions. Unlike a spawn, a **reply is NOT subject to the per-rule daily cap** — those `reply-loop-safety-bounded` guards plus your budget/spend caps bound it, so a legitimate high-volume rule (e.g. a dev-pipeline gate auto-approver that replies once per run) is never paused for the day. See [away-mode-rich-actions-contract.md](/.claude/memory/contracts/away-mode-rich-actions-contract.md) `reply-loop-safety-bounded`. The other four actions (rename / archive / inbox note / organize) are cheap and don't re-drive the agent, so they need no cap.

### Giving a session a color

You can paint any session a color of your choosing. The color shows up as a thin **colored bar at the left edge** of that session's row — everywhere the session appears: the inbox feed, the desktop sidebar, and the mobile list. The bar sits next to the usual status dot (green = running, amber = needs you, red = error); it never replaces it, so you still see at a glance whether the session needs attention. Use it to flag a few sessions visually — "these three are the client work", "this one is the experiment I keep losing".

There are **two ways** to set a color:

- **By hand** — right-click any session and pick **Set color**, then choose a swatch from the color picker. To remove a color again, open the same picker and click **No color** (the bar disappears and the session goes back to inheriting its normal status color). Setting or clearing a color is undoable.
- **Automatically** — the **Organize the session** auto-reply action (above) can set a color whenever a rule matches, on its own or together with a new title and a tag.

A color you set sticks with the session until you change or clear it, and it updates live in every open window the moment it changes.

### Spawning a CLI session from a message (cli_session action)

The **`cli_session`** action spawns a fresh Claude Code session in an Omniscio project when a rule's condition matches — the canonical use case is _"GitHub Actions failure email arrives → spawn a Claude session in the matching project to investigate and fix it."_ Two fields control how the session opens:

- **`prompt`** — the initial message the session receives. Supports the same template vars as `notify` / `forward_email` / `auto_respond`: `{{body}}`, `{{subject}}`, `{{sender}}`, `{{channel}}`. Substitution happens at fire time, so the agent gets the verbatim email body / SMS text / Slack message instead of a generic placeholder.
- **`projectResolution`** — `'static'` (default — the session opens in the project named by the action's `projectId` field; existing automations are unchanged) or `'auto'`. With `'auto'`, Omniscio scans the message body for a GitHub URL (`github.com/<owner>/<repo>/...`), strips a trailing `.git`, normalizes the repo name (lowercased; dashes, underscores, spaces, dots removed), and fuzzy-matches against your registered projects in two passes — first an **exact normalized match** against project names AND folder names (last path segment), then a **substring match**. So a message body containing `https://github.com/me/reddit-scout/actions/runs/123` resolves to the project named `Reddit Scout` (or the folder `reddit_scout/`). If no GitHub URL is present or no project matches, the executor logs a warning and falls back to the configured `projectId` so the rule still fires — auto-resolution is best-effort, never a blocker.

Together these turn "GitHub Actions Failure Auto-Fix" into a single rule: condition matches the GitHub email, action `cli_session` with `projectResolution: 'auto'` and a templated prompt like `Investigate the failed CI run mentioned in this email:\n\n{{body}}` opens a session in the right repo's project with the failure email injected as context. **Backtest** ([next section](#backtest-a-rule-against-the-last-24-hours)) renders both the resolved `projectId` and the rendered prompt in its dry-run preview, so you can verify the routing and substitution before any real session spawns. The implementation lives at [/src/main/services/automation/legacy/automation-action-executor.ts](/src/main/services/automation/legacy/automation-action-executor.ts) — `renderTemplate(config.prompt, msg, triggerContext)` for substitution and `resolveProjectFromMessage(body)` for fuzzy lookup.

### Archiving a Claude session (cli_archive_session action)

The **`cli_archive_session`** action archives a Claude Code session — the same archive the sidebar performs: it kills the session's running process, flips its status to _archived_, and stamps the archive-attribution chip as **"Automation rule (auto-archive)"** so a glance at the archive tells you why the session was put away. Use it for housekeeping — keeping the sidebar clear of sessions that have served their purpose. It's available on **every** trigger type (message / manual / session-state / PM board event).

A single **target selector** decides _which_ session is archived:

- **The sessions this rule matched** (`target: 'matched'`) — only meaningful on an **`on_session_state`** trigger. When the rule fires because every scoped session reached one of its matched statuses, this archives exactly those sessions. This is the canonical "auto-archive finished sessions" rule: scope = a single session or a hand-picked set, conditions = `ended` (optionally + `error`), one action = Archive Session targeting the matched sessions. The radio is hidden on message / schedule triggers because there are no matched sessions to act on, so the action uses the specific-session target there.
- **A specific session** (`target: 'specific'`, the default) — you pick one session from a dropdown when building the rule, and it's archived whenever the rule fires. Works on any trigger. Because sessions are ephemeral, a hard-coded target ages out over time — the durable, set-and-forget case is _matched sessions on a session-state trigger_.

The action is **idempotent and safe**: a target that's already archived, deleted, paused, or missing is skipped with a note (never an error) and simply doesn't count toward the archived total — exactly like the Send-to-CLI-Session action, so a stale rule never breaks the chain. **Backtest** renders the action in its dry-run preview without archiving anything real. Implementation: the `cli_archive_session` arm in [/src/main/services/automation/legacy/automation-action-executor.ts](/src/main/services/automation/legacy/automation-action-executor.ts) resolves the target ids (from the trigger's matched sessions or the configured `sessionId`) and calls the canonical `archiveSessionService(id, 'automation-rule')` from [/src/main/services/session/session-lifecycle-service.ts](/src/main/services/session/session-lifecycle-service.ts) — the same path the sidebar archive uses.

### Sending an outbound HTTP request (http.request action)

The **`http.request`** action makes Omniscio **call a web address for you** when a rule matches —
the action to reach when the thing that needs to happen next lives in some other service rather
than in Omniscio. Typical uses: ping a webhook, post a note into a system that only accepts HTTP,
or kick off a job somewhere else.

**Type `http.request`** in the action's type field, then fill in:

- **Method** — `GET`, `POST`, `PUT`, `PATCH` or `DELETE`.
- **URL** — the address to call. It **must be `http://` or `https://`**; anything else is refused
  rather than attempted. The URL and the other text fields accept the same template variables as
  every other action (`{{sender}}`, `{{body}}`, `{{channel}}`, `{{subject}}`), so you can put
  something from the message into the address.
- **Headers** *(optional)* — one `Name: value` per line.
- **Body** *(optional)* — sent as the request body, as plain text or a JSON string.
- **Auth credential** *(optional)* — pick a **stored credential** instead of pasting a token into
  the headers. This is the part worth using: the credential is kept in the same encrypted store as
  your other automation credentials and referenced by id, so **the secret never appears in the rule
  itself**. Three schemes are supported — **bearer** (a token), **basic** (username + password), and
  **header** (your own header name + value). Leaving the credential out is a normal, valid choice:
  plenty of public endpoints need none. See [Automation credentials](automation-credentials.md).

Four things about this action are worth knowing before you rely on it:

- **It will not call your own machine or your private network.** Addresses that point at the local
  machine, a private network, or a cloud metadata endpoint are **refused before the request is
  made** — a stored rule must not become a way to reach services that are not otherwise reachable.
  The same check is re-applied to every redirect, so a public address cannot bounce the request
  inward. A request to such an address fails with *"Refused to fetch private or internal network
  address"*.
- **It is never retried automatically.** Every other outbound action can be retried by the engine;
  this one deliberately is not, because the address is arbitrary and repeating a request that may
  have already taken effect is worse than reporting the failure once.
- **A slow endpoint gives up after about 25 seconds**, and only the first part of the reply body is
  kept — enough to see what came back, not to move a large payload through a rule.
- **The stored credential is masked in previews.** A backtest shows `***` in place of the
  authorization header, so a preview you screenshot or paste into a log cannot leak it.

The request is sent as the automation fires. **Backtest** renders this action like any other, so you
can see the resolved URL and body before anything real is sent.

This action is currently reachable by **authoring the action type directly**; it is not one of the
types offered in the UI's action picker.

### Preview AI replies before they send

An Automation's **reply** action can write its reply with **AI** (`useAi`). Because that text is
generated from the incoming message, a hostile inbound message could try to steer the model into
replying with a scam link or a request for private info — which would then go out under **your**
identity. To close that, Omniscio has a default-on setting, **Review AI replies before sending**
(`autoRespondPreviewBeforeSend`), in the Automation settings (Settings search: "preview before send").

- **On (default):** an AI-written auto-reply is **not sent** — it lands in your **inbox as an
  approve/reject card** showing the exact reply text and the destination conversation. **Approve**
  sends it; **Reject** drops it. Approving is reachable from your phone too.
- **Off:** AI replies send immediately (the earlier behavior).
- Only genuine **AI** replies are held. A plain **template** reply (including the fallback used when
  the automation AI daily-spend cap is reached) always sends immediately.
- The reply you approve is exactly what will send, and the destination is read from the saved card —
  never from the click — so nothing can be tricked into redirecting or rewriting a queued reply. On
  approve the reply sends then the card clears; if the channel is offline the card stays so you can
  retry or reject.
- This is **Layer 1**. A link-allowlist and output-side exfiltration detection are deliberately
  deferred follow-up layers. Full invariants:
  [auto-reply-preview-before-send-contract.md](/.claude/memory/contracts/auto-reply-preview-before-send-contract.md).

### Backtest a rule against the last 24 hours

Open the **Automations** virtual project → click any rule row to expand its inline editor → expand the **Backtest (last 24 hours)** card → click **Run Backtest**. Omniscio replays every message that arrived on the rule's channels in the last 24 hours (capped at 100 most-recent messages) against your in-progress draft and shows two things per match: the rule's confidence (for AI conditions in Phase B) and the **rendered preview** of every action in the chain — the literal email subject and body a `forward_email` would send, the templated reply text an `auto_respond` would post, the label name an `archive` would apply, etc. **No real side effects fire.** Backtest runs through the same executor as live automations but with a dryRun gate that short-circuits each action arm before it touches the network or the database.

The list also flags any match whose message historically triggered the live rule with an **"Already triggered"** badge — useful for distinguishing "this rule worked" from "this rule would also have caught these other messages I missed." Cancelling a long scan is one click; the backend stops between messages and reports how many were evaluated before the cancel.

**Phase A scope (current):**

- Window: last 24 hours, max 100 messages, on-message rules only
- Channels: SMS, Slack, Telegram. Gmail is unsupported because the inbound tracking table doesn't store message bodies (only metadata)
- Conditions: rule-method only. AI-method conditions return an error ("AI conditions require Phase B") because re-classifying 100 messages against a draft is expensive and needs a cache
- Approval status is ignored — backtest runs whether the rule is pending, approved, or rejected. It's a design tool, not an execution
- Single-rule only — `stopOnMatch` interactions across multiple rules are not simulated
- Attachments are not matched — body, sender, subject, channel only

**How it works:** [/src/main/services/automation/legacy/automation-backtest-service.ts](/src/main/services/automation/legacy/automation-backtest-service.ts) pulls recent messages via `getRecentSmsMessages` / `getRecentSlackMessages` / `getRecentTelegramMessages` from their per-channel query modules ([queries-sms/messages.ts](/src/main/db/queries-sms/messages.ts) / [queries-slack.ts](/src/main/db/queries-slack.ts) / [queries-telegram.ts](/src/main/db/queries-telegram.ts)), evaluates each through the standard pattern matcher, then invokes [/src/main/services/automation/legacy/automation-action-executor.ts](/src/main/services/automation/legacy/automation-action-executor.ts) with `dryRun: true`. The dryRun branch in every action arm renders the action body (template substitution, recipient resolution) but returns before the side effect. UI lives in [/src/renderer/src/features/settings/BacktestPanel.tsx](/src/renderer/src/features/settings/BacktestPanel.tsx) and is mounted lazy-loaded inside [/src/renderer/src/features/automations/components/AutomationRuleEditor.tsx](/src/renderer/src/features/automations/components/AutomationRuleEditor.tsx) Card 6 (gated behind `triggerType === 'on_message'`).

### Which senders can trigger an automation from email

An email's sender is a `From:` line, and a `From:` line is something anyone can write — so
an email-triggered automation only spends money for a sender the mail system has actually
vouched for.

- **The sender is matched by address, not by name.** A rule like `sender contains
someone@acme.com` matches that exact address. It no longer matches a message whose
  display name happens to contain that text, and it no longer matches a lookalike domain
  such as `someone@acme.com.example.net`.
- **A paid action needs an authenticated sender.** Before an email can start a session,
  reply to the sender, or run a paid recipe, the receiving mail host has to confirm the
  message really came from the domain in its `From:` line (DMARC aligned with the From
  domain). If it cannot, the message still arrives and free actions — archiving,
  labelling, notifying, summarizing — still run, but the money-spending actions are held
  back and you get one inbox notice naming the channel.
- **This only applies to email.** Text-message senders keep matching the way they always
  have, including a contact name.

If an automation you rely on stops firing, the most likely cause is the third point: the
sending domain publishes no mail-authentication record, so its mail can never be
confirmed. The notice in your inbox names it.

### When an action permanently fails (the automation dead-letter queue)

An action that talks to the outside world — posting a Slack message, calling a webhook, sending
mail — can fail for a reason that is worth retrying: a timeout, a 503, a dropped connection. Those
are retried a **bounded** number of times. If every retry fails, the action is neither dropped
silently nor retried forever: it is written to a durable **automation dead-letter queue**, and the
rest of the rule's action chain still runs. This is the one place you can learn that a message never
went out or a webhook never landed.

Two things happen when something is set aside:

- **An inbox alert.** **"Some automation actions could not be completed"** carries the running count
  and climbs as more accumulate. It is deduped (one row, updated) and **clears itself** when the
  queue empties.
- **A 30-day retention window.** Set-aside actions are purged on the same 30-day maintenance cadence
  as the rest of the automation history, so the queue cannot grow without bound.

**The honest limit: there is no per-item screen for this queue today.** It is durable and counted,
but nothing in the app or over the CLI lists an individual set-aside action — the count in that alert
is the whole of what you can see. If an action you care about was set aside, the alert is your
signal, re-running the rule is the fix, and the raw cause is in the app log.

**Naming caution.** "Dead letter" names three *other* queues in Omniscio —
[Team Chat's failed scheduled messages](team-chat-dead-letter-recovery.md), undelivered
[agent-to-agent messages](cross-session-messaging-part-2.md), and [PM webhooks](pm-webhooks.md).
This is the automation one. If you searched the term and landed on one of those, that is why.

## Related

- [dev-pipeline-panel.md](dev-pipeline-panel.md) — the Dev Pipeline can **seed a starter set** of workflow auto-replies for you (the opt-in "Add the workflow auto-replies" companion under Settings → Features → Enable Dev Pipeline skill); it skips any trigger you already have and, turned off, removes only the ones it added. The matcher's quote/code-strip behavior above is locked by [away-mode-rich-actions-contract.md](/.claude/memory/contracts/away-mode-rich-actions-contract.md) `matcher-ignores-code-and-quoted-spans`.
- [automation-credentials.md](automation-credentials.md) — where v2 actions (Slack, GitHub, SMTP, etc.) load their secrets from; actions reference a credential by id rather than embedding the secret in the action config
- [quick-replies.md](quick-replies.md) — the paired virtual project for authoring the snippet library that Auto-replies rules and `auto_respond` actions cite
- [use-quick-responses.md](use-quick-responses.md) — composer-side UX for using snippets (Alt+S picker, AI chips, Zap button)
- [daily-digest.md](daily-digest.md) — the digest summarizes what your automations did yesterday
- [Automations & Auto-replies (part 2)](automations-and-auto-replies-part-2.md) — the rest of this page: the auto-response badge taxonomy that names which automated path produced a system message, the per-step activity-log breakdown for v2 runs, and the implementation map of the engines, stores, watchers and IPC channels behind both kinds of rule.
