More ways to use this page
Webhook events
Sendora posts each event as JSON to the webhooks that take it. A delivery may arrive more than once, so key your handling on id, and verify Sendora-Signature before you trust the body. Webhooks shows how.
Headers
Sendora-Signatureheaderstringt=the Unix time of sending, thenv1=an HMAC-SHA256 over<t>.<body>for each live secret of the webhook, the newest first. Verify one before you trust the body.Sendora-EventheaderstringThe event, the same as
eventin the body.Sendora-Delivery-IdheaderstringThe delivery, the same as
idin the body and on every retry of it.User-AgentheaderstringSendora-Webhooks/1.
Acknowledges the delivery. Any other answer, or none within 10 seconds, is retried.
Message events
Every event in this group carries these fields:
idstringrequiredThe delivery's id. A delivery may arrive more than once, so key your handling on it.
attemptintegerrequiredThe attempt number, from 1.
messageIdstringrequiredThe message the event is about.
recipientstringrequiredThe recipient the event is about.
occurredAtstring (date-time)requiredWhen it happened.
serverIdstringrequiredThe server the message was sent from.
streamIdstringrequiredThe stream the message went on.
broadcastIdstring or nullrequiredThe broadcast the message belongs to; null for a message sent on its own.
tagstring or nullrequiredThe tag you sent with the message.
metadatamap of string to stringrequiredThe metadata you sent with the message.
testbooleanrequiredTrue for a test server's message: the event was simulated and nothing was sent.
delivered
The receiver accepted the message for the recipient.
event"delivered"requireddetailsobjectrequiredcodeinteger or nullrequiredThe SMTP status code the receiver answered; null when it gave none.
responsestring or nullrequiredThe receiver's response text.
Example (20 lines)
{
"id": "9b2f4c1e-7a3d-4e5f-8c6b-1d2e3f4a5b6c",
"attempt": 1,
"messageId": "5f432ffd-c005-499b-aa3a-5b8a088ea20e",
"recipient": "anna@example.com",
"occurredAt": "2026-09-15T12:00:03.000Z",
"serverId": "0f8a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
"streamId": "3d1a2b4c-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"broadcastId": null,
"tag": "invoice",
"metadata": {
"invoiceId": "2026-0912"
},
"test": false,
"event": "delivered",
"details": {
"code": 250,
"response": "2.0.0 OK"
}
}bounced
The receiver refused the message for good, or Sendora gave up; the recipient is suppressed when hard.
event"bounced"requireddetailsobjectrequiredcodeinteger or nullrequiredThe SMTP status code the receiver answered; null when it gave none.
responsestring or nullrequiredThe receiver's response text.
classificationstring or nullrequiredHow the bounce was classified, such as InvalidRecipient.
hardbooleanrequiredTrue when the address is now on the suppression list.
expiredbooleanrequiredTrue when the message expired after repeated deferrals rather than being refused.
Example (23 lines)
{
"id": "9b2f4c1e-7a3d-4e5f-8c6b-1d2e3f4a5b6c",
"attempt": 1,
"messageId": "5f432ffd-c005-499b-aa3a-5b8a088ea20e",
"recipient": "anna@example.com",
"occurredAt": "2026-09-15T12:00:03.000Z",
"serverId": "0f8a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
"streamId": "3d1a2b4c-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"broadcastId": null,
"tag": "invoice",
"metadata": {
"invoiceId": "2026-0912"
},
"test": false,
"event": "bounced",
"details": {
"code": 550,
"response": "5.1.1 The email account that you tried to reach does not exist.",
"classification": "InvalidRecipient",
"hard": true,
"expired": false
}
}deferred
The receiver asked Sendora to try later; several may follow.
event"deferred"requireddetailsobjectrequiredcodeinteger or nullrequiredThe SMTP status code the receiver answered; null when it gave none.
responsestring or nullrequiredThe receiver's response text.
Example (20 lines)
{
"id": "9b2f4c1e-7a3d-4e5f-8c6b-1d2e3f4a5b6c",
"attempt": 1,
"messageId": "5f432ffd-c005-499b-aa3a-5b8a088ea20e",
"recipient": "anna@example.com",
"occurredAt": "2026-09-15T12:00:03.000Z",
"serverId": "0f8a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
"streamId": "3d1a2b4c-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"broadcastId": null,
"tag": "invoice",
"metadata": {
"invoiceId": "2026-0912"
},
"test": false,
"event": "deferred",
"details": {
"code": 451,
"response": "4.7.1 Greylisted, try again later"
}
}spam_complaint
The recipient reported the message as spam; only Sendora support lifts that suppression.
event"spam_complaint" requireddetailsobjectrequiredfeedbackTypestring or nullrequiredThe feedback type the receiver reported, such as abuse.
Example (19 lines)
{
"id": "9b2f4c1e-7a3d-4e5f-8c6b-1d2e3f4a5b6c",
"attempt": 1,
"messageId": "5f432ffd-c005-499b-aa3a-5b8a088ea20e",
"recipient": "anna@example.com",
"occurredAt": "2026-09-15T12:00:03.000Z",
"serverId": "0f8a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
"streamId": "3d1a2b4c-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"broadcastId": null,
"tag": "invoice",
"metadata": {
"invoiceId": "2026-0912"
},
"test": false,
"event": "spam_complaint",
"details": {
"feedbackType": "abuse"
}
}unsubscribed
The recipient unsubscribed through the link or their mail client's one-click button; only Sendora support lifts that suppression.
event"unsubscribed"requireddetailsobjectrequiredsource"link" | "one_click" requiredlinkfor the page's button,one_clickfor a mail client's request.
Example (17 lines)
{
"id": "9b2f4c1e-7a3d-4e5f-8c6b-1d2e3f4a5b6c",
"attempt": 1,
"messageId": "5f432ffd-c005-499b-aa3a-5b8a088ea20e",
"recipient": "anna@example.com",
"occurredAt": "2026-09-15T12:00:03.000Z",
"serverId": "0f8a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c",
"streamId": "3d1a2b4c-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"broadcastId": "2c4e6a8b-0d1f-4a3b-8c5d-7e9f1a3b5c7d",
"tag": "newsletter",
"metadata": {},
"test": false,
"event": "unsubscribed",
"details": {
"source": "one_click"
}
}The monthly cap
Every event in this group carries these fields:
idstringrequiredThe delivery's id. A delivery may arrive more than once, so key your handling on it.
attemptintegerrequiredThe attempt number, from 1.
scope"account" | "server" | "test"requiredWhose cap it is:
accountfor the account's,serverfor a server's own,testfor the cap the account's test servers share.serverIdstring or nullrequiredThe server whose cap it is; null for the account's or the test servers' cap.
capintegerrequiredEmails the scope may send in the month.
usedintegerrequiredEmails sent in the month so far.
periodStartstring (date-time)requiredThe month the counts are for: its first moment, in UTC.
periodEndstring (date-time)requiredThe first moment of the next month, in UTC.
occurredAtstring (date-time)requiredWhen it happened.
cap_warning
80 percent of the monthly cap is used.
event"cap_warning" required
Example
{
"id": "4d6f8a0b-2c4e-4f6a-8b0c-2d4e6f8a0b1c",
"attempt": 1,
"scope": "account",
"serverId": null,
"cap": 50000,
"periodStart": "2026-09-01T00:00:00.000Z",
"periodEnd": "2026-10-01T00:00:00.000Z",
"used": 40000,
"occurredAt": "2026-09-24T09:30:00.000Z",
"event": "cap_warning"
}cap_reached
A send was refused because the monthly cap is used up.
event"cap_reached" required
Example
{
"id": "4d6f8a0b-2c4e-4f6a-8b0c-2d4e6f8a0b1c",
"attempt": 1,
"scope": "account",
"serverId": null,
"cap": 50000,
"periodStart": "2026-09-01T00:00:00.000Z",
"periodEnd": "2026-10-01T00:00:00.000Z",
"used": 50000,
"occurredAt": "2026-09-29T16:05:00.000Z",
"event": "cap_reached"
}Received mail
inbound
A message was received on an inbound stream: the reference, or the reference and the message itself, as the webhook asks.
Both forms carry these fields:
idstringrequiredThe delivery's id. A delivery may arrive more than once, so key your handling on it.
attemptintegerrequiredThe attempt number, from 1.
event"inbound"requiredinboundMessageIdstringrequiredThe received message; GET /v1/inbound/{id} reads it.
serverIdstringrequiredThe server whose stream received it.
streamIdstringrequiredThe inbound stream that received it.
receivedAtstring (date-time)requiredWhen the message was accepted from the sending server.
envelopeRecipientstringrequiredThe address of yours the message was sent to.
mailboxHashstring or nullrequiredThe text after
+in the local part of that address, when the sender used one.sizeBytesintegerrequiredThe size of the message as received, in bytes.
attachmentCountintegerrequiredhasTextbooleanrequiredhasHtmlbooleanrequiredauthenticationobjectrequiredWhat Sendora found when it checked the message's authentication;
uncheckeduntil the checks have run.spf"pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror" | "unchecked"requiredSPF for the address in MAIL FROM.
spfHelo"pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror" | "unchecked"requiredSPF for the name the sending server gave in HELO.
dkim"pass" | "fail" | "none" | "temperror" | "permerror" | "unchecked"requireddmarc"pass" | "fail" | "none" | "temperror" | "permerror" | "unchecked"requireddmarcPolicy"none" | "quarantine" | "reject" or nullrequiredWhat the sender's domain asks for; told only when DMARC failed.
arc"none" | "pass" | "fail" | "unchecked"requiredcheckedAtstring (date-time) or nullrequiredWhen the checks ran; null until they have.
parseIssuestring or nullrequiredWhat the parser met; null when the message parsed clean. A hard issue empties the parsed parts, and the raw message stays:
no_headers,header_block_too_large,too_many_headers,too_many_parts,nesting_too_deep,missing_boundary,decoded_too_large,parse_timeout,parser_error. A soft issue keeps them and says what was changed or dropped:truncated_multipart,too_many_attachments,unknown_transfer_encoding,undecodable_container,filename_sanitised,control_chars_stripped,multiple_from,duplicate_header,malformed_header_dropped,address_list_truncated,header_value_truncated. A message with several names the hard one, or the first soft one in this order.
The reference to the received message, for a webhook with
inboundContentreference.Example (25 lines)
{ "id": "6e8a0c2e-4a6c-4e8a-8c0e-2a4c6e8a0c2e", "attempt": 1, "event": "inbound", "inboundMessageId": "1b3d5f7a-9c1e-4b3d-8f7a-9c1e3b5d7f9a", "serverId": "0f8a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c", "streamId": "8a0c2e4a-6c8e-4a0c-8e4a-6c8e0a2c4e6a", "receivedAt": "2026-09-15T12:00:00.000Z", "envelopeRecipient": "support@example.se", "mailboxHash": null, "sizeBytes": 18432, "attachmentCount": 1, "hasText": true, "hasHtml": true, "authentication": { "spf": "pass", "spfHelo": "pass", "dkim": "pass", "dmarc": "pass", "dmarcPolicy": null, "arc": "none", "checkedAt": "2026-09-15T12:00:00.000Z" }, "parseIssue": null }The reference and the message itself, for a webhook with
inboundContentfull.envelopeobject or nullrequiredThe envelope sender; null for a bounce.
senderstring or nullrequired
fromobject or nullrequiredaddressstringrequirednamestring or nullrequired
replyToarray of objectrequiredaddressstringrequirednamestring or nullrequired
toarray of objectrequiredUp to 100 entries;
toCountis the whole number.addressstringrequirednamestring or nullrequired
toCountintegerrequiredccarray of objectrequiredUp to 100 entries;
ccCountis the whole number.addressstringrequirednamestring or nullrequired
ccCountintegerrequiredsubjectstring or nullrequireddatestring (date-time) or nullrequiredThe sender's Date header, when it was a real moment.
messageIdHeaderstring or nullrequiredinReplyTostring or nullrequiredreferencesarray of stringrequiredheadersarray of objectrequirednamestringrequiredvaluestringrequired
textstring or nullrequiredThe plain-text body; null when there is none, or over 1 MiB.
htmlstring or nullrequiredThe HTML body; null when there is none, or over 2 MiB.
attachmentsarray of objectrequiredEach attachment, without its bytes.
positionintegerrequiredIts place among the message's attachments, from 0.
namestringrequiredcontentTypestringrequiredcontentIdstring or nullrequiredThe Content-ID an HTML body refers to with
cid:, when it has one.sizeintegerrequiredinlinebooleanrequiredTrue for a part shown in the body rather than offered as a file.
contentOmitted"too_large" | "expired" or null requiredtoo_largewhen text or HTML was left out for size;expiredwhen the content window has passed and the reference is all that remains.
Example (74 lines)
{ "id": "6e8a0c2e-4a6c-4e8a-8c0e-2a4c6e8a0c2e", "attempt": 1, "event": "inbound", "inboundMessageId": "1b3d5f7a-9c1e-4b3d-8f7a-9c1e3b5d7f9a", "serverId": "0f8a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c", "streamId": "8a0c2e4a-6c8e-4a0c-8e4a-6c8e0a2c4e6a", "receivedAt": "2026-09-15T12:00:00.000Z", "envelopeRecipient": "support@example.se", "mailboxHash": null, "sizeBytes": 18432, "attachmentCount": 1, "hasText": true, "hasHtml": true, "authentication": { "spf": "pass", "spfHelo": "pass", "dkim": "pass", "dmarc": "pass", "dmarcPolicy": null, "arc": "none", "checkedAt": "2026-09-15T12:00:00.000Z" }, "parseIssue": null, "envelope": { "sender": "anna@example.com" }, "from": { "address": "anna@example.com", "name": "Anna Andersson" }, "replyTo": [], "to": [ { "address": "support@example.se", "name": null } ], "toCount": 1, "cc": [], "ccCount": 0, "subject": "A question about the invoice", "date": "2026-09-15T11:59:58.000Z", "messageIdHeader": "<20260915115958.a1b2@example.com>", "inReplyTo": null, "references": [], "headers": [ { "name": "From", "value": "Anna Andersson <anna@example.com>" }, { "name": "To", "value": "support@example.se" }, { "name": "Subject", "value": "A question about the invoice" } ], "text": "Hi! I have a question about the invoice for September.", "html": "<p>Hi! I have a question about the invoice for September.</p>", "attachments": [ { "position": 0, "name": "invoice.pdf", "contentType": "application/pdf", "contentId": null, "size": 15360, "inline": false } ], "contentOmitted": null }