When channels.matrix.streaming.mode is "quiet", Apus streams the reply by editing a single preview event in place. Previews are sent as non-notifying m.notice events, and the finalized edit is marked with content["com.apus.finalized_preview"] = true. Matrix clients notify on that final edit only if a per-user push rule matches the marker. This page is for operators who self-host Matrix and want to install that rule for each recipient account.

streaming.mode: "progress" finalizes its drafts through the same path, so the same rule also fires for progress-mode finalized edits.

If you only want stock Matrix notification behavior, use streaming.mode: "partial" or leave streaming off. See Matrix message behavior.

Prerequisites

Steps

Configure quiet previews

{
  channels: {
    matrix: {
      streaming: { mode: "quiet" },
    },
  },
}

Get the recipient's access token

Reuse an existing client session token where possible. To mint a fresh one:
curl -sS -X POST \
  "https://matrix.example.org/_matrix/client/v3/login" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "m.login.password",
    "identifier": { "type": "m.id.user", "user": "@alice:example.org" },
    "password": "REDACTED"
  }'
Replace `https://matrix.example.org` with your homeserver base URL and
`@alice:example.org` with the recipient's MXID. Export the `access_token`
from the response as `USER_ACCESS_TOKEN`; the remaining steps read it:
export USER_ACCESS_TOKEN="<access_token from the login response>"

Verify pushers exist

curl -sS \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN" \
  "https://matrix.example.org/_matrix/client/v3/pushers"

If no pushers come back, fix normal Matrix push delivery for this account before continuing.

Install the override push rule

Install a rule that matches the finalized-preview marker plus the bot MXID as sender:
curl -sS -X PUT \
  "https://matrix.example.org/_matrix/client/v3/pushrules/global/override/apus-finalized-preview-botname" \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "conditions": [
      { "kind": "event_match", "key": "type", "pattern": "m.room.message" },
      {
        "kind": "event_property_is",
        "key": "content.m\\.relates_to.rel_type",
        "value": "m.replace"
      },
      {
        "kind": "event_property_is",
        "key": "content.com\\.apus\\.finalized_preview",
        "value": true
      },
      { "kind": "event_match", "key": "sender", "pattern": "@bot:example.org" }
    ],
    "actions": [
      "notify",
      { "set_tweak": "sound", "value": "default" },
      { "set_tweak": "highlight", "value": false }
    ]
  }'
Replace before running:

- `https://matrix.example.org`: your homeserver base URL
- `$USER_ACCESS_TOKEN`: the recipient user's access token
- `apus-finalized-preview-botname`: a rule ID unique per bot per recipient (pattern: `apus-finalized-preview-<botname>`)
- `@bot:example.org`: your Apus bot MXID, not the recipient's

Verify

curl -sS \
  -H "Authorization: Bearer $USER_ACCESS_TOKEN" \
  "https://matrix.example.org/_matrix/client/v3/pushrules/global/override/apus-finalized-preview-botname"

Then test a streamed reply. In quiet mode the room shows a quiet draft preview and notifies once the block or turn finishes.

To remove the rule later, DELETE the same rule URL with the recipient's token.

Multi-bot notes

Push rules are keyed by ruleId: re-running PUT against the same ID updates a single rule. For multiple Apus bots notifying the same recipient, create one rule per bot with a distinct sender match.

New user-defined override rules are inserted ahead of server-default suppress rules, so no extra ordering parameter is needed. The rule only affects text-only preview edits that can be finalized in place. Media replies, stale-preview fallbacks, and final texts that would activate Matrix mentions are delivered as normal notifying messages instead.

Homeserver notes

Synapse
No special `homeserver.yaml` change is required. If normal Matrix notifications already reach this user, the recipient token + `pushrules` call above is the main setup step.

If you run Synapse behind a reverse proxy or workers, make sure `/_matrix/client/.../pushrules/` reaches Synapse correctly. Push delivery is handled by the main process or `synapse.app.pusher` / configured pusher workers — ensure those are healthy.

The rule uses the `event_property_is` push-rule condition (MSC3758, push rule v1.10), which was added to Synapse in 2023. Older Synapse releases accept the `PUT pushrules/...` call but silently never match the condition — upgrade Synapse if no notification arrives on a finalized preview edit.
Tuwunel
Same flow as Synapse. No Tuwunel-specific config is needed for the finalized preview marker.

If notifications disappear while the user is active on another device, check whether `suppress_push_when_active` is enabled. Tuwunel added this option in 1.4.2 (September 2025) and it can intentionally suppress pushes to other devices while one device is active.

Related