DEV Community

Cover image for How Do Verified Sending Domains, Webhooks, and Logs Work in an Email API?
sohom das
sohom das

Posted on

How Do Verified Sending Domains, Webhooks, and Logs Work in an Email API?

These three pieces are what let you run email through an API instead of your own mail server: domain verification proves you're allowed to send as your domain, webhooks push you real-time events instead of making you poll, and logs give you a queryable record of what happened to every message. I'll explain how each works generally, then show exactly how Notify implements them — since the generic version and one specific provider's actual behavior aren't always identical, and it's worth knowing where they diverge.

Verified Sending Domains

A verified domain tells receiving mail providers your email is legitimately from you, not spoofed. You do this by adding three DNS records:

SPF lists which servers are authorized to send for your domain — the provider's sending infrastructure gets added to this list.

DKIM cryptographically signs each outgoing message. The provider signs with a private key; you publish the matching public key in DNS, and receivers verify the signature against it.

DMARC tells receivers what to do if SPF or DKIM checks fail — reject, quarantine, or just report — and where to send aggregate reports about your domain's authentication.

With Notify specifically: you add a domain through the Domains dashboard, and Notify hands you all three records to add. Verification typically completes within 24–48 hours once DNS propagates, and you can check status via the API (GET /api/email/domains/{domain}) rather than only the dashboard. Domain limits scale by plan — 1 on Free, 3 on Pro, 10 on Scale — and sending from an unverified domain doesn't degrade gracefully; it's rejected outright with a DOMAIN_NOT_VERIFIED error.

Webhooks

The general pattern: instead of polling "did this bounce yet," you register an HTTPS endpoint, and the provider POSTs a JSON payload to it as events happen — delivered, bounced, complained, opened, clicked, rejected. Good implementations on the receiving end verify the payload's authenticity (often via a signature the provider includes), handle duplicate deliveries idempotently in case of retries, and acknowledge quickly with a 2xx response while processing the actual event asynchronously.

With Notify specifically, here's where I want to be precise rather than just describe the generic pattern as if it applies uniformly:

  • The event types are Send, Delivery, Open, Click, Bounce, Complaint, and DeliveryDelay — a single Bounce type, not split into hard/soft the way some providers do it
  • Webhook payloads aren't signed. There's no signature header to verify a request genuinely came from Notify. This matters because it's the opposite of the "often using a signature the provider supplies" pattern that's common elsewhere — treat your webhook URL itself as a secret, require HTTPS, and cross-check anything sensitive against the logs API rather than trusting the payload alone
  • Registering one is scoped to a single verified domain, with optional narrowing to specific subdomains or from addresses
  • There's a dedicated test endpoint (POST /api/webhooks/test) to confirm your receiving code parses the payload correctly without waiting for a real bounce
  • Availability is plan-gated: 0 on Free, 3 endpoints on Pro, 10 on Scale
curl -X POST https://notify.cx/api/webhooks \
  -H "Content-Type: application/json" \
  -H "x-api-key: $NOTIFY_API_KEY" \
  -d '{
    "webhookUrl": "https://yourapp.com/webhooks/email",
    "subscribedEvents": ["Delivery", "Bounce", "Complaint"],
    "domainId": "your-domain-id"
  }'
Enter fullscreen mode Exit fullscreen mode

If you want the full payload shape before building a receiver, the docs cover it in a few minutes.

Handling the Receiving End Properly

Regardless of provider, a webhook handler should acknowledge quickly with a 2xx response and do the actual processing (updating a database, triggering a notification) asynchronously rather than inside the request itself — a slow handler risks the provider treating the delivery as failed even though your app did eventually process it. Duplicate deliveries are also a general possibility with webhooks across this category, so designing your handler to be idempotent (safe to process the same event twice without double-counting) is good practice generally. I don't have Notify's specific retry-on-failure behavior confirmed from the documentation, so I can't say precisely how often or whether it retries a failed delivery to your endpoint — which is exactly the kind of detail worth building your handler defensively around rather than assuming one way or the other.

Logs

