# Callbacks and webhooks

Namirial Notify can send HTTP push notifications to your system whenever a transaction changes state. This allows your application to react to delivery events in real time without polling the Query endpoints.

Callbacks are configured per transaction at the time of submission, using three fields in the Submit request body.

## Configuration fields

### `PushNotificationUrl`

The URL where Namirial Notify will POST a callback when a matching state change occurs. Your endpoint must be publicly accessible.


```json
"PushNotificationUrl": "https://your-system.example.com/callbacks/notify"
```

### `PushNotificationFilter`

An array of state names that should trigger a callback. Only transitions to the listed states will fire a notification. If omitted, no callbacks are sent.

Available values depend on the service — see the per-service filter tables [below](#available-filter-values-by-service).

### `PushNotificationExtraData`

An optional free-text string included in every callback for this transaction. Use it to pass correlation data your system needs to route the notification — for example, an internal order ID or reference.


```json
"PushNotificationExtraData": "order-12345"
```

This value is returned inside `AdditionalData.ExtraData` in the callback payload.

## Callbacks and batch sending

When you send through the [batch API](/products/namirialnotify/apis/evinotice-api#batch-operations), the callback settings you configure on the batch (`PushNotificationUrl` / `BatchPushNotificationUrl`, `PushNotificationFilter`, `PushNotificationExtraData`) are **propagated to every evidence** generated from the batch. Each generated notice, email, or SMS then fires the **normal per-evidence callbacks** described on this page — there is no separate batch-level callback event.

`PushNotificationUrl` and `BatchPushNotificationUrl` on a batch are aliases for the same value. The **batch's own** lifecycle (`Draft`, `Submitted`, `Scheduled`, `Processing`, `Processed`, `Invalid`, `Failed`) is **not** pushed — track it by polling `GET /v2/Evi{Service}/Batches/{BatchId}`.

## Available filter values by service

### EviSMS

| Value | Triggered when |
|  --- | --- |
| `Ready` | Message has been locally processed and is ready to send. |
| `Dispatched` | The system has completed local processing; the message is ready to be sent to the telecommunications operator. |
| `Sent` | The telecommunications operator accepted the message for routing. |
| `Delivered` | Message was delivered to the recipient's device. |
| `Read` | Recipient opened the message (where channel supports it). |
| `Failed` | Delivery failed (not a final state — retries may follow). |
| `Closed` | Tracking complete; no further events expected. |
| `AffidavitPublished` | A new affidavit has been published for this message. |


`Replied` is accepted as a filter value and the reply state is reported as `RepliedOn` in the Query response, but the EviSMS pipeline does not currently emit a `Replied` callback — do not rely on it for push notifications. (EviMail and EviNotice do emit `Replied`.)

### EviMail

| Value | Triggered when |
|  --- | --- |
| `Ready` | Message has been locally processed and is ready to send. |
| `Dispatched` | Sending has been requested; the message is ready for the sender component. |
| `Sent` | The recipient's mail server accepted the message. |
| `Delivered` | Delivery confirmation was received. |
| `Read` | Recipient opened the message (where supported). |
| `Replied` | Recipient accepted or rejected the message. |
| `Failed` | Delivery failed (not a final state — retries may follow). |
| `Closed` | Tracking complete; no further events expected. |
| `AffidavitPublished` | An affidavit has been published/generated for this message. |


### EviNotice

| Value | Triggered when |
|  --- | --- |
| `Processed` | EviNotice has been locally processed and is ready to send. |
| `Dispatched` | Notification request has been issued and the delivery process has started. |
| `Sent` | The selected delivery channel accepted the notification. |
| `Delivered` | Delivery notification reached the recipient. |
| `Received` | Recipient followed the link and reached the hosted notice. |
| `Read` | Recipient opened and read the hosted notice content. |
| `Replied` | Recipient accepted or rejected the notice. |
| `Failed` | Delivery failed (not a final state — retries may follow). |
| `Closed` | Tracking complete; no further events expected. |
| `AffidavitPublished` | An affidavit has been published/generated for this notice. |


### EviPost

| Value | Triggered when |
|  --- | --- |
| `Processed` | Postal communication has been locally processed. |
| `Dispatched` | Dispatched to the postal communication system. |
| `Sent` | Handed off to the postal operator or messaging service. |
| `Delivered` | Delivered to the final recipient (accepted or rejected). |
| `Closed` | Tracking complete; no further events expected. |
| `Cancelled` | Communication was cancelled. |
| `Issued` | Item issued by the postal operator. |
| `Disposed` | Item disposed of following an undelivered or returned status. |
| `AffidavitPublished` | A new affidavit has been published for this communication. |


### EviSign

| Value | Triggered when |
|  --- | --- |
| `Processed` | The signature workflow was prepared and certified. |
| `Sent` | A signature request notification was sent to one signer. |
| `Delivered` | A signature request notification was delivered to one signer. |
| `Signed` | One signer completed the signing step. |
| `Rejected` | One signer rejected the document. |
| `FullySent` | Signature requests were sent to all parties. |
| `FullyDelivered` | Signature requests were delivered to all parties. |
| `FullySigned` | All required signers completed the signing step. |
| `Closed` | The workflow reached its final state. |
| `AffidavitPublished` | A new affidavit has been published for this signature workflow. |


## Callback payload

When a matching state change occurs, Namirial Notify sends an HTTP **POST** to the configured `PushNotificationUrl` with a JSON body describing the event.

Callbacks are sent with a JSON content type. The callback integration model
does not define an `Authorization` header, HMAC signature header,
shared-secret header, or a stable source-IP allowlist for origin validation.

Respond with `200 OK` after your system has validated the payload and safely
stored or queued the event for processing.

### Common fields

The shared callback envelope uses these fields, regardless of service. Nullable
values may be sent as `null` or omitted, depending on the service and event.

| Field | Type | Description |
|  --- | --- | --- |
| `Identifier` | string | Unique identifier of the callback event. |
| `Kind` | string | Callback or event kind (for example, `Sent`, `Delivered`, or `AffidavitPublished`). |
| `Date` | datetime | Timestamp of the event in ISO 8601 format. |
| `EvidenceId` | string | Identifier of the transaction (same as `eviId` or `Id` from Submit). |
| `EvidenceType` | string | Type of transaction — `eviMail`, `eviNotice`, `eviSMS`, `eviSign`, or `eviPost`. |
| `EvidenceState` | string/null | Lifecycle state associated with the event, when populated. |
| `Site` | string | Name of the issuer's site. |
| `Owner` | string/null | Display name of the issuer account. Current EviMail and EviPost callbacks can send this as `null`; do not treat it as required. |
| `OwnerEmail` | string | Email of the issuer account. |
| `AdditionalData` | object | Service- and event-specific fields (see below). |


### Delivery order and reconciliation

Callbacks for different events are delivered independently. Do not assume they
arrive in lifecycle order: a callback that is being retried can arrive after a
later event for the same transaction.

- Deduplicate callbacks by `Identifier`. Retries of the same event keep the same
identifier.
- Use `Date` as the time of the event, not as a delivery sequence number.
- If two events have the same timestamp, the callback contract does not provide
a deterministic order between them.
- Use the Query or Get endpoint to reconcile the current state, outcome, and
timestamp fields. These API responses are the authoritative snapshot.


### `AdditionalData` — common fields

These field families cover the mail, SMS, hosted-notice, and postal services. **EviSign uses a different service-specific payload shape** with signer, party, and workflow fields; for that contract, see the [EviSign service guide](/products/namirialnotify/services/evisign#callbacks-and-what-you-receive).

These fields appear inside `AdditionalData` for **EviMail** and **EviNotice**:

| Field | Description |
|  --- | --- |
| `From` | Issuer's email address. |
| `To` | Recipient's email address. |
| `Subject` | Subject of the transaction. |
| `LookupKey` | The lookup key set by the issuer at submission. |
| `State` | Current state of the transaction. |
| `CreationDate` | Timestamp when the transaction was created. |
| `ExtraData` | The value of `PushNotificationExtraData` set at submission, if any. |


For **EviSMS**, the `AdditionalData` object uses different field names for sender and recipient:

| Field | Description |
|  --- | --- |
| `Source` | Name of the issuer. |
| `Destination` | Name or number of the recipient. |
| `LookupKey` | The lookup key set by the issuer at submission. |
| `State` | Current state of the transaction. |
| `CreationDate` | Timestamp when the transaction was created. |
| `ExtraData` | The value of `PushNotificationExtraData` set at submission, if any. |


For **EviPost**, the `AdditionalData` object also uses different field names:

| Field | Description |
|  --- | --- |
| `Sender` | Name of the sender (issuer). |
| `Recipient` | Name of the recipient. |
| `LookupKey` | The lookup key set by the issuer at submission. |
| `State` | Current state of the transaction. |
| `CreationDate` | Timestamp when the transaction was created. |
| `ExtraData` | The value of `PushNotificationExtraData` set at submission, if any. |


### `AdditionalData` — event-specific fields

Depending on the event type, additional fields are included.

**EviNotice, EviMail, EviSMS:**

| Event | Extra fields |
|  --- | --- |
| `Sent` | `XmissionDetails` — technical detail of the delivery attempt. |
| `Dispatched`, `Delivered`, `Read`, `Received` | `Progress` — title of the progress step; `Description` — detailed description. For `Received` and `Read` events on EviNotice, also includes `IpAddress`, `BrowserData`, `UserLanguages`, `UserIdentity`, and `UserAgent` (when available). |
| `Failed` | `Progress` — short error summary; `Description` — detailed description of the failure or retry exhaustion. |
| `Replied` (EviMail and EviNotice only) | `Kind` — action taken by the recipient (e.g., accepted, rejected); `Comments` — any comments left by the recipient. |
| `Closed` | `Outcome` — final outcome of the transaction; `OutcomeDescription` — human-readable outcome description. |


**EviPost:**

| Event | Extra fields |
|  --- | --- |
| `Dispatched`, `Sent`, `Issued` | `TransmissionDetails` — technical detail of the notification or sending. |
| `Delivered` | `Details` — delivery details. |
| `Closed` | `Outcome` — final outcome of the postal process. |


## Example payloads

### EviMail — `Sent` event


```json
{
  "Identifier": "1234",
  "EvidenceId": "2aca3ea149f943879726a87000c1f704",
  "EvidenceType": "eviMail",
  "Kind": "Sent",
  "Date": "2018-01-22T12:46:32.4830752+01:00",
  "Site": "my-site",
  "Owner": null,
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "From": "sender@example.com",
    "To": "recipient@example.com",
    "Subject": "Your certified document",
    "LookupKey": "order-12345",
    "State": "Sent",
    "CreationDate": "2018-01-22T12:46:12.3113880+01:00",
    "XmissionDetails": "Successfully sent to recipient's mail server.",
    "ExtraData": "{\"orderId\": \"99cf386b-1590-4ddb-af68-607b3e7c1194\"}"
  }
}
```

**HTTPS-only callback fields for EviMail.** The `XmissionDetails` field on `Sent` events and the `Comments` field on `Replied` events are only included when `PushNotificationUrl` uses HTTPS. Plain HTTP callback URLs receive the rest of the payload without these fields.

### EviNotice — `Received` event


```json
{
  "Identifier": "0188d313-e2a9-4293-b995-c36324c889a7",
  "Kind": "Received",
  "Date": "2023-06-19T09:54:35.7608150Z",
  "EvidenceId": "0188d313642147089ab0c1398514df49",
  "EvidenceType": "eviNotice",
  "Site": "my-site",
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "From": "sender@example.com",
    "To": "recipient@example.com",
    "Subject": "Your certified notice",
    "State": "Received",
    "CreationDate": "2023-06-19T09:54:03.7486430Z",
    "Progress": "Received of the notification confirmed",
    "Description": "The recipient followed the notice link and reached the hosted content.",
    "IpAddress": "188.26.211.105",
    "BrowserData": "Chrome",
    "UserLanguages": "es-ES, es;q=0.9"
  }
}
```

### EviPost — `AffidavitPublished` event (including on-demand affidavits)

Affidavit-related callbacks for EviPost — including those generated by `POST /v1/EviPost/AffidavitRequest` — use the **same envelope** as state-change callbacks, with `Kind` set to `AffidavitPublished`. They are posted to the `PushNotificationUrl` originally configured on the EviPost submission, provided `AffidavitPublished` was included in `PushNotificationFilter`.

The `AdditionalData` for EviPost uses `Sender` and `Recipient` instead of `From` and `To` (which are the EviMail/EviNotice field names).


```json
{
  "Identifier": "1234",
  "Kind": "AffidavitPublished",
  "Date": "2026-01-22T12:46:32.4830752+01:00",
  "EvidenceId": "2aca3ea149f943879726a87000c1f704",
  "EvidenceType": "eviPost",
  "Site": "my-site",
  "Owner": null,
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "Sender": "Sender Name",
    "Recipient": "Recipient Name",
    "LookupKey": "order-12345",
    "State": "Closed",
    "CreationDate": "2026-01-22T12:46:12.3113880+01:00",
    "AffidavitId": "000c1f7042aca3ea149f943879726a87",
    "AffidavitName": "Certification of postal communication (upon request)",
    "RequestId": "79726a87000c1f7042aca3ea149f9438"
  }
}
```

`AffidavitKind` is not currently emitted for EviPost affidavit callbacks. `RequestId` is only present when the affidavit was produced by an on-demand request. A `Regenerated: true` entry is added when the affidavit was regenerated by the platform rather than published for the first time.

### EviMail — `AffidavitPublished` event (including on-demand affidavits)

Affidavit-related callbacks for EviMail — including those generated by `POST /v1/EviMail/AffidavitRequest` — use the same envelope as state-change callbacks, with `Kind` set to `AffidavitPublished`. They are posted to the `PushNotificationUrl` originally configured on the EviMail submission, provided `AffidavitPublished` was included in `PushNotificationFilter`.

`AffidavitPublished` can be emitted for any generated or published affidavit. `AffidavitRequest` does not accept a separate callback URL; on-demand request results are delivered on the EviMail's existing push URL and include `RequestId` when applicable.


```json
{
  "Identifier": "1234",
  "Kind": "AffidavitPublished",
  "Date": "2026-01-22T12:46:32.4830752+01:00",
  "EvidenceId": "2aca3ea149f943879726a87000c1f704",
  "EvidenceType": "eviMail",
  "Site": "my-site",
  "Owner": null,
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "From": "sender@example.com",
    "To": "recipient@example.com",
    "Subject": "Your certified document",
    "LookupKey": "order-12345",
    "State": "Read",
    "CreationDate": "2026-01-22T12:46:12.3113880+01:00",
    "AffidavitId": "000c1f7042aca3ea149f943879726a87",
    "AffidavitKind": "OnDemand",
    "AffidavitName": "Certification of certified e-mail (on request)",
    "RequestId": "79726a87000c1f7042aca3ea149f9438"
  }
}
```

`RequestId` is only present when the affidavit was produced by an on-demand request. A `Regenerated: true` entry is added when the affidavit was regenerated by the platform rather than published for the first time.

## Retry policy

If your endpoint is unreachable or returns an error, Namirial Notify retries the callback. The retry count and per-attempt delay are platform configuration, not a fixed contract. The schedule below reflects the current production configuration; confirm the active values with your Namirial Notify contact before designing alerting or SLAs around them.

| Attempt | Delay after previous |
|  --- | --- |
| 1 | 30 seconds |
| 2 | 30 seconds |
| 3 | 5 minutes |
| 4 | 15 minutes |
| 5 | 30 minutes |
| 6 | 1 hour |
| 7 | 5 hours |
| 8 | 15 hours |
| 9 | 30 hours |


After the configured retries are exhausted, the callback is dropped.

Make your callback endpoint idempotent. A network timeout on your side may cause Namirial Notify to retry even if your system already processed the event. Retries are independent for each event, so they can also change the order in which callbacks arrive.

## Securing your callback endpoint

Namirial Notify does not sign callback payloads with an HMAC or include a shared-secret header. The following compensating controls are practical and recommended.

**Use HTTPS.** Configure `PushNotificationUrl` with an HTTPS endpoint. Plain HTTP exposes the payload in transit and suppresses sensitive fields (`XmissionDetails` on EviMail `Sent` callbacks, `Comments` on EviMail `Replied` callbacks).

**Return 2xx immediately, process asynchronously.** Acknowledge the callback as fast as possible — write the raw body to a queue or durable store before doing any business logic. If your processing takes too long, the platform may time out and retry, producing duplicate deliveries.

**Treat callbacks as notifications, not the source of truth.** A callback tells you something happened. Before taking any irreversible action (sending a legal notice, releasing funds, updating a public record), verify the event by calling the Query endpoint and confirming the state and outcome match what the callback described.

**Deduplicate by `Identifier`.** The platform retries on timeout or non-2xx. Your handler will receive the same callback more than once in failure scenarios. Deduplicate on the `Identifier` field — all retries for the same event carry the same value.

**Do not order callbacks by arrival time.** Different events are delivered and retried independently. Use each callback's `Date` as the event time and reconcile with Query or Get when callbacks arrive out of order or share the same timestamp.

**Use `PushNotificationExtraData` as a correlation value.** The free-text string you set at submit time is echoed back in every callback inside `AdditionalData.ExtraData`. You can set a hard-to-guess value here (for example, a UUID you generate at submit time and store alongside the `eviId`) and verify it on receipt. This does not replace proper authentication but adds a lightweight consistency check.

**Do not rely on source IP allowlisting unless Namirial provides official egress IP ranges for your environment.** Validate callbacks by correlation data and, before irreversible actions, by confirming the current state through the Query API rather than by source IP alone.

If your callback endpoint is temporarily unavailable, callbacks will retry on the schedule in the [Retry policy](#retry-policy) section above. After all retries are exhausted, the callback is dropped permanently. Use the Query endpoint to catch up on missed events.

## Related

- [API documentation](/products/namirialnotify/dev/api-documentation)
- [Integration workflows](/products/namirialnotify/dev/integration-patterns)
- [Security and authentication](/products/namirialnotify/dev/security-best-practices)
- [Performance and scaling](/products/namirialnotify/dev/performance-guidelines)
- [EviSMS API](/products/namirialnotify/apis/evisms-api)
- [EviMail API](/products/namirialnotify/apis/evimail-api)
- [EviNotice API](/products/namirialnotify/apis/evinotice-api)
- [EviPost API](/products/namirialnotify/apis/evipost-api)
- [States and outcomes](/products/namirialnotify/user-guides/states-outcomes)