Event types

Every webhook event Notix sends, with the payload of each.

Every event Notix sends, grouped by what it is about. Each one arrives inside the same envelope, described in Webhooks. The examples below show the data field only.

Fields marked optional in a description can be missing from a payload. Times are ISO 8601 strings in UTC.

Email events

Most email events carry the same base fields:

FieldTypeWhat it is
idstringThe email's id.
statusstringThe email's status after this event, such as DELIVERED.
fromstringThe sender.
tostring[]The recipients.
occurredAtstringWhen the event happened.
subjectstringOptional. The subject line.
campaignIdstring or nullOptional. Set when the email came from a broadcast.
contactIdstring or nullOptional. Set when the email went to a contact.
domainIdnumber or nullOptional. The sending domain's id.
templateIdstringOptional. Set when the email used a template.
metadataobjectOptional. The metadata you attached when you sent it.

email.queued

The email was accepted and queued for sending.

{
  "id": "em_4471receipt",
  "status": "QUEUED",
  "from": "Acme <receipts@acme.com>",
  "to": ["chiamaka@example.com"],
  "subject": "Your receipt for order 4471",
  "occurredAt": "2026-10-01T09:29:52.000Z",
  "domainId": 12,
  "metadata": { "orderId": "4471" }
}

email.sent

Notix handed the email to the recipient's mail server.

{
  "id": "em_4471receipt",
  "status": "SENT",
  "from": "Acme <receipts@acme.com>",
  "to": ["chiamaka@example.com"],
  "subject": "Your receipt for order 4471",
  "occurredAt": "2026-10-01T09:29:55.000Z",
  "domainId": 12
}

email.delivery_delayed

The recipient's server did not accept the email yet. Delivery is being retried.

{
  "id": "em_4471receipt",
  "status": "DELIVERY_DELAYED",
  "from": "Acme <receipts@acme.com>",
  "to": ["chiamaka@example.com"],
  "subject": "Your receipt for order 4471",
  "occurredAt": "2026-10-01T09:35:10.000Z",
  "domainId": 12
}

email.delivered

The recipient's server accepted the email.

{
  "id": "em_4471receipt",
  "status": "DELIVERED",
  "from": "Acme <receipts@acme.com>",
  "to": ["chiamaka@example.com"],
  "subject": "Your receipt for order 4471",
  "occurredAt": "2026-10-01T09:29:58.000Z",
  "domainId": 12
}

email.bounced

The email bounced. bounce.type is Permanent for an address that does not work, or Transient for a problem that may pass. subType is one of General, NoEmail, Suppressed, OnAccountSuppressionList, MailboxFull, MessageTooLarge, ContentRejected or AttachmentRejected.

{
  "id": "em_4471receipt",
  "status": "BOUNCED",
  "from": "Acme <receipts@acme.com>",
  "to": ["tunde@example.com"],
  "subject": "Your receipt for order 4471",
  "occurredAt": "2026-10-01T09:30:02.000Z",
  "domainId": 12,
  "bounce": {
    "type": "Permanent",
    "subType": "NoEmail",
    "message": "550 5.1.1 The email account that you tried to reach does not exist."
  }
}

email.rejected

The email was rejected before it was sent.

{
  "id": "em_4471receipt",
  "status": "REJECTED",
  "from": "Acme <receipts@acme.com>",
  "to": ["chiamaka@example.com"],
  "subject": "Your receipt for order 4471",
  "occurredAt": "2026-10-01T09:29:53.000Z",
  "domainId": 12
}

email.rendering_failure

The email's template could not be rendered, so it was not sent.

{
  "id": "em_4471receipt",
  "status": "RENDERING_FAILURE",
  "from": "Acme <receipts@acme.com>",
  "to": ["chiamaka@example.com"],
  "occurredAt": "2026-10-01T09:29:53.000Z",
  "templateId": "tmpl_receipt"
}

email.complained

The recipient marked the email as spam.

{
  "id": "em_7781news",
  "status": "COMPLAINED",
  "from": "Acme <news@acme.com>",
  "to": ["ngozi@example.com"],
  "subject": "What is new at Acme in October",
  "occurredAt": "2026-10-02T14:12:40.000Z",
  "campaignId": "cmp_oct_news",
  "contactId": "cnt_77ab",
  "domainId": 12
}

email.failed

Notix could not send the email. failed.reason says why.

{
  "id": "em_4471receipt",
  "status": "FAILED",
  "from": "Acme <receipts@acme.com>",
  "to": ["chiamaka@example.com"],
  "subject": "Your receipt for order 4471",
  "occurredAt": "2026-10-01T09:29:54.000Z",
  "domainId": 12,
  "failed": {
    "reason": "The sending domain is paused."
  }
}

