Webhooks
Webhooks notify your backend of email changes. Administration requires an owner or administrator’s human session and is unavailable to integration keys. The Developers screen does not yet include a configuration form.
Configure the destination
The session surface uses POST /workspaces/{workspaceId}/webhooks, list/detail GET and PATCH /workspaces/{workspaceId}/webhooks/{id}. Writes require idempotency; PATCH also requires the ETag in If-Match. In the browser, the /api/workspaces/... facade applies session, origin and CSRF checks.
Example creation body:
{
"url": "https://hooks.example.com/sendiro",
"events": ["email.delivered", "email.bounced", "email.complained"],
"enabled": true
}
Replace the destination with your own service. It must use HTTPS on port 443 and resolve to public IPv4; IP literals, private destinations and redirects are unsupported. There is a maximum of 20 endpoints. The secret appears once at creation and is not returned by an idempotent replay.
Catalog and envelope
Configurable events: email.queued, email.submitted, email.submission_unknown, email.delivered, email.bounced, email.complained, email.opened, email.clicked, email.unsubscribed, email.failed and email.suppressed.
The JSON contains schemaVersion: 1, id, createdAt and event, with id, workspaceId, emailId, kind, occurredAt and recordedAt. It does not include email content. occurredAt describes event time; recordedAt describes recording time.
Verify before processing
Read X-Sendiro-Event-Id, X-Sendiro-Delivery-Id and X-Sendiro-Signature. The latter has the format t=TIMESTAMP,v1=HEX.
Calculate HMAC-SHA256 with the secret over timestamp + "." + rawBody, using the original bytes, and compare in constant time. Apply a timestamp age window in your receiver and reject invalid signatures before using data. Parsing and reserializing JSON changes the signed bytes.
Rotate a signing secret
An administrator can call POST /workspaces/{workspaceId}/webhooks/{id}/rotate-secret
with a human session, Idempotency-Key, and the current configuration ETag in
If-Match. For a planned renewal, send:
{"mode":"overlap","overlapSeconds":3600,"reason":"Scheduled receiver key renewal"}
The response reveals the replacement secret once and increments the webhook
revision. Lists, details and idempotent replays never reveal keys. Save the first
response securely; a lost response requires another explicit rotation. The
default overlap is one hour, with a minimum of five minutes and a maximum of
24 hours. During that window, the signature header is
t=TIMESTAMP,v1=NEW_HEX,v1=PREVIOUS_HEX: verify every v1 candidate and accept
when any matches your configured key using constant-time comparison. Install the
replacement key before secretRotation.overlapExpiresAt, then remove the old key
from your receiver. A second overlap rotation is blocked while one is active.
For a compromised key, explicitly send
{"mode":"immediate","reason":"Receiver reported a compromised key"}. This
revokes all older signing keys immediately, including any active overlap, and
can interrupt delivery until your receiver installs the replacement. Anyone who
holds an older key can still forge requests to a receiver that accepts it, so
remove compromised keys from the receiver too. Pending deliveries use current
keys when invoked and preserve event and delivery IDs. A request already sent
cannot be recalled. Deploying Sendiro does not automatically rotate secrets.
Acknowledge and deduplicate
Persist the event ID and business effect together in a transaction or apply equivalent idempotency. The same event can arrive multiple times and out of order; retain its identity across repeated POSTs. Return 2xx once durably accepted.
Other responses and errors retry with increasing delays and a finite budget; exhausted work requires review. Permanent receiver failure has no eventual arrival guarantee. Inspect deliveries through GET /workspaces/{workspaceId}/webhook-deliveries with an administrative session. Disabling cancels pending deliveries when processed and does not reverse received events.