E-mail replies
When an admin writes to a host or a family from a conversation in the admin, fepli sends the message by e-mail. With e-mail replies set up, the recipient answers by replying to that e-mail: fepli receives the reply through Brevo and adds it to the conversation, as a comment with the channel email. Without it, replies go to the organisation's reply address, like replies to any other e-mail fepli sends.
This page is for integrators who run the fepli image. If fepli hosts your instance, you only add DNS records: see Instances fepli hosts.
How a reply comes back
- Only the e-mail Nachricht aus einer Unterhaltung gets a reply address of its own, e.g.
kommentar+0192d0e83a4b7c5d9e6f7a8b9c0d1e07.4f2a9c1e0b7d3a65@reply.example.fepli.eu. The part after the+names the message, signed withAPP_SECRET. The domain is the organisation's reply domain. Every other e-mail keeps its usual reply address. - The MX records of the reply domain deliver the reply to Brevo.
- Brevo posts it to the webhook registered for that domain, on the organisation's own domain:
https://example.fepli.eu/webhook/brevo_inbound?secret=…. - fepli checks the secret in the URL and the signature in the address, keeps the new text without the quoted e-mail, and adds it to the conversation. When the sender's address belongs to an account, the reply is signed with that account, otherwise with the sender's name. Admins get a notification in the admin.
Brevo posts every reply for a domain to one webhook, and fepli handles a webhook request for the tenant whose domain it arrives at. So on a multi-tenant installation, each tenant has a reply domain and a webhook of its own.
Changing APP_SECRET also changes the signatures. Replies to e-mails sent
before the change no longer reach fepli.
Set it up on your installation
You need:
- A Brevo account and an API key of it. Brevo only receives the replies: fepli keeps sending its e-mails through the server in
MAILER_DSN. - A reply domain, e.g.
reply.example.fepli.eu, in a DNS zone you control: one for the installation, or one per tenant on a multi-tenant installation. It must differ from the domain fepli sends from, and nothing else may receive mail for it. - Domains Brevo can reach over HTTPS: the webhook is on the tenant's own domain, or on the domain in
APP_BASE_URLon a single-tenant installation.
1. Verify the reply domain with Brevo
Brevo receives mail only for domains it has verified. Add the reply domain in Brevo under Senders, Domains & IPs → Domains, and add the records Brevo lists for it to your DNS zone. Brevo's help explains the records.
2. Point its MX records at Brevo
Every reply goes to an address of its own under the reply domain, so the whole domain must deliver to Brevo:
| Host | Type | Priority | Value |
|---|---|---|---|
reply.example.fepli.eu | MX | 10 | inbound1.sendinblue.com. |
reply.example.fepli.eu | MX | 20 | inbound2.sendinblue.com. |
DNS changes can take hours to spread, so add the records early.
3. Configure fepli
Set these variables, in the same env file for the web, the worker and the cron, and restart the containers. The worker writes the reply address into the e-mails; the web container receives the replies.
- Name
INBOUND_SECRET- Type
- string
- Description
A secret that guards the webhooks: fepli rejects requests whose URL doesn't carry it, with
403. Generate it withopenssl rand -hex 32. Empty switches e-mail replies off, for every tenant.
- Name
INBOUND_DOMAIN- Type
- host
- Description
The reply domain, on a single-tenant installation only, e.g.
reply.example.fepli.eu. Empty switches e-mail replies off. A multi-tenant installation ignores it.
- Name
BREVO_API_KEY- Type
- string
- Description
The API key of your Brevo account. fepli registers the webhooks with it and downloads attachments with it. The same key sends the text messages of every tenant, over the key admins store in the admin (see Optional).
On a multi-tenant installation, each tenant names its reply domain in the admin, as a domain with the purpose E-Mail-Empfang. Its super admins do that under Einstellungen → Allgemein → Systemkonfiguration; global admins reach the same page from Installation → Mandanten in the platform console. Like a sending domain, a reply domain serves no request, so it doesn't go into TRUSTED_HOSTS.
4. Register the webhooks
Preview what fepli would send to Brevo, then register the webhooks:
docker compose exec web php bin/console brevo --webhook=inbound --dry-run
docker compose exec web php bin/console brevo --webhook=inbound
The command creates one webhook per reply domain in the Brevo account of BREVO_API_KEY:
| Field | Value |
|---|---|
| type | inbound |
| events | inboundEmailProcessed |
| domain | the reply domain |
| url | https://example.fepli.eu/webhook/brevo_inbound?secret=<INBOUND_SECRET>, on the organisation's own domain |
- On a multi-tenant installation, the command registers a webhook for every tenant that has a reply domain, on that tenant's own domain. It skips tenants that are offline. With
--tenant=<slug>, it registers only that tenant's webhook. - fepli answers a tenant's webhook on every domain it serves for the tenant, also on one that serves only the admin. The platform console doesn't answer webhooks.
- On a single-tenant installation, the webhook is on the domain in
APP_BASE_URL. - Running the command again changes nothing while a webhook exists.
--forcedeletes it and creates it anew. - When Brevo holds the webhook for another reply domain, e.g. after the tenant changed its domain, the command says so. Run it again with
--forcefor that tenant. - It skips the webhook, with a warning, while
INBOUND_SECRETis empty or the reply domain is missing. - If fepli is behind HTTP basic auth, add
--auth=user:pass. Brevo then sends the credentials in the URL. - After you change
INBOUND_SECRET, run the command again. It creates webhooks with the new secret; delete the ones with the old secret under Webhooks in Brevo. - Without
--webhook, the command asks which webhook to register.--webhook=allalso registers the one for delivery reports.
You can also create a webhook by hand under Webhooks in Brevo, with the same values.
5. Switch it on in the admin
Once INBOUND_SECRET is set, super admins find Antworten im System empfangen in the admin under Einstellungen → Allgemein → E-Mail. On a single-tenant installation, it also needs INBOUND_DOMAIN. It is on by default. With tenants, a tenant that has it on but no reply domain sees a note there that it needs one, with a link to its domains. The Handbuch describes the setting.
On a multi-tenant installation, the platform console shows how many tenants have a reply domain, under Installation → Systemwerte at Eingang für Antworten.
Instances fepli hosts
fepli runs the Brevo account and registers the webhooks. Your part is the DNS of your domains:
- The domain you send from, the domain of your sender address under Einstellungen → Allgemein → E-Mail. Brevo sends only from domains it has verified. Its records, a Brevo code, DKIM and DMARC, belong to the Brevo account that sends, which is fepli's.
- A reply domain, in one of two ways:
- a subdomain of your own, e.g.
reply.in front of your domain. Brevo verifies it with the same kind of records, and it gets the two MX records from step 2 above. Add it under Einstellungen → Allgemein → Systemkonfiguration as a domain with the purpose E-Mail-Empfang. - a subdomain fepli runs for you, e.g.
reply.musterstadt.fepli.eu. The fepli team sets it up, and you add no records for it.
- a subdomain of your own, e.g.
To get the records for your domains, or a subdomain from fepli, write to support@fepli.de. Replies arrive once the records are in place and fepli has registered the webhook for your reply domain.
Attachments
Replies can carry files, e.g. a signed form. fepli downloads them from Brevo, checks them and stores them with the other uploads: in the storage volume, or in the files bucket if you configure object storage. Admins download them from the conversation; the browser never shows them.
- At most 10 files per reply, each up to 15 MB.
- PDF, JPEG, PNG, GIF, WebP, HEIC, TXT, CSV, DOC, XLS, PPT, DOCX, XLSX, PPTX, ODT and ODS. The content of a file has to match its extension.
- fepli drops every other file and logs it. The reply itself still arrives.
The files hold what families and hosts sent: include them in your backups.
When replies don't arrive
Every request to a webhook leaves a line in the web container's log, docker compose logs web: Webhook accepted or Webhook rejected, with the status.
| What you see | Why |
|---|---|
| The admin doesn't show Antworten im System empfangen. | INBOUND_SECRET is empty in the web container, or, on a single-tenant installation, INBOUND_DOMAIN. |
| Antworten im System empfangen shows a note instead of the reply address. | The tenant has no domain with the purpose E-Mail-Empfang. |
The e-mail carries the organisation's reply address, not a kommentar+… one. | Antworten im System empfangen is off, the tenant has no reply domain, or the worker lacks the variables. |
| No webhook line in the log. | Brevo doesn't receive the replies or doesn't forward them: the MX records aren't in place yet, the reply domain isn't verified, or its webhook isn't registered. |
Webhook rejected with 403. | The secret in the webhook's URL isn't INBOUND_SECRET. Register the webhook again. |
Inbound reply rejected: no conversation for token. | The webhook for this reply domain points at another tenant's domain, e.g. after the domain moved to another tenant. Run the command with --force. The message may also have been deleted. |
Inbound reply rejected: invalid or expired token. | The reply answers an e-mail sent before APP_SECRET changed, or the reply address was altered on the way. |