Webhooks: send sign-ups, confirmations and sent issues to Zapier, Make or your own system
How Brieva webhooks work: the four events, the JSON envelope, the signed header and how to verify it, and step-by-step setup for Zapier and Make.
The short version
- A webhook is a URL Brieva POSTs to whenever something happens to a newsletter: a sign-up, a confirmation, an unsubscribe, or an issue going out.
- Every request is JSON with the same envelope, and carries an X-Brieva-Signature header (HMAC-SHA256 of the body) so you can prove it came from Brieva.
- Up to five webhooks per newsletter. Zapier and Make catch-hook URLs work with no code.
- Brieva waits four seconds for a 2xx reply, records the result on the webhook, and never retries. Your side should accept quickly and process later.
- The signing secret is shown once, when you create the webhook. Keep it; you cannot read it again.
What a webhook is for
Your subscriber list lives in Brieva, but the rest of your business lives elsewhere: a CRM, a spreadsheet, a Slack channel, an accounting tool with a customer list. A webhook is the simplest way to keep them in step without anyone copying rows by hand. When a reader confirms their subscription, Brieva tells your endpoint straight away, and your endpoint can do whatever you like with that fact.
The most common uses are: add confirmed readers to a CRM or mailing tool you already use; post a line in Slack when someone unsubscribes so you notice churn; log every sent issue to a sheet for reporting; or trigger a welcome sequence in another tool.
The four events
subscribe: a reader has submitted the sign-up form or been imported from a CSV. Their status is pending. They have not yet clicked the confirmation email, so do not treat this as consent to contact them.
confirm: the reader clicked the confirmation link. Their status is active and they will receive the next issue. This is the event to use for anything that means "they are on the list".
unsubscribe: the reader used the unsubscribe link or the one-click header in their mail client. Their status is unsubscribed. If you mirror the list elsewhere, this is the event that removes them.
issue_sent: an issue has finished going out. The payload includes the issue date, the subject line, how many readers it reached and a link to the public archive copy (which works only if the archive is switched on for that newsletter).
The request Brieva sends
Every delivery is an HTTP POST with Content-Type application/json and a body shaped like this. The data block changes per event; everything else is constant.
POST https://your-endpoint.example.com/brieva
Content-Type: application/json
User-Agent: Brieva-Webhooks/1.0
X-Brieva-Event: confirm
X-Brieva-Delivery: 5c1f0d3e2b3a4d1e9f8a7b6c5d4e3f2a
X-Brieva-Signature: sha256=3f7b2c...e91a
{
"event": "confirm",
"newsletter_id": "nl_5ba9db11978d4077",
"occurred_at": "2026-09-15T03:13:31Z",
"data": {
"email": "reader@example.com",
"status": "active",
"tags": ["vip"]
}
}Payloads by event
subscribe and confirm carry email, status and tags (the reader's tags at that moment; an empty list if none). unsubscribe carries email and status. issue_sent carries issue_date (YYYYMMDD), subject, recipients, ok, failed and archive_url.
Field names are stable. New fields may be added inside data in future; your code should ignore keys it does not recognise rather than reject the request.
{ "event": "issue_sent", "newsletter_id": "nl_5ba9db11978d4077",
"occurred_at": "2026-09-15T03:43:00Z",
"data": {
"issue_date": "20260915",
"subject": "Material costs spike 10%+ YoY; Auckland builders must stress-test bids now",
"recipients": 2, "ok": 2, "failed": 0,
"archive_url": "https://w47hv0p1e3.execute-api.us-east-1.amazonaws.com/public/archive/nl_5ba9db11978d4077/20260915"
} }Verifying the signature
Anyone who knows your endpoint URL could POST to it. The signature stops that. When you add a webhook Brieva shows a secret once. Every request carries X-Brieva-Signature: sha256=<hex>, where hex is the HMAC-SHA256 of the raw request body using that secret as the key. Compute the same thing on your side over the exact bytes you received (before any JSON parsing or re-serialising) and compare with a constant-time comparison.
If you use Zapier or Make you can skip this; their catch-hook URLs are unguessable and that is usually protection enough for a subscriber feed. If you run your own endpoint, verify.
# Python (Flask-style)
import hmac, hashlib
def verify(secret: str, raw_body: bytes, header: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header or "")
// Node (Express with express.raw())
const crypto = require('crypto')
function verify(secret, rawBody, header) {
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
return header && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header))
}Setting up with Zapier
In Zapier create a Zap whose trigger is Webhooks by Zapier, event Catch Hook. Copy the URL it gives you. In Brieva open the newsletter, then the Webhooks panel, paste the URL, tick the events you want (usually confirm and unsubscribe) and click Add webhook. Press Test on the new row; Zapier will show a test request with a data block you can map from. Then add your action step, for example Create Contact in your CRM, mapping data.email and data.tags.
One Zap per event is simplest. If you send several events to one Zap, add a Filter step on the event field so the CRM action only runs for confirm.
Setting up with Make
In Make add a Webhooks module, Custom webhook, and copy its address. Add it in Brieva the same way as above and press Test so Make can determine the data structure. From there route on the event field with a Router and add the modules you need.
Delivery, timeouts and failures
Brieva sends each event once, at the moment it happens, and waits up to four seconds for a response. Any 2xx status counts as delivered. Anything else, a timeout, a redirect or a 4xx or 5xx, is recorded as a failure on the webhook row, which the Webhooks panel shows as the last delivery status. There is no automatic retry, so treat webhooks as a live feed rather than a ledger: if you need a complete record, export the subscriber list from the Subscribers panel.
Your endpoint should reply as soon as it has stored the request and do any slow work afterwards. A CRM call that takes six seconds inside the request handler will show up in Brieva as a timeout even though your side eventually succeeded.
Limits and rules
Five webhooks per newsletter. URLs must be https and point at a public hostname; private addresses, IP literals and localhost are refused. The signing secret is per webhook. Removing a webhook stops deliveries immediately.
Webhooks are available on Pro and Scale. On Free and Solo the panel shows what they do and where to upgrade.
Ready to set it up?