AllyOneMail / Webhooks

Webhooks

Delivery events at your system's URL, signed with HMAC-SHA256.

Register

POST/api/v1/webhooks
{
  "url": "https://your-app.com/webhooks/allyonemail",
  "events": ["delivered", "bounced", "complained"],
  "description": "Production"
}

Requires the admin scope (or the dashboard). The URL must be public — internal addresses are refused.

Store the secret now

The full secret appears only in this response; later reads show it masked. To see a new value, use POST /api/v1/webhooks/:id/rotate-secret.

Events

EventWhen
deliveredThe recipient's server accepted the message.
deferredTemporary failure; AllyOneMail will try again. Not a bounce.
bouncedThe destination refused the message.
complainedThe recipient marked it as spam (provider feedback loop).
openedOpen recorded by the pixel.
clickedClick on a tracked link (url in the payload).
unsubscribedUnsubscribe through the link or the one-click header.
blockedSend stopped before leaving (suppressed or blocked recipient).
domain_verifiedThe domain passed DNS verification.
ip_pausedA sending IP was paused due to reputation.
reputation_droppedSharp reputation drop on an IP (carries bounce and complaint rates).
billing_threshold_reachedMonthly usage crossed a plan alert threshold.
incident_escalatedA deliverability incident was escalated (operational alert).
Repeated deliveries

The same event may arrive more than once — through a retry, or because delivered is emitted when the sending server accepts the message and again on the final delivery confirmation. Deduplicate by X-AllyOneMAIL-Id, or by message_id + event.

Payload

{
  "event": "bounced",
  "message_id": "msg_a1b2c3",
  "to": "cliente@destino.com",
  "provider": "gmail",
  "response": "550 5.1.1 The email account that you tried to reach does not exist",
  "timestamp": "2026-10-01T13:00:00.000Z",
  "client_id": "c1f0..."
}

Every payload carries client_id. Send events carry message_id, to, provider and, depending on the source, from, response (the recipient server's reply) and latency_ms. opened and unsubscribed carry message_id, to and timestamp; clicked also carries url.

Verify the signature

X-AllyOneMAIL-Event: bounced
X-AllyOneMAIL-Sig: sha256=<hex>

Headers X-AllyOneMAIL-Event (the event) and X-AllyOneMAIL-Sig: sha256= + HMAC-SHA256 of the raw body with the secret. Compute it over the bytes received, before parsing. The X-AllyOneMAIL-Id header identifies the delivery and repeats across retries.

const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
// compare in constant time against the header X-AllyOneMAIL-Sig

Delivery and failures

  • Success is any 2xx within 10 seconds; redirects are not followed.
  • On failure, up to 3 attempts: right away, after ~1 minute and after ~2 minutes.
  • Every attempt is in GET /api/v1/webhooks/:id/deliveries (kept for 90 days). Resend a delivery with POST /api/v1/webhooks/:id/deliveries/:deliveryId/retry.
  • After 10 consecutive deliveries failing all 3 attempts, the webhook is paused. Fix the endpoint and reactivate it with PATCH /api/v1/webhooks/:id.
  • Respond 2xx quickly and process in a queue on your side.

Test

POST/api/v1/webhooks/:id/test

Fires a synthetic delivered to your URL, signed with the same secret.

Webhooks — AllyOneMail