email.cancelled

A scheduled email was cancelled before it was sent.

{
  "id": "em_9120reminder",
  "status": "CANCELLED",
  "from": "Acme <reminders@acme.com>",
  "to": ["chiamaka@example.com"],
  "subject": "Your appointment is tomorrow",
  "occurredAt": "2026-10-03T08:00:00.000Z",
  "domainId": 12
}

email.suppressed

The email was not sent because the address is on your suppression list. suppression.type is Bounce, Complaint or Manual.

{
  "id": "em_4480receipt",
  "status": "SUPPRESSED",
  "from": "Acme <receipts@acme.com>",
  "to": ["tunde@example.com"],
  "subject": "Your receipt for order 4480",
  "occurredAt": "2026-10-04T11:02:00.000Z",
  "domainId": 12,
  "suppression": {
    "type": "Bounce",
    "reason": "The address bounced permanently on 2026-10-01.",
    "source": "em_4471receipt"
  }
}

email.opened

The recipient opened the email. Sent only when open tracking is on for the domain.

{
  "id": "em_7781news",
  "status": "OPENED",
  "from": "Acme <news@acme.com>",
  "to": ["ngozi@example.com"],
  "subject": "What is new at Acme in October",
  "occurredAt": "2026-10-02T10:15:21.000Z",
  "campaignId": "cmp_oct_news",
  "domainId": 12,
  "open": {
    "timestamp": "2026-10-02T10:15:21.000Z",
    "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
    "ip": "198.51.100.10",
    "platform": "iOS"
  }
}

email.clicked

The recipient clicked a link in the email. Sent only when click tracking is on for the domain.

{
  "id": "em_7781news",
  "status": "CLICKED",
  "from": "Acme <news@acme.com>",
  "to": ["ngozi@example.com"],
  "subject": "What is new at Acme in October",
  "occurredAt": "2026-10-02T10:16:03.000Z",
  "campaignId": "cmp_oct_news",
  "domainId": 12,
  "click": {
    "timestamp": "2026-10-02T10:16:03.000Z",
    "url": "https://acme.com/october",
    "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
    "ip": "198.51.100.10",
    "platform": "iOS"
  }
}

Received email events

email.received

One of your inboxes received a message that was not caught as spam. strippedText is the new part of a reply without the quoted history. Attachment links work for 15 minutes. inReplyToEmailId is set when the message answers an email Notix sent. senderVerified is true only when the sender's domain passed DMARC. When it is false, treat the From address as unconfirmed. See Receiving emails.

{
  "messageId": "inm_8f2k1",
  "conversationId": "cnv_31ad9",
  "inbox": { "id": "ibx_supp", "name": "Support" },
  "from": "Chiamaka Obi <chiamaka@example.com>",
  "to": ["acme-support@inbound.usenotix.dev"],
  "cc": [],
  "subject": "Refund for double charge on order 4471",
  "text": "Hello, I was charged twice for order 4471.",
  "html": "<p>Hello, I was charged twice for order 4471.</p>",
  "strippedText": "Hello, I was charged twice for order 4471.",
  "attachments": [
    {
      "filename": "bank-statement.pdf",
      "contentType": "application/pdf",
      "size": 184220,
      "url": "https://..."
    }
  ],
  "contactId": "cnt_77ab",
  "inReplyToEmailId": "em_4471receipt",
  "spamVerdict": "PASS",
  "dmarcVerdict": "PASS",
  "senderVerified": true,
  "receivedAt": "2026-10-01T09:12:04.000Z"
}

Contact events

contact.created, contact.updated and contact.deleted carry the whole contact.

contact.created

A contact was added to a contact book.

{
  "id": "cnt_77ab",
  "email": "ngozi@example.com",
  "contactBookId": "cb_customers",
  "subscribed": true,
  "properties": { "plan": "pro", "city": "Lagos" },
  "firstName": "Ngozi",
  "lastName": "Eze",
  "createdAt": "2026-10-01T08:00:00.000Z",
  "updatedAt": "2026-10-01T08:00:00.000Z"
}

contact.updated

A contact's details changed.

{
  "id": "cnt_77ab",
  "email": "ngozi@example.com",
  "contactBookId": "cb_customers",
  "subscribed": true,
  "properties": { "plan": "business", "city": "Lagos" },
  "firstName": "Ngozi",
  "lastName": "Eze",
  "createdAt": "2026-10-01T08:00:00.000Z",
  "updatedAt": "2026-10-05T12:30:00.000Z"
}

