Skip to main content

Email suppression event payloads: worked examples

When a recipient unsubscribes, bounces, or files a spam complaint, Orbit writes the address to your tenant’s email suppression list and emits a subscription event to your webhook endpoints. This guide gives you the exact payload for each event, handlers that verify the signature and act on your contact records, and the persisted shape you read back from GET /api/v1/email/suppressions. For the conceptual model (what the suppression list fences, channel scoping, the three entry points), read Message suppression model and Choose your suppression entry point. This page is the payload-and-handler companion.

1. Event taxonomy recap

Four subscription events cover the email opt-out lifecycle. All four are delivered to your webhook endpoints with the same HMAC signing and retry policy as delivery-status events. Two reasons are terminal per bulk-sender convention and should never be re-enabled: hard_bounce and complaint. Orbit also tracks soft_bounce_exhausted (an address that crossed the consecutive-soft-bounce threshold) and manual (rows you added yourself, including one-click unsubscribes you mirror in). You can pass any of these to the reason filter below.
Suppression rows and subscription events are tenant-owned controls: Orbit enforces the send gate on your behalf, but the decision to re-enable an address, and the downstream update to your own contact records, is yours. This guide is not legal advice.

2. Sample payloads

Every payload carries a top-level type (the event name), a monotonically useful created_at, and a data block with the affected recipient plus the delivery-status body that caused the event, where one exists.

email.notification-unsubscribed

A one-click unsubscribe. The delivery-status body records the unsubscribe mechanism.

email.notification-subscribed

A re-subscription. No delivery-status body applies; the event carries the restored preference.

email.notification-unsubscribed-by-bounce

A hard bounce. The delivery-status body carries the provider’s bounce classification.

email.notification-unsubscribed-by-complaint

A spam complaint. Terminal; do not re-enable these addresses.

3. Read the suppression list

GET /api/v1/email/suppressions returns the persisted rows for your tenant, newest first. It accepts limit, offset, search (substring match on the address), and reason.
Use search to check one address before a send, and pair the read with the event handlers below so your own contact store never drifts from the ledger Orbit gates on.

4. Verification handlers

All three handlers share one verifier. Orbit signs every delivery with HMAC-SHA256 in the X-Orbit-Signature header, formatted t=<unix_seconds>,v1=<hex hmac> over the raw request body. Reject anything older than five minutes and anything whose v1 does not match.

Handler 1 — one-click unsubscribe: unsubscribe the contact

Handler 2 — bounce: downgrade, and unsubscribe on a terminal bounce

Soft bounces recover; a single soft bounce should downgrade the contact’s engagement tier, not remove them. A hard_bounce (or soft_bounce_exhausted) unsubscribes the contact to match the terminal row Orbit wrote.

Handler 3 — complaint: unsubscribe and mark do-not-recontact

Complaints are the strongest negative signal a mailbox provider can send. Unsubscribe the contact and flag the record so no other channel re-adds it.

Replay a payload locally

Stamp a captured payload with your test secret and POST it at your handler, exactly as Orbit would:
Expect 200 and the contact record updated; a tampered body or a stale t= must return 401.

5. Sequence: send → complaint → suppression write → contact update

The same sequence applies to bounces (reason: hard_bounce) and one-click unsubscribes; only the triggering delivery-status body differs.

6. Opt-out alias table

Recipients express opt-outs differently per channel. In SMS and WhatsApp they type keywords; in email they click or complain. This table maps the intent you already handle on messaging channels to its email equivalent and the event your handler receives. Unlike keyword opt-outs, email events need no keyword normalization on your side: the event type is the normalized intent. Treat email.notification-unsubscribed-by-complaint as STOPALL semantics in your contact store (unsubscribe everywhere, do not re-add), and email.notification-unsubscribed as STOP semantics (email only).

7. FAQ

Does a suppression row block all of my senders?

Yes, within your tenant. The list gates every email send you make regardless of sender address or domain, because reputation attaches to your sending identity as a whole. It does not affect SMS or WhatsApp unless you also suppress those channels.

Can I re-enable a suppressed address?

It depends on the reason. Rows with reason manual or soft_bounce_exhausted are recoverable: delete the row with DELETE /api/v1/email/suppressions/{id} and the next send is allowed. Rows with reason hard_bounce or complaint are terminal by bulk-sender convention; re-enabling them harms your sender reputation and is strongly discouraged. A later email.notification-subscribed event clears a recoverable row automatically.

I handle unsubscribes in my own system. Do I still need these events?

Yes. Your system records the preference; Orbit’s list enforces the gate at send time. Wiring the handlers above keeps the two in lockstep, and the complaint and bounce events carry information your own system can never observe directly.

Why did an address appear in the list with no event of mine?

Rows you create through the console, the CSV import, or the Consent API write the same ledger without emitting a subscription event, as do bounce and complaint rows Orbit derives from provider feedback. Use GET /api/v1/email/suppressions?search=<address> to inspect the reason and created_at when reconciling.

Do these events retry if my endpoint is down?

Yes, with the same retry schedule as delivery-status events. Handlers must be idempotent: key your contact update on data.recipient (or the event id if you dedupe) so a redelivered event produces the same contact state.