Webhooks

The recipient marked the email as spam

Posted to the subscriptions to email.complained when the recipient marks the email as spam. The address joins the suppression list. Signed and retried like every event. The test event (POST /api/v1/webhooks/{id}/test) arrives under this type too, with data: { test: true, message } and none of the fields of the real event: check data.test first.

postWebhookemail.complained

Headers

Mailcheer-Signaturestring required

t=<unix timestamp>,v1=<hex>: v1 is the HMAC-SHA256 of "<t>.<raw body>" keyed with the subscription's secret. Reject a t more than 300 seconds away from your clock.

Mailcheer-Timestampstring required

The same t, in seconds.

Mailcheer-Event-Idstring required

The event's id — the same on every attempt and every replay: the deduplication key.

Mailcheer-Event-Typestring required

The event's type.

Mailcheer-Delivery-Idstring required

This delivery. A manual replay gets a new one.

Mailcheer-Attemptstring required

The attempt number, starting at 1.

Payload

OR

Example payload

{
  "id": "evt_Ab3cD9eF0gH1iJ2k",
  "type": "email.bounced",
  "created_at": "2026-09-25T08:31:09.020Z",
  "data": {
    "message_id": "cmu651xf200021n7nm68sikfw",
    "email": "nobody@example.com",
    "source": "api",
    "subject": "Your September invoice",
    "reason": "Permanent"
  }
}

Response

Any 2xx within 15 seconds acknowledges receipt. Anything else — another status, a redirect (not followed), no answer, a network error — is retried: 5 attempts in all, the next ones 30 s, 2 min, 10 min and 1 h after the previous failure.

Changes

Webhooks have no change history: a webhook is keyed by its event name rather than a path and method, so no recorded change can name it.