The general pattern: every send gets recorded with message metadata, a timestamp, delivery status, and error detail on failures, retained for a period that varies by plan (short on free tiers, longer or permanent on paid) — queryable through a dashboard for one-off debugging, or an API for building your own reporting on top.

With Notify specifically: each message record includes a messageId, sentAt, and an events array — each entry with an event type, timestamp, and destination. Retention is 48 hours on Free (older logs aren't deleted, just not accessible until you upgrade) and permanent on Pro and Scale. Querying is filterable by event type and date range, with pagination:

curl -X GET "https://notify.cx/api/email/logs?eventType=Bounce&from=2026-09-01T00:00:00Z&limit=50" \
  -H "x-api-key: $NOTIFY_API_KEY"
Enter fullscreen mode Exit fullscreen mode

I don't have Notify supporting custom tags or arbitrary headers as searchable log metadata confirmed — if you need to correlate a log entry back to something in your own system, the messageId you get back from the send call is the reliable join key to build that around yourself.

How They Work Together

A typical flow, concretely:

  1. Verify your domain once — everything else depends on this being done first
  2. Send via the API; each call returns a messageId
  3. Register a webhook so bounces and complaints reach your app in real time — flag the account, suppress future sends, whatever your logic calls for
  4. Fall back to the logs API when you need the fuller picture — a support ticket asking about a specific message, or a weekly bounce-rate report you're building yourself

Webhooks and logs aren't redundant with each other: a webhook fires once, at the moment of the event, and if your handler is briefly down, that specific notification doesn't come back around. Logs are a persisted, queryable record you can check any time after the fact, which is why I'd treat webhooks as the fast path and logs as the source of truth you fall back on.

I set this up in roughly that order the last time I built it — domain first, since nothing else works without it, then basic sending against the free plan's 48-hour logs while the feature was still in progress, and only added webhooks once I'd moved to Pro for a real production launch. The free tier covers testing domain verification and logging fully; webhooks specifically are the one piece you can't fully evaluate without upgrading first, since they're not available to try on Free at all.

Generic Pattern vs. Notify's Specifics

Generic email API pattern Notify
Domain verification SPF/DKIM/DMARC required Same — all three required, checkable via API
Bounce classification Often split hard/soft Single Bounce event type
Webhook signature Often included for verification Not included — treat endpoint URL as a secret
Webhook testing Varies by provider Dedicated /api/webhooks/test endpoint
Log retention on free tier Often 24–48 hours 48 hours (data retained, just hidden until upgrade)
Webhooks on free tier Varies Not included — starts on Pro ($10/mo)

Frequently Asked Questions

How do verified sending domains, webhooks, and logs work in an email API?

Domain verification uses SPF, DKIM, and DMARC DNS records to prove you're authorized to send as your domain. Webhooks push real-time HTTP events (delivery, bounce, complaint, and similar) to an endpoint you register. Logs are the persisted, queryable record of what happened to each send. Notify implements all three — with the notable specifics that its webhook payloads aren't signed and free-tier logs are limited to 48 hours.

What is Notify?

Notify is a lightweight transactional email API for developers — one endpoint to send, domain verification, delivery logs, and webhooks, without templates or marketing tools.

Does Notify sign its webhook payloads for verification?

No — there's no signature header. Treat your webhook endpoint URL as a secret, require HTTPS, and cross-reference sensitive events against the logs API if stronger verification matters for your use case.

Does Notify distinguish between hard and soft bounces?

Not as separate event types — Notify's webhook and log event list has a single Bounce type rather than splitting permanent and temporary failures.

Are webhooks included on Notify's free plan?

No — webhooks require the $10/month Pro plan (3 endpoints) or Scale (10 endpoints). The free plan includes domain verification and 48-hour logs, but not webhooks.

Can I test a Notify webhook before a real event occurs?

Yes — POST /api/webhooks/test sends a test payload to your registered endpoint on demand, so you can confirm your handler parses it correctly ahead of relying on a real bounce or delivery event.

Does Notify retry a webhook delivery if my endpoint fails to respond?

I don't have this specific behavior confirmed from the documentation — worth designing your handler to acknowledge quickly and process idempotently regardless, since that's good practice across this category whether or not retries are guaranteed.

Top comments (0)