contact.deleted

A contact was deleted.

{
  "id": "cnt_77ab",
  "email": "ngozi@example.com",
  "contactBookId": "cb_customers",
  "subscribed": true,
  "properties": { "plan": "business", "city": "Lagos" },
  "firstName": "Ngozi",
  "lastName": "Eze",
  "createdAt": "2026-10-01T08:00:00.000Z",
  "updatedAt": "2026-10-05T12:30:00.000Z"
}

contact.subscribed

A contact became subscribed. source is created when the contact was added already subscribed, or double_opt_in when they confirmed by email.

{
  "contactId": "cnt_77ab",
  "contactBookId": "cb_customers",
  "email": "ngozi@example.com",
  "source": "double_opt_in"
}

contact.unsubscribed

A contact became unsubscribed, by any route. reason is UNSUBSCRIBED, BOUNCED or COMPLAINED.

{
  "contactId": "cnt_77ab",
  "contactBookId": "cb_customers",
  "email": "ngozi@example.com",
  "reason": "UNSUBSCRIBED"
}

Domain events

All four domain events carry the domain. status is NOT_STARTED, PENDING, SUCCESS, FAILED or TEMPORARY_FAILURE. sendingState is ACTIVE, WARNED or PAUSED; see Domain health and pauses. Payloads built before that feature shipped may not have it.

domain.created

A sending domain was added.

{
  "id": 12,
  "name": "mail.acme.com",
  "status": "PENDING",
  "region": "eu-west-1",
  "createdAt": "2026-09-20T10:00:00.000Z",
  "updatedAt": "2026-09-20T10:00:00.000Z",
  "clickTracking": false,
  "openTracking": false,
  "subdomain": null,
  "dkimStatus": "PENDING",
  "spfDetails": "PENDING",
  "dmarcAdded": false,
  "sendingState": "ACTIVE"
}

domain.verified

Every record of the domain was found, and it can send.

{
  "id": 12,
  "name": "mail.acme.com",
  "status": "SUCCESS",
  "region": "eu-west-1",
  "createdAt": "2026-09-20T10:00:00.000Z",
  "updatedAt": "2026-09-20T10:08:00.000Z",
  "clickTracking": false,
  "openTracking": false,
  "subdomain": null,
  "dkimStatus": "SUCCESS",
  "spfDetails": "SUCCESS",
  "dmarcAdded": true,
  "sendingState": "ACTIVE"
}

domain.updated

A domain's settings or state changed, such as its tracking switches.

{
  "id": 12,
  "name": "mail.acme.com",
  "status": "SUCCESS",
  "region": "eu-west-1",
  "createdAt": "2026-09-20T10:00:00.000Z",
  "updatedAt": "2026-10-02T15:00:00.000Z",
  "clickTracking": true,
  "openTracking": true,
  "subdomain": null,
  "dkimStatus": "SUCCESS",
  "spfDetails": "SUCCESS",
  "dmarcAdded": true,
  "sendingState": "ACTIVE"
}

domain.deleted

A domain was deleted.

{
  "id": 12,
  "name": "mail.acme.com",
  "status": "SUCCESS",
  "region": "eu-west-1",
  "createdAt": "2026-09-20T10:00:00.000Z",
  "updatedAt": "2026-10-05T09:00:00.000Z",
  "clickTracking": true,
  "openTracking": true,
  "subdomain": null,
  "dkimStatus": "SUCCESS",
  "spfDetails": "SUCCESS",
  "dmarcAdded": true,
  "sendingState": "ACTIVE"
}

One-time code events

Every verification payload can carry channel, which is email or sms. Older email payloads may not have it. See One-time codes.

verification.sent

A code was queued for delivery. For email, to holds the recipient's domain only. risk.reasons are signal codes such as role_address or disposable_domain.

{
  "id": "ver_abc123",
  "to": "example.com",
  "expiresAt": "2026-10-01T10:40:00.000Z",
  "risk": {
    "score": 20,
    "level": "LOW",
    "reasons": ["role_address"]
  },
  "channel": "email"
}

verification.verified

A submitted code matched. attempts counts every attempt, including the one that matched.

{
  "id": "ver_abc123",
  "verifiedAt": "2026-10-01T10:33:00.000Z",
  "attempts": 2,
  "channel": "email"
}

verification.failed

The code can no longer be used. reason is max_attempts, expired or delivery_failed.

{
  "id": "ver_abc123",
  "reason": "max_attempts",
  "attempts": 5,
  "channel": "email"
}

verification.refused

Risk scoring refused the request. No code was made and nothing was sent.

{
  "id": "ver_def456",
  "risk": {
    "score": 90,
    "level": "HIGH",
    "reasons": ["disposable_domain"]
  },
  "refusalCodes": ["disposable_domain"],
  "channel": "email"
}

verification.delivered

The SMS provider confirmed an SMS code was delivered. Sent for channel: "sms" only.

{
  "id": "ver_ghi789",
  "channel": "sms",
  "deliveredAt": "2026-10-01T10:33:05.000Z"
}

SMS events

to is masked to the last four digits on every SMS event. See Send SMS.

sms.sent

The provider accepted the message. status is queued or sent.

{
  "id": "sms_abc123",
  "to": "+234*******5678",
  "status": "sent",
  "segments": 1,
  "occurredAt": "2026-10-01T10:33:00.000Z"
}

sms.failed

The message failed or was rejected. status is failed or rejected. reason is a plain reason, never a provider code.

{
  "id": "sms_abc123",
  "to": "+234*******5678",
  "status": "failed",
  "segments": 1,
  "reason": {
    "code": "not_reachable",
    "message": "Recipient number not reachable."
  },
  "occurredAt": "2026-10-01T10:33:05.000Z"
}

sms.delivered

The provider confirmed delivery.

{
  "id": "sms_abc123",
  "to": "+234*******5678",
  "status": "delivered",
  "segments": 1,
  "occurredAt": "2026-10-01T10:33:05.000Z"
}

Automation events

See Automations.

journey.enrolled

A contact entered an automation, and a run started.

{
  "journeyId": "jrn_welcome",
  "runId": "run_5k2m",
  "contactId": "cnt_77ab"
}

journey.completed

A run finished the automation's last step.

{
  "journeyId": "jrn_welcome",
  "runId": "run_5k2m",
  "contactId": "cnt_77ab",
  "completedAt": "2026-10-08T09:00:00.000Z"
}

journey.exited

A run left the automation before the end. exitReason is UNSUBSCRIBED, JOURNEY_ARCHIVED, CONTACT_DELETED, SEND_FAILED or PROCESSING_ERROR.

{
  "journeyId": "jrn_welcome",
  "runId": "run_5k2m",
  "contactId": "cnt_77ab",
  "exitReason": "UNSUBSCRIBED"
}

Broadcast events

See Broadcasts.

campaign.started

The first batch of a broadcast was queued. total is how many people it goes to.

{
  "campaignId": "cmp_oct_news",
  "total": 4820
}

campaign.paused_for_limit

The broadcast stopped part way through because your team used its monthly units and pay-as-you-go is off. Nothing is cancelled: it keeps its place in the list and carries on at resumesAt, when the units reset, or sooner if you upgrade or switch on pay-as-you-go. limit is the monthly units that ran out. Sent once per billing period per broadcast.

{
  "campaignId": "cmp_oct_news",
  "limit": 50000,
  "resumesAt": "2026-11-01T00:00:00.000Z"
}

campaign.completed

The last email of the broadcast was processed, and the broadcast moved to sent. sent counts the emails handed off for delivery and failed the ones that could not be. Neither number says what mailbox providers did next. Use email.delivered and email.bounced for that.

{
  "campaignId": "cmp_oct_news",
  "sent": 4796,
  "failed": 24
}

Account events

team.closed

The owner closed the account. It fires at once, not when the data is erased, so you have the whole recovery window to export data or tell your own users. purgeAfter is the earliest time the data can be erased. There is no event at erasure, because the account's webhooks are erased with it.

{
  "id": 123,
  "name": "Acme",
  "closedAt": "2026-10-01T10:00:00.000Z",
  "purgeAfter": "2026-10-31T10:00:00.000Z"
}

team.restored

A closed account was brought back before its data was erased. Undo anything you did on team.closed.

{
  "id": 123,
  "name": "Acme",
  "restoredAt": "2026-10-04T09:14:22.000Z"
}

Deliverability events

deliverability.blocked

The pre-send check refused a broadcast send, schedule or resume, an automation activation, or an edit to a live automation step. kind is campaign, journeyStep or journeyStepDraft, and id names the broadcast or step. findingIds lists the findings that blocked it, and is empty when a blocking check did not finish in time. See Check before you send.

{
  "kind": "campaign",
  "id": "cmp_oct_news",
  "findingIds": ["unsubscribe_missing", "domain_unverified"]
}

Test events

webhook.test

Sent when you press Send a test event or call Send a test event. It is signed like a real event.

{
  "test": true,
  "webhookId": "wh_9d2c",
  "sentAt": "2026-10-01T10:30:00.000Z"
}
Open in ClaudeOpen in ChatGPT