Guide · 2 min read · Updated
A $0 double opt-in newsletter with PocketBase and Brevo
Every site we run has a newsletter sign-up, and none of them pays for it. The database is PocketBase on the same $5 server as the sites. Emails are sent through Brevo’s free tier, which allows 300 emails a day. Here’s how it fits together.
The flow
- Sign-up. The form posts an email address to
/api/subscribeon the site’s own domain. The web server passes that path to PocketBase. - Pending record. A PocketBase hook validates the address and stores it as
pending, with the page it came from, the consent text shown, the time, and a random 32-character token. - Confirmation email. The hook calls Brevo’s email API to send one message with a confirm link (
/api/confirm?t=…) and an unsubscribe link. - Confirm. Clicking the link marks the record
confirmedand redirects to a thank-you page. - Unsubscribe. Every email links to
/api/unsubscribe?t=…, which marks the recordunsubscribed.
One PocketBase serves all six sites. The hook reads the request’s host name to decide which site’s wording, sender address and colours to use.
The details that matter
- No list leaks. The form gives the same “check your inbox” answer whether or not the address was already subscribed, so nobody can use it to test who is on the list.
- A honeypot field. The form has a hidden field that people never see. Bots fill it in, and the hook quietly accepts those sign-ups and throws them away.
- Rate limits. At most 5 sign-ups an hour from the same visitor, and at most one confirmation email to the same address every 10 minutes. This stops the form being used to flood someone’s inbox. The visitor is a hash of the IP address and browser, never the raw IP.
- One-click unsubscribe. Emails carry
List-UnsubscribeandList-Unsubscribe-Postheaders (RFC 8058). This lets Gmail and others show their own unsubscribe button. Large mailbox providers now expect bulk senders to have it. - Works without JavaScript. The form is a normal HTML form post that redirects to a thank-you page. With JavaScript it sends the same request in the background and shows the message inline.
Sending from your own domain
Brevo will only send “from” your domain once you prove you own it. You add a verification code, a DKIM record and a DMARC record to the domain’s DNS. Then register the exact sender address in Brevo. If you skip that step, Brevo rewrites the From address to one of its own domains, which looks odd to subscribers. We learned that the hard way on our first site.
Brevo also has a list of server IP addresses allowed to use your API key. Add your server’s IPv4 and IPv6 addresses, or sending fails, and the only sign is an error in your logs.
What it costs
| Item | Per month |
|---|---|
| PocketBase (on the existing server) | $0 |
| Brevo free tier, up to 300 emails a day | $0 |
| Domain DNS records | $0 |
The limit is the 300 emails a day. Past that, Brevo’s Starter plan begins at $9 a month. You can add it to the email line in the AI app cost calculator.
Questions
Why double opt-in?
Anyone can type anyone's address into a form. Asking people to click a link first proves the address is theirs, keeps fake and mistyped addresses off the list, and gives you a record of consent.
When does Brevo's free tier stop being enough?
The free tier sends up to 300 emails a day. A monthly newsletter to more than 300 people has to go out over several days or move to a paid plan. Brevo's Starter plan starts at $9 a month for 5,000 emails.
Do I need a mailbox on my domain?
Not to send. You need the domain's DNS records that prove Brevo may send for it (DKIM and DMARC, plus Brevo's verification code). Receiving replies needs a mailbox or forwarding.
Sources
- Brevo pricing . Brevo
- PocketBase documentation: JavaScript routing . PocketBase
- RFC 8058: Signaling one-click functionality for list email headers . IETF
Prices in this guide were checked on the date shown above and change often. Check each provider's pricing page before you commit.