# Notifications and email

Members are told about replies in two places: in the site, and by email. The
reasoning behind every decision here is in the
[design note](../design/2026-08-05-following-and-email.md).

---

## Email needs a cron entry

**Convoro never sends email from inside a web request.** A topic with two
hundred followers would be two hundred SMTP conversations inside the request of
whoever pressed Post — a minute of spinner, a timeout, and a member who thinks
the forum is broken.

Mail is queued into a table and sent by a command. **Without this cron entry
no email ever leaves the site:**

```bash
* * * * * cd /path/to/convoro && php tools/convoro mail:send >/dev/null 2>&1
```

Once a minute is fine. The command is silent when there is nothing to do, so it
will not fill anybody's mailbox with reports of having done nothing.

### Checking on it

```bash
php tools/convoro mail:send --status
```

| State | Meaning |
|---|---|
| `pending` | waiting for the next run |
| `sending` | a run has claimed it right now |
| `sent` | delivered; pruned after 7 days |
| `failed` | gave up after 5 attempts — `last_error` says why |

Failed rows are kept deliberately. They are the ones somebody needs to look at.

A run killed mid-send leaves rows marked `sending`; they are returned to the
queue automatically after 15 minutes, because mail that silently stops going
out is worse than mail sent twice.

Two overlapping runs cannot send the same message: a row is claimed before it
is sent.

---

## Following

**Replying to a thread follows it.** That is what somebody meant by replying,
and a forum that makes you tick a box as well is a forum where nobody gets told
anything. Members can turn that off under Settings → Notifications.

A follow can be set to **notify here**, **email me**, **both**, or **not
following**, from the control at the top of any topic or forum.

**Un-following sticks.** It is stored as a muted follow rather than a deleted
one, so somebody's next reply cannot drag them back into a conversation they
deliberately left.

### One email until they have been back

If a thread gets six replies before somebody reads their inbox, they get **one**
email, not six. Reading the topic is what re-arms it.

This is the cheapest possible digest, it needs no scheduling, and it is the
difference between useful and unsubscribed.

---

## Unsubscribing

Every notification email carries a link that works **without signing in**.
Somebody who wants out will not log in to find a settings page — they will mark
it as spam, and that hurts delivery for everybody else on the site.

- With a thread on the link: mutes that thread only.
- Without one: turns email off entirely, **without un-following anything**.
  "Stop emailing me" is not "stop telling me".

Each member has an `unsubscribe_token`. Clearing it in the database invalidates
every link already sent to them.

---

## For administrators

There is no site-wide "email everybody" switch and no digest. What exists:

- `default_follow_mode` (setting): how a follow created by taking part is set
  up. `in_app` unless changed.
- Per member: `email_notifications` and `auto_follow`, both on by default and
  both editable by the member.

SMTP is configured under **Admin → Settings → Email**, and the *Send a test
email* button there is the fastest way to find out whether it works. Note the
test sends immediately rather than queueing, so a failure shows up straight
away rather than in the outbox.

## What this does not do

- **No digests.** "Everything from this week in one email" needs scheduling and
  a real answer about what counts as a week. The outbox makes it possible.
- **No following people**, which has its own privacy questions.
- **No web push.** Service workers, VAPID keys and a permission prompt deserve
  their own design note.
