fepli
Hostingenterprise

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

  1. 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 with APP_SECRET. The domain is the organisation's reply domain. Every other e-mail keeps its usual reply address.
  2. The MX records of the reply domain deliver the reply to Brevo.
  3. Brevo posts it to the webhook registered for that domain, on the organisation's own domain: https://example.fepli.eu/webhook/brevo_inbound?secret=….
  4. 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_URL on 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:

HostTypePriorityValue
reply.example.fepli.euMX10inbound1.sendinblue.com.
reply.example.fepli.euMX20inbound2.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 with openssl 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:

FieldValue
typeinbound
eventsinboundEmailProcessed
domainthe reply domain
urlhttps://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. --force deletes 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 --force for that tenant.
  • It skips the webhook, with a warning, while INBOUND_SECRET is 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=all also 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.

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 seeWhy
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.

Was this page helpful?