# EviMail

EviMail is the Namirial Notify service for sending certified emails. The recipient gets a normal email in their inbox, and Namirial Notify records evidence for each phase of the delivery so you can prove that the message was sent, delivered, and — when configured — opened, accepted, or rejected.

This page is the integration guide. For the REST contract, see the [EviMail API reference](/products/namirialnotify/apis/evimail-api). For shared concepts and terminology used across services, see [States and outcomes](/products/namirialnotify/user-guides/states-outcomes) and [Evidence and affidavits](/products/namirialnotify/user-guides/evidences-affidavits).

## When to use EviMail

Pick EviMail when the certified content is the email message itself and your integration needs to prove that the message reached the recipient's mailbox. Pick a different service when:

- You need recipients to access content inside a hosted notice and record their on-platform actions. Use [EviNotice](/products/namirialnotify/services/evinotice).
- You need to deliver to a phone via SMS or RCS. Use [EviSMS](/products/namirialnotify/services/evisms).
- You need an electronic signature workflow on a document. Use [EviSign](/products/namirialnotify/services/evisign).
- You need certified physical mail. Use [EviPost](/products/namirialnotify/services/evipost).


A side-by-side comparison is on the [Services](/products/namirialnotify/services) page.

## How the pieces fit together

EviMail involves a small set of related concepts. Keep these distinct as you read the rest of the page.


```mermaid
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#E4F2F2', 'primaryBorderColor': '#006660', 'primaryTextColor': '#0A1111', 'lineColor': '#047C76', 'secondaryColor': '#CDDBDB', 'tertiaryColor': '#f5fafa', 'edgeLabelBackground': '#f5fafa', 'transitionColor': '#047C76'}}}%%
flowchart LR
    C["EviMail<br/>communication"] --> R["Recipient<br/>(1 main + N CC)"]
    C --> EVS["Events<br/>(Ready, Dispatched, Sent, Delivered,<br/>Read, Replied, Closed, Failed)"]
    EVS --> EV["Evidence items<br/>(state transitions + timestamps)"]
    EV --> AFF["Affidavits<br/>(PDFs signed by Namirial Notify)"]
    EVS --> CB["Callbacks<br/>(filtered by PushNotificationFilter)"]
    CB --> YS["Your system<br/>(HTTPS endpoint)"]
    AFF -.->|"via AffidavitPublished<br/>callback or Query"| YS
```

- **Communication** — a single submission to `POST /v1/EviMail/Submit`. Identified by `eviId` (returned in the response) and, optionally, by your own `LookupKey`.
- **Recipient** — one main recipient (`Recipient.EmailAddress`) and optional `CarbonCopy[]` addresses. Only the main recipient generates evidence events.
- **Event** — a recorded fact during the lifecycle, such as `Sent` or `Delivered`. Some events also drive state transitions.
- **State** — the current phase of the communication. See the [state table](#states-reference) below.
- **Evidence item** — the recorded data behind an event. The Query endpoint exposes evidence summaries; affidavits certify selected items.
- **Affidavit** — a signed PDF that certifies one or more evidence items. The `AffidavitKinds` you choose at submit time control which affidavits are generated.
- **Callback** — an HTTP POST that Namirial Notify sends to your `PushNotificationUrl` when an event matches the `PushNotificationFilter` you configured.


## How EviMail works

The diagram below shows the happy path. The state names match the values returned by the API (see [API reference](/products/namirialnotify/apis/evimail-api#states-and-outcomes)).


```mermaid
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#E4F2F2', 'primaryBorderColor': '#006660', 'primaryTextColor': '#0A1111', 'lineColor': '#047C76', 'secondaryColor': '#CDDBDB', 'tertiaryColor': '#f5fafa', 'edgeLabelBackground': '#f5fafa', 'transitionColor': '#047C76'}}}%%
flowchart TD
    A["POST /v1/EviMail/Submit"] --> B["State: New<br/>submission accepted"]
    B --> C["State: Ready<br/>validated + content certified"]
    C --> D["State: Dispatched<br/>sending requested"]
    D --> E["State: Sent<br/>sent to mail infrastructure"]
    E --> F["State: Delivered<br/>delivery confirmation received"]
    F -.->|optional| G["State: Read<br/>recipient opened the message"]
    F -.->|optional| H["State: Replied<br/>recipient accepted or rejected"]
    G -.->|optional| H
    F --> I["State: Closed<br/>tracking ends, outcome assigned"]
    G --> I
    H --> I
```

`Read` and `Replied` are optional and only appear when configured (open tracking enabled, and `CommitmentOptions` enabled for accept/reject). `Closed` is terminal.

For a state model that includes the failure path, see:


```mermaid
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#E4F2F2', 'primaryBorderColor': '#006660', 'primaryTextColor': '#0A1111', 'lineColor': '#047C76', 'secondaryColor': '#CDDBDB', 'tertiaryColor': '#f5fafa', 'edgeLabelBackground': '#f5fafa', 'transitionColor': '#047C76'}}}%%
stateDiagram-v2
    [*] --> New: Submit accepted
    New --> Ready: content certified
    Ready --> Dispatched: sending requested
    Dispatched --> Sent: sent to mail infrastructure
    Dispatched --> Failed: send error (may retry)
    Sent --> Delivered: delivery confirmation
    Sent --> Failed: delivery error
    Delivered --> Read: recipient opens (when configured)
    Sent --> Replied: accepts/rejects without opening (when configured)
    Delivered --> Replied: accepts/rejects (when configured)
    Read --> Replied: accepts/rejects (when configured)
    Sent --> Closed
    Delivered --> Closed
    Read --> Closed
    Replied --> Closed
    Failed --> Closed
    Closed --> [*]
```

`Failed` is not necessarily terminal. The platform may retry. Wait for `Closed` (with a final `Outcome`) before declaring the communication a permanent failure.

The diagram below combines the happy-path sequence with the callbacks and affidavits that fire at each step. Use it as a quick reference for what to expect at any given state.


```mermaid
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#E4F2F2', 'primaryBorderColor': '#006660', 'primaryTextColor': '#0A1111', 'lineColor': '#047C76', 'secondaryColor': '#CDDBDB', 'tertiaryColor': '#f5fafa', 'edgeLabelBackground': '#f5fafa', 'transitionColor': '#047C76'}}}%%
flowchart LR
    S([Submit]) --> N["New"]
    N --> R["Ready<br/>CB: Ready<br/>AFF: Submitted"]
    R --> D["Dispatched<br/>CB: Dispatched"]
    D --> SE["Sent<br/>CB: Sent<br/>AFF: TransmissionResult"]
    SE --> DL["Delivered<br/>CB: Delivered<br/>AFF: DeliveryResult"]
    DL -.->|optional| RD["Read<br/>CB: Read<br/>AFF: Read"]
    DL -.->|optional| RP["Replied<br/>CB: Replied<br/>AFF: Committed"]
    RD -.->|optional| RP
    DL --> CL["Closed<br/>CB: Closed<br/>AFF: Closed · Complete"]
    RD --> CL
    RP --> CL
```

`CB` = callback delivered to `PushNotificationUrl` (if that kind is in `PushNotificationFilter`). `AFF` = affidavit generated (if that kind is in `AffidavitKinds`). Both are opt-in — configure them at submit time or neither fires.

## Submit a certified email

Only three fields are required: `Subject`, `Body`, and `Recipient.EmailAddress`. Everything else is optional but operationally important.

**Minimal valid request**


```bash
curl -X POST "https://api.evicertia.com/v1/EviMail/Submit" \
  -u "$EVI_USER:$EVI_PASS" \
  -H "Content-Type: application/json" \
  -H "X-Evi-IdempotencyToken: 3f1a8b40-1f4a-4c2c-8f3a-cabf12a14b6c" \
  -d '{
    "Subject": "Your certified policy update",
    "Body": "<p>Hello, please review the attached document.</p>",
    "Recipient": {
      "EmailAddress": "recipient@example.com"
    }
  }'
```

This is enough to get a certified email delivered and tracked. The platform uses account defaults for certification level, language, and all other settings. No callbacks, no affidavits beyond the defaults — useful for a first smoke-test.

Beyond the three required fields, every other field — including everything in `Options` — is optional at the API level. When you omit one, the platform applies an account or site default (for example `Language` follows your site's configured language). The web submission form pre-selects these defaults, which is why they can look mandatory in the portal — but the API only requires `Subject`, `Body`, and `Recipient.EmailAddress`.

**Full production request**


```bash
curl -X POST "https://api.evicertia.com/v1/EviMail/Submit" \
  -u "$EVI_USER:$EVI_PASS" \
  -H "Content-Type: application/json" \
  -H "X-Evi-IdempotencyToken: 3f1a8b40-1f4a-4c2c-8f3a-cabf12a14b6c" \
  --data @evimail-submit.json
```

`evimail-submit.json`:


```json
{
  "Subject": "Your certified policy update",
  "Body": "<p>Hello, please review the attached document.</p>",
  "LookupKey": "ORDER-12345",
  "IssuerName": "Sender Corp.",
  "From": "sender@example.com",
  "ReplyTo": "reply@example.com",
  "Recipient": {
    "LegalName": "Acme Corp",
    "EmailAddress": "recipient@example.com"
  },
  "CarbonCopy": [
    { "Name": "QA Team", "EmailAddress": "qa@example.com" }
  ],
  "Attachments": [
    {
      "DisplayName": "Policy",
      "Filename": "policy.pdf",
      "Data": "JVBERi0xLjQgZXhhbXBsZQ==",
      "IncludeOnAffidavits": true
    }
  ],
  "Options": {
    "CertificationLevel": "Advanced:EU",
    "TimeToLive": 1440,
    "Language": "en",
    "AffidavitLanguage": "en",
    "AffidavitKinds": ["Submitted", "DeliveryResult", "Read", "Closed"],
    "PushNotificationUrl": "https://your-system.example.com/callbacks/evimail",
    "PushNotificationFilter": ["Sent", "Delivered", "Read", "Failed", "Closed"],
    "PushNotificationExtraData": "order-12345",
    "EvidenceAccessControlMethod": "Public",
    "OnlineRetentionPeriod": 1
  }
}
```

On success the response is:


```json
{
  "eviId": "87ffa214e7734bd59b8da8ef00fd80f8"
}
```

Persist both `eviId` and your `LookupKey`. You need them to reconcile against Query results and to correlate incoming callbacks.

### Useful fields you should configure

- **`LookupKey`** — string. Your correlation key for this communication. Passed back verbatim in every callback (`AdditionalData.LookupKey`) and filterable in Query via `WithLookupKeys`. The platform does not enforce uniqueness — two submissions with the same `LookupKey` are both accepted — so enforce uniqueness within your domain. Avoid characters that require URL encoding (spaces, `&`, `#`, etc.) if you intend to use `WithLookupKeys` as a query parameter. Omitting this field makes reconciliation significantly harder.
- **`IssuerName`** — string. The legal entity name declared as the sender of the certified communication. This value appears on the generated affidavit PDFs and on the Namirial Notify hosted evidence page. It is **not** the same as `From` — `IssuerName` is the legal record of who initiated the communication, while `From` is the inbox sender address the recipient sees. Use the full registered legal name of the sending organization.
- **`From`** — email address string. The sender address displayed in the recipient's inbox. Your account is pre-configured with a set of permitted sender addresses. The most common error cases: a malformed or unregistered address returns `400 Bad Request`; using the account's own reserved default From address returns `403 Forbidden`. Confirm the permitted addresses with your Namirial Notify contact before going live — this is a common source of integration failures in pre-production.
- **`ReplyTo`** — email address string. Sets the reply-to header on the delivered email. Affects only how the recipient's mail client handles a reply action; it plays no role in the evidence chain or certification. Omit it if recipients should not be able to reply.
- **`Options.CertificationLevel`** — enum string. The legal framework and regional variant applied to this communication. Base levels are `Standard` and `Advanced`; regional variants include `Standard:EU`, `Advanced:EU`, and country-specific codes such as `CO`, `CR`, `EC`, `MX`, `PE`. The set of values available to your account depends on your subscription — submitting an unsupported level returns `400 Bad Request` with a field-level error. Confirm available levels with your Namirial Notify contact.
- **`Options.TimeToLive`** — integer, minutes. How long the platform continues attempting delivery before marking the communication as expired. When the TTL elapses without a delivery confirmation, the communication transitions to `Failed` and eventually `Closed` with `Outcome: Failed`. If omitted, the account's default TTL is used. Set a value that accommodates slow or greylisting recipient mail servers — a too-short TTL is a common cause of unexpected failures in production.
- **`Options.AffidavitKinds`** — array of enum strings. The affidavit types you want generated for this communication. Generation is asynchronous and fires at the lifecycle moment each type is tied to (see the [affidavit table](#when-each-affidavit-is-generated)). An invalid or unsupported value returns `400`. Choosing more kinds than you need increases storage and callback volume. See [Evidence and affidavits](/products/namirialnotify/user-guides/evidences-affidavits#evimail) for the full catalog and legal implications of each type. **Cannot be combined with `AffidavitProfile`** — submitting both fields in the same request returns `400 Bad Request`.
- **`Options.AffidavitsOnDemandEnabled`** — **(Deprecated)** boolean. Legacy parameter retained for backward compatibility, primarily for SMTP-originated flows. For new integrations, include `OnDemand` in `AffidavitKinds` instead. **Cannot be enabled retroactively.**
- **`Options.PushNotificationUrl`** — URL string. Your HTTPS endpoint to receive callbacks. Using `http://` instead of `https://` reduces the callback payload: `AdditionalData.XmissionDetails` (on `Sent` callbacks) and `AdditionalData.Comments` (on `Replied` callbacks) are stripped from HTTP responses. The URL must be publicly reachable from the Namirial Notify infrastructure — localhost, private network, or VPN-only addresses will silently exhaust retries. See [Callbacks and what you receive](#callbacks-and-what-you-receive) for the full delivery contract.
- **`Options.PushNotificationFilter`** — array of enum strings. The callback kinds you want delivered. Valid values: `Ready`, `Dispatched`, `Sent`, `Delivered`, `Read`, `Replied`, `Failed`, `Closed`, `AffidavitPublished`. Submitting an invalid or duplicate value returns `400 Bad Request`. An empty array disables all callbacks. Subscribe only to the kinds you act on — each extra subscription multiplies retry traffic when your endpoint is unhealthy.
- **`Options.PushNotificationExtraData`** — string. An arbitrary value set at submit time that is echoed verbatim in `AdditionalData.ExtraData` on every callback for this communication. Use it to embed a routing key, tenant identifier, or your internal record ID so your callback handler can process the event without a separate lookup. Any string value is accepted; there is no structural format requirement.
- **`Options.CommitmentOptions`** — enum string. Enables and configures the accept/reject interaction for the recipient. Without this field (or with `Disabled`), the recipient never reaches `Replied` state. **Cannot be changed after submission.** See [Accept/reject commitment](#acceptreject-commitment-commitmentoptions) below for the full behavior and related fields.
- **`Options.EvidenceAccessControlMethod`** — enum string. Controls how the recipient authenticates before accessing the Namirial Notify hosted evidence page. Behavior per value:
  - `Default` — uses the account's configured default method.
  - `Public` — no authentication required; anyone with the link can view the evidence page. Simplest recipient experience.
  - `Session` — **(Deprecated)** submitting this value returns a validation error. Retained in the contract for backward compatibility only.
  - `Challenge` — sends the recipient a one-time authentication code via a secondary channel before granting access.
  - `AutoChallenge` — automatically selects the appropriate challenge method based on the recipient's registered profile.
Use `Public` for lowest friction; use `Challenge` or `AutoChallenge` for communications that contain sensitive content or require stronger access control.
- **`Options.OnlineRetentionPeriod`** — integer, years. How long the communication record — evidence items, affidavits, and the hosted evidence page — remains accessible online through the Namirial Notify platform. After this period, the record may be archived or removed from online access. Note: this does not affect the legal validity of affidavit PDFs you have already downloaded — those are self-contained signed documents. Valid values depend on your account subscription; confirm with your Namirial Notify contact.
- **`Options.AffidavitProfile`** — **(Deprecated)** enum string. Legacy parameter retained for compatibility with existing clients. New integrations should use `AffidavitKinds` instead. Accepted values: `None`, `Basic`, `AdvancedContentOnSubmit`, `AdvancedContentOnClose`, `AdvancedContentOnSubmitAndOnClose`. **Cannot be combined with `AffidavitKinds`** — submitting both fields returns `400 Bad Request`.
- **`Options.Language`** — enum string. The language for the notification email sent to the recipient. Accepted values: `ca`, `de`, `en`, `es`, `fr`, `it`, `pt`, `pt-BR`, `ro`. Defaults to the account's configured language when omitted. This controls recipient-facing UI text, not the content of your `Body` field.
- **`Options.AffidavitLanguage`** — enum string. The language of generated affidavit PDFs. Accepted values: `ca`, `de`, `en`, `es`, `fr`, `it`, `pt`, `pt-BR`, `ro`, `el`. Note `el` (Greek) is available for affidavits but not for `Language`. Defaults to the account's configured affidavit language when omitted.


### Idempotency

Include the `X-Evi-IdempotencyToken` header on every Submit. Use a UUID and keep it across retries.

- A replay that matches a cached `200 OK` returns `202 Accepted` with the original body.
- A replay that arrives while the first request is still in flight returns `409 Conflict`. Back off briefly and resend the same token.
- Responses `400`, `401`, `408`, `409`, `429`, and `5xx` are not cached; the next request with the same token is re-executed.


The response also carries an `X-Evi-IdempotencyStatus` header with one of `New`, `Replay`, or `Conflict`, so your client can branch deterministically without parsing the body.

### Accept/reject commitment (`CommitmentOptions`)

`Options.CommitmentOptions` controls whether and how the recipient can accept or reject the certified communication. It accepts one of four values:

| Value | Effect |
|  --- | --- |
| `Disabled` | No commitment interaction. The recipient receives the email as normal. This is commonly disabled unless platform configuration sets another default. |
| `Accept` | The recipient is shown an Accept button on the Namirial Notify hosted page. |
| `Reject` | The recipient is shown a Reject button. |
| `AcceptOrReject` | The recipient can choose to Accept or Reject. |


When commitment is enabled, the email body includes a link to a hosted Namirial Notify page where the recipient takes the configured action. Their response transitions the communication to `Replied` state with outcome `Accepted` or `Rejected`.

Related fields you can combine with `CommitmentOptions`:

- **`Options.CommitmentCommentsAllowed`** (`true`/`false`) — when `true`, the recipient can enter a free-text comment alongside their response. The comment is included in the `Replied` callback's `AdditionalData.Comments` field (HTTPS endpoints only).
- **`Options.RequireAcceptReason`** and **`Options.AcceptReasons`** — when `RequireAcceptReason` is `true`, the recipient must pick a reason from the `AcceptReasons` list before accepting.
- **`Options.RequireRejectReason`** and **`Options.RejectReasons`** — same pattern for rejection.


Accept/reject reason fields require `CommitmentCommentsAllowed: true`. Accept-only reason fields are not valid with `CommitmentOptions: Reject`, and reject-only reason fields are not valid with `CommitmentOptions: Accept`.

When the recipient completes the action, the `Replied` callback fires with `AdditionalData.Kind` identifying whether the action was an acceptance or rejection. The `Committed` or `CommittedAdvanced` affidavit is generated at this point (if included in `AffidavitKinds`).

`CommitmentOptions` cannot be changed after submission. If your use case requires acceptance or rejection, enable it at submit time.

## What the recipient sees

### Standard certified email (no commitment)

The recipient receives a normal email in their inbox. The email may include a banner or header indicating it is a certified communication (controlled by `Options.DeliveryAppearance` or the legacy `Options.HideBanners` field).

No action is required from the recipient. Namirial Notify records delivery evidence and, when open tracking is available and configured, records the open as a `Read` event.

### Certified email with accept/reject commitment

When `Options.CommitmentOptions` is set to `Accept`, `Reject`, or `AcceptOrReject`, the email includes a link to a Namirial Notify hosted confirmation page. The recipient:

1. Receives the email in their inbox.
2. Clicks the link in the email to open the hosted page.
3. Reviews the content and selects the configured action (accept, reject, or both).
4. Optionally adds a comment (if `CommitmentCommentsAllowed` is `true`).
5. Submits. The platform records the response and moves the communication to `Replied`.


The recipient's response is what generates the `Replied` callback and the `Committed` affidavit. If the recipient never clicks the link, the communication eventually reaches `Closed` without a `Replied` state.

The hosted confirmation page is served and branded by Namirial Notify. The `EvidenceAccessControlMethod` you set at submit time controls how recipients authenticate before they can access the page.

## Callbacks and what you receive

Namirial Notify pushes JSON over HTTP to your `PushNotificationUrl` whenever a state change matches the configured `PushNotificationFilter`. Callbacks are per-communication and opt-in.


```mermaid
%%{init: {'theme': 'base', 'themeVariables': {'actorBkg': '#E4F2F2', 'actorBorder': '#006660', 'actorTextColor': '#0A1111', 'actorLineColor': '#047C76', 'signalColor': '#047C76', 'signalTextColor': '#0A1111', 'noteBkgColor': '#CDDBDB', 'noteTextColor': '#0A1111', 'noteBorderColor': '#006660', 'activationBkgColor': '#CDDBDB', 'activationBorderColor': '#006660', 'labelBoxBkgColor': '#E4F2F2', 'labelBoxBorderColor': '#006660', 'labelTextColor': '#0A1111', 'loopTextColor': '#0A1111'}}}%%
sequenceDiagram
    participant N as Namirial Notify
    participant Y as Your callback endpoint
    Note over N: state transition fires
    N->>N: filter check (PushNotificationFilter ∋ Kind?)
    N->>Y: POST PushNotificationUrl (JSON, no auth header)
    alt 2xx within timeout
        Y-->>N: 200 OK
        Note over N: callback delivery acknowledged
    else error or timeout
        Y-->>N: timeout or non-2xx
        Note over N: retry after a configured delay
        N->>Y: POST again (same Identifier)
    end
```

Every callback shares the same envelope:


```json
{
  "Identifier": "1234",
  "Kind": "Sent",
  "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 policy update",
    "LookupKey": "ORDER-12345",
    "State": "Sent",
    "CreationDate": "2026-01-22T12:46:12.3113880+01:00",
    "XmissionDetails": "Successfully sent to recipient's mail server.",
    "ExtraData": "order-12345"
  }
}
```

Use `Identifier` as the idempotency key on your side. The shared [Callbacks and webhooks](/products/namirialnotify/dev/callbacks) page documents the full payload structure, retry behavior, and per-service field tables.

**HTTPS unlocks extra detail.** The `XmissionDetails` field on `Sent` callbacks and the `Comments` field on `Replied` callbacks are only included when `PushNotificationUrl` uses HTTPS.

### Securing your callback endpoint

Namirial Notify sends callbacks as plain HTTP POST requests with no authentication header — there is nothing in the request itself that proves it came from the platform. Apply layered defenses on your endpoint:

**Use HTTPS.** Required for the full payload and for any endpoint handling certified evidence data. HTTP endpoints are accepted by the API but should only be used during local development.

**Restrict by IP.** Allowlist only the egress IP ranges used by the Namirial Notify callback infrastructure at your network or load-balancer layer. Requests from any other source are dropped before they reach your application. Obtain the current IP range list from your Namirial Notify contact — ranges can change, so subscribe to update notifications if your contact provides them.

**Validate the payload before acting on it.** On every incoming callback, check:

- `EvidenceType` equals `"eviMail"` — reject anything else silently.
- `EvidenceId` matches a communication you submitted — cross-reference against your own records and discard unknown IDs.
- `Kind` is one of the values you subscribed to in `PushNotificationFilter` — anything outside that set is unexpected.


Log and discard any callback that fails these checks. Do not update business state based on a callback you cannot corroborate.

**Keep the URL low-profile.** Treat `PushNotificationUrl` as a secret: do not publish it in client-side code, documentation, or unredacted logs. If you suspect it has been exposed, set a new URL on future submissions — the URL is fixed at submit time and cannot be changed for in-flight communications, so rotate promptly and monitor existing open communications until they close.

**Respond quickly with a `2xx`.** Return the status code before doing any heavy processing, and handle the work in a background queue. A slow response risks a timeout-triggered retry, which multiplies traffic on your endpoint and may create duplicate processing if your handler is not idempotent.

### Additional callback payload examples

**`Delivered` — delivery confirmation received**


```json
{
  "Identifier": "2345",
  "Kind": "Delivered",
  "Date": "2026-01-22T12:47:05.0000000+01:00",
  "EvidenceId": "87ffa214e7734bd59b8da8ef00fd80f8",
  "EvidenceType": "eviMail",
  "Site": "my-site",
  "Owner": null,
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "From": "sender@example.com",
    "To": "recipient@example.com",
    "Subject": "Your certified policy update",
    "LookupKey": "ORDER-12345",
    "State": "Delivered",
    "CreationDate": "2026-01-22T12:46:12.3113880+01:00",
    "Progress": "Confirmation of message delivery",
    "Description": "The message or the notification with the link were delivered to the final recipient, but the content still has not been opened/read.",
    "ExtraData": "order-12345"
  }
}
```

`Delivered` is not a final outcome. Wait for `Closed` before marking a transaction complete. The `DeliveryResult` affidavit is generated at this point (if included in `AffidavitKinds`).

**`Read` — recipient opened the message (open tracking enabled)**


```json
{
  "Identifier": "3456",
  "Kind": "Read",
  "Date": "2026-01-22T13:15:42.0000000+01:00",
  "EvidenceId": "87ffa214e7734bd59b8da8ef00fd80f8",
  "EvidenceType": "eviMail",
  "Site": "my-site",
  "Owner": null,
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "From": "sender@example.com",
    "To": "recipient@example.com",
    "Subject": "Your certified policy update",
    "LookupKey": "ORDER-12345",
    "State": "Read",
    "CreationDate": "2026-01-22T12:46:12.3113880+01:00",
    "Progress": "Notification of opening or reading of the message",
    "Description": "The message has been opened or read by the final recipient.",
    "ExtraData": "order-12345"
  }
}
```

Open tracking is best-effort. Deduplicate on `Identifier` — the same open event can fire more than once if the recipient opens the message multiple times. The `Read` affidavit is generated only once.

**`Failed` — delivery attempt failed**


```json
{
  "Identifier": "4567",
  "Kind": "Failed",
  "Date": "2026-01-22T12:51:00.0000000+01:00",
  "EvidenceId": "c3d4e5f601234bd59b8da8ef00fd80f8",
  "EvidenceType": "eviMail",
  "Site": "my-site",
  "Owner": null,
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "From": "sender@example.com",
    "To": "recipient@example.com",
    "Subject": "Your certified policy update",
    "LookupKey": "ORDER-99999",
    "State": "Failed",
    "CreationDate": "2026-01-22T12:46:12.3113880+01:00",
    "Progress": "There was an error that precludes normal processing of the message.",
    "Description": "An unrecoverable error occurred or the retry limit has been reached, making it impossible to send the message.",
    "ExtraData": "order-99999"
  }
}
```

`Failed` is not terminal on its own — the platform may retry. Do not resubmit. Wait for the `Closed` callback and check `AdditionalData.Outcome`. Permanent failure is `Closed` with `Outcome: Failed`.

**`Replied` — recipient accepted (HTTPS endpoint, `CommitmentOptions` enabled)**


```json
{
  "Identifier": "5678",
  "Kind": "Replied",
  "Date": "2026-01-22T14:23:10.0000000+01:00",
  "EvidenceId": "87ffa214e7734bd59b8da8ef00fd80f8",
  "EvidenceType": "eviMail",
  "Site": "my-site",
  "Owner": null,
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "From": "sender@example.com",
    "To": "recipient@example.com",
    "Subject": "Your certified policy update",
    "LookupKey": "ORDER-12345",
    "State": "Replied",
    "CreationDate": "2026-01-22T12:46:12.3113880+01:00",
    "Kind": "Accepted",
    "Comments": "I agree with the terms and conditions.",
    "ExtraData": "order-12345"
  }
}
```

`AdditionalData.Kind` is `Accepted` or `Rejected` — branch on this to determine the recipient's commitment decision. `Comments` is only present when `PushNotificationUrl` uses HTTPS and `CommitmentCommentsAllowed` was `true`.

**`Closed` — lifecycle ended, outcome assigned**


```json
{
  "Identifier": "9012",
  "Kind": "Closed",
  "Date": "2026-01-22T14:25:00.0000000+01:00",
  "EvidenceId": "87ffa214e7734bd59b8da8ef00fd80f8",
  "EvidenceType": "eviMail",
  "Site": "my-site",
  "Owner": null,
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "From": "sender@example.com",
    "To": "recipient@example.com",
    "Subject": "Your certified policy update",
    "LookupKey": "ORDER-12345",
    "State": "Closed",
    "CreationDate": "2026-01-22T12:46:12.3113880+01:00",
    "Outcome": "Accepted",
    "OutcomeDescription": "The communication was accepted by the recipient.",
    "ExtraData": "order-12345"
  }
}
```

`Closed` is the terminal event. `AdditionalData.Outcome` holds the final result — `Delivered`, `Accepted`, `Rejected`, `Failed`, etc. Always wait for `Closed` before marking a transaction complete on your side.

**`AffidavitPublished` — an affidavit has been generated and is ready to download**


```json
{
  "Identifier": "6789",
  "Kind": "AffidavitPublished",
  "Date": "2026-01-22T12:48:30.0000000+01:00",
  "EvidenceId": "87ffa214e7734bd59b8da8ef00fd80f8",
  "EvidenceType": "eviMail",
  "Site": "my-site",
  "Owner": null,
  "OwnerEmail": "sender@example.com",
  "AdditionalData": {
    "From": "sender@example.com",
    "To": "recipient@example.com",
    "Subject": "Your certified policy update",
    "LookupKey": "ORDER-12345",
    "State": "Delivered",
    "CreationDate": "2026-01-22T12:46:12.3113880+01:00",
    "AffidavitId": "a1b2c3d4e5f641239726a87000c1f704",
    "AffidavitKind": "DeliveryResult",
    "AffidavitName": "Certification of delivery",
    "ExtraData": "order-12345"
  }
}
```

One `AffidavitPublished` callback fires per generated affidavit. Persist `AdditionalData.AffidavitId` — it is the key for downloading the PDF from the API. `AdditionalData.AffidavitKind` tells you which lifecycle moment the affidavit covers (see the [affidavit table](#when-each-affidavit-is-generated)). For on-demand affidavits requested via `POST /v1/EviMail/AffidavitRequest`, the callback also includes `AdditionalData.RequestId` so you can correlate it with your original request. If the affidavit was regenerated, `AdditionalData.Regenerated` is `true`.

### What each callback means

| `Kind` | When it fires | Final business outcome? | Affidavit-bearing? | Extra fields in `AdditionalData` | What you should do |
|  --- | --- | --- | --- | --- | --- |
| `Ready` | After admission and content certification | No | Indirect (`Submitted` / `SubmittedAdvanced`) | — | Optional internal "queued" → "processed" transition |
| `Dispatched` | After sending is requested and the message is ready for the sender component | No | — | `Progress`, `Description` | Update tracking |
| `Sent` | After the message is sent to mail infrastructure | No | `TransmissionResult` | `XmissionDetails` (HTTPS only) | Update tracking; do not retry on your side |
| `Delivered` | After delivery confirmation is received | No | `DeliveryResult` | `Progress`, `Description` | Treat as confirmed delivery |
| `Read` | After open is detected | No | `Read` | `Progress`, `Description` | Open tracking is best-effort. Deduplicate on `Identifier` |
| `Replied` | After the recipient accepts or rejects (when `CommitmentOptions` enabled) | Yes, if it occurs | `Committed` / `CommittedAdvanced` | `Kind` (accept / reject), `Comments` (HTTPS only) | Branch on `AdditionalData.Kind`; persist `Comments` when present |
| `Closed` | When tracking ends | Yes | `Closed`, `Complete`, `Failed` | `Outcome`, `OutcomeDescription` | Mark the transaction terminal |
| `Failed` | On send or delivery failure | No (terminal failure is signaled by `Closed` with `Outcome: Failed`) | `Failed` (when terminal) | `Progress`, `Description` | Do not resubmit; wait for `Closed` |
| `AffidavitPublished` | Each time an affidavit is published (per-event or on-demand) | No | The affidavit itself | `AffidavitId`, `AffidavitKind`, `AffidavitName`; `RequestId` if on-demand; `Regenerated` if true | Download the affidavit by `AffidavitId` or include it in the next Query |


### `AdditionalData` fields by event

All callbacks share a common set of fields. Some fields only appear on specific event types or under specific conditions.

**Fields present in every callback**

| Field | Type | Description |
|  --- | --- | --- |
| `From` | string | Sender address as configured on Submit |
| `To` | string | Main recipient address |
| `Subject` | string | Email subject |
| `LookupKey` | string | Your correlation key (omitted if not set on Submit) |
| `State` | string | Current communication state at the time of the callback |
| `CreationDate` | ISO 8601 | Timestamp when the communication was first submitted |
| `ExtraData` | string | Verbatim copy of `PushNotificationExtraData` from Submit (omitted if not set) |


**Event-specific fields**

| Field | Present on | Condition | Description |
|  --- | --- | --- | --- |
| `XmissionDetails` | `Sent` | HTTPS endpoint only | Human-readable summary of the SMTP handoff result |
| `Progress` | `Dispatched`, `Delivered`, `Read`, `Failed` | Always | Human-readable string describing the current processing stage |
| `Description` | `Dispatched`, `Delivered`, `Read`, `Failed` | Always | Human-readable description of the event outcome |
| `Kind` | `Replied` | Always | `Accepted` or `Rejected` — the recipient's commitment decision |
| `Comments` | `Replied` | HTTPS endpoint + `CommitmentCommentsAllowed: true` | Free-text comment entered by the recipient |
| `Outcome` | `Closed` | Always | Final outcome: `Delivered`, `Accepted`, `Rejected`, `Failed`, etc. |
| `OutcomeDescription` | `Closed` | Always | Human-readable description of the final outcome |
| `AffidavitId` | `AffidavitPublished` | Always | Unique identifier of the generated affidavit — use this to download the PDF |
| `AffidavitKind` | `AffidavitPublished` | Always | Which affidavit type was generated (e.g. `DeliveryResult`, `Committed`) |
| `AffidavitName` | `AffidavitPublished` | Always | Display name of the affidavit document |
| `RequestId` | `AffidavitPublished` | On-demand affidavits only | Correlates the callback to the original `AffidavitRequest` call |
| `Regenerated` | `AffidavitPublished` | Only when `true` | Present and `true` when this affidavit replaced a previously generated one |


## States reference

The Query endpoint and callbacks both expose the `State` value. Use this table to interpret it.

| Display name | Technical name | When it occurs | Final? | Callback repeatable? | Related callback | Affidavit implication | What you should do |
|  --- | --- | --- | --- | --- | --- | --- | --- |
| Draft | `Draft` | Saved in UI but not submitted | No | No | — | — | Not visible via API |
| New | `New` | Submit accepted; admission and content certification begin | No | No | — | `Submitted` / `SubmittedAdvanced` once certified | Store `eviId` and `LookupKey` for reconciliation |
| Ready | `Ready` | Validated, certified, and ready for sending | No | No | `Ready` | — | Optional UI transition |
| Dispatched | `Dispatched` | Sending has been requested; the message is ready for the sender component | No | No | `Dispatched` | — | Update tracking; not yet sent or delivered |
| Sent | `Sent` | Message has been sent to mail infrastructure | No | No | `Sent` | `TransmissionResult` | Reconcile against your in-flight set |
| Delivered | `Delivered` | Delivery confirmation was received | No | No | `Delivered` | `DeliveryResult` | Surface "delivered" to the operator |
| Read | `Read` | Recipient opened the message (when supported) | No | **Yes** — fires on each open; deduplicate on `Identifier` | `Read` | `Read` (generated once regardless) | Treat as best-effort; deduplicate on `Identifier` |
| Replied | `Replied` | Recipient accepted or rejected (when configured) | No | No | `Replied` | `Committed` / `CommittedAdvanced` | Branch on `Kind` from the callback |
| Closed | `Closed` | Tracking ended; outcome assigned | Yes | No | `Closed` | `Closed` / `ClosedAdvanced`, often `Complete` / `CompleteAdvanced` | Mark the transaction terminal |
| Failed | `Failed` | Delivery failure occurred; retries may still follow | No | **Yes** — fires on each retry attempt | `Failed` | `Failed` (when terminal) | Wait for `Closed` before declaring permanent failure |


`Unknown` is enum padding for forward compatibility; treat it as "unmapped" and rely on the recorded timestamps.

For outcomes (`None`, `Certified`, `Sent`, `Delivered`, `Read`, `Accepted`, `Rejected`, `Failed`), see the [API reference](/products/namirialnotify/apis/evimail-api#states-and-outcomes).

## Evidence and affidavits

EviMail records evidence for relevant lifecycle and technical events, and the `AffidavitKinds` you set at submit time control which of those items get certified into signed PDF affidavits. The full catalog and formal definitions are in [Evidence and affidavits](/products/namirialnotify/user-guides/evidences-affidavits#evimail).

### When each affidavit is generated

The table below maps the `AffidavitKinds` values to the lifecycle moment that triggers generation. All values are optional and configured at submit time except `OnDemand`, which is triggered on request.

| `AffidavitKinds` value | Triggered at | Notes | `AffidavitPublished` callback fires? |
|  --- | --- | --- | --- |
| `Submitted` | `New → Ready` (content certified) | Certifies admission, content, and metadata | Yes, if in `PushNotificationFilter` |
| `SubmittedAdvanced` | `New → Ready` | Advanced variant; includes rendered body | Yes |
| `TransmissionResult` | `Sent` (SMTP response received) | Certifies the handoff to the recipient mail server | Yes |
| `DeliveryResult` | `Delivered` (delivery confirmation/result received) | Certifies the delivery result | Yes |
| `Read` | `Read` (recipient opened the message) | Only generated once, even if open is detected multiple times | Yes |
| `Committed` | `Replied` (recipient accepted or rejected) | Certifies the acceptance or rejection action | Yes |
| `CommittedAdvanced` | `Replied` | Advanced variant of the commitment affidavit | Yes |
| `Closed` | `Closed` | Certifies the end of tracking | Yes |
| `ClosedAdvanced` | `Closed` | Advanced variant | Yes |
| `Complete` | `Closed` | Certifies the full chain from submission to closure, regardless of final result | Yes |
| `CompleteAdvanced` | `Closed` | Advanced variant of the complete lifecycle affidavit, regardless of final result | Yes |
| `OnDemand` | On request via `POST /v1/EviMail/AffidavitRequest` | Requires `OnDemand` in `AffidavitKinds` at submit time and an eligible, non-closed communication | Yes, with `RequestId` in `AdditionalData` |
| `Event` | Technical events when generated by the configured affidavit kinds | Internal technical record for events that do not have a more specific affidavit kind | Yes |
| `Failed` | `Closed` following terminal failure | Only when the final outcome is failure | Yes |


**Key things to know:**

- Every generated affidavit fires an `AffidavitPublished` callback to the original `PushNotificationUrl`, but only if `AffidavitPublished` is in `PushNotificationFilter`. The callback includes `AffidavitId`, `AffidavitKind`, and `AffidavitName` in `AdditionalData`.
- Affidavit generation is **asynchronous**. The affidavit is not available instantly after the state transition — wait for the `AffidavitPublished` callback or poll with `IncludeAffidavits=true` on Query.
- To enable on-demand affidavits, include `OnDemand` in `AffidavitKinds` at submit time. This **cannot be enabled retroactively**.
- On-demand affidavit requests are rejected when the communication is `Draft`, already `Closed`, not enabled for on-demand affidavits, not allowed by its affidavit profile, over the configured maximum request count, requested by a non-owner, or using `Notify` delivery mode.
- You can also retrieve affidavit metadata and binary content via the Query endpoint without subscribing to callbacks.


If `OnDemand` was included in `AffidavitKinds` at submit time and the communication is still eligible, you can request an additional affidavit later with `POST /v1/EviMail/AffidavitRequest` (see the [API reference](/products/namirialnotify/apis/evimail-api#request-an-on-demand-affidavit)). The generation is asynchronous; you receive an `AffidavitPublished` callback when it is ready.

## Querying status

Use the Query endpoint as a fallback when callbacks lag, when you need a point-in-time snapshot, or for batch reconciliation. The most common filters:

- `WithUniqueIds=<eviId>` — fetch one or more communications by id.
- `WithLookupKeys=<your-key>` — fetch by your correlation key.
- `OnState=Closed` — fetch only closed communications.
- `WithOutcome=Delivered` — fetch by final outcome.
- `IncludeAffidavits=true` — include affidavit metadata in results.
- `IncludeAffidavitBlobs=true` — include the affidavit binary content (heavy; use sparingly).


Example:


```bash
curl -X GET "https://api.evicertia.com/v1/EviMail/Query?WithLookupKeys=ORDER-12345&IncludeAffidavits=true" \
  -u "$EVI_USER:$EVI_PASS"
```

Response:


```json
{
  "totalMatches": 1,
  "results": [
    {
      "uniqueId": "87ffa214e7734bd59b8da8ef00fd80f8",
      "lookupKey": "ORDER-12345",
      "state": "Closed",
      "outcome": "Delivered",
      "creationDate": "2026-01-22T12:46:12.3113880Z",
      "sentOn": "2026-01-22T12:46:32.4830752Z",
      "deliveredOn": "2026-01-22T12:47:05.1023400Z",
      "affidavits": [
        { "uniqueId": "…", "kind": "Submitted", "name": "Certification of admission" },
        { "uniqueId": "…", "kind": "DeliveryResult", "name": "Certification of delivery" },
        { "uniqueId": "…", "kind": "Closed", "name": "Certification of closure" }
      ]
    }
  ]
}
```

For the full filter and response schema, see the [API reference](/products/namirialnotify/apis/evimail-api#query-certified-emails).

## First-call walkthrough

Run through these steps before wiring EviMail into production. The goal is to confirm that the full round-trip — submit, receive callbacks, verify state, and download an affidavit — works end-to-end from your system.

### What you need

- **API credentials** — `EVI_USER` and `EVI_PASS` for your account. Obtain them from your Namirial Notify contact. Keep them out of source control.
- **A recipient address you control** — use a personal or team inbox so you can observe what the recipient receives and verify delivery.
- **An HTTPS endpoint for callbacks** (optional for the smoke-test, required to receive `XmissionDetails` and `Comments`) — the URL must be reachable from the public internet. If you do not have one yet, use a webhook inspection tool or a local tunnel to expose a port on your machine temporarily.


### Step 1 — Send your first communication

Submit a minimal request using a recipient address you control. Generate a fresh UUID for the idempotency token each time you submit a new communication.


```bash
curl -X POST "https://api.evicertia.com/v1/EviMail/Submit" \
  -u "$EVI_USER:$EVI_PASS" \
  -H "Content-Type: application/json" \
  -H "X-Evi-IdempotencyToken: 3f1a8b40-1f4a-4c2c-8f3a-cabf12a14b6c" \
  -d '{
    "Subject": "Test certified email",
    "Body": "<p>This is a test from EviMail integration.</p>",
    "LookupKey": "TEST-001",
    "Recipient": {
      "EmailAddress": "your-inbox@example.com"
    },
    "Options": {
      "AffidavitKinds": ["Complete"],
      "PushNotificationUrl": "https://your-endpoint.example.com/callbacks/evimail",
      "PushNotificationFilter": ["Sent", "Delivered", "Closed", "AffidavitPublished"]
    }
  }'
```

**Expected response:**


```json
{ "eviId": "87ffa214e7734bd59b8da8ef00fd80f8" }
```

Save `eviId`. If you get `400`, check `responseStatus.errors[]` for the failing field. If you get `401` or `403`, confirm credentials and that your account is provisioned for EviMail.

### Step 2 — Verify the state progresses

Query the communication a few seconds after submitting to confirm it has moved out of `New`:


```bash
curl "https://api.evicertia.com/v1/EviMail/Query?WithLookupKeys=TEST-001" \
  -u "$EVI_USER:$EVI_PASS"
```

You should see `"state": "Dispatched"` or `"state": "Sent"` within seconds. If still `"state": "New"`, wait and retry — the platform is still processing admission. If it stays in `New` for more than a minute, contact support.

### Step 3 — Confirm delivery

If you configured a `PushNotificationUrl`, check your endpoint logs as the communication progresses. You should receive callbacks with `Kind: Sent`, then `Kind: Delivered`, and finally `Kind: Closed`.

If you did not configure a callback URL, poll Query every 10–30 seconds until `"state": "Closed"`:


```bash
curl "https://api.evicertia.com/v1/EviMail/Query?WithLookupKeys=TEST-001" \
  -u "$EVI_USER:$EVI_PASS"
```

When `Closed`, check `"outcome"`. For a valid recipient address, the expected outcome is `"Delivered"`. Also check your inbox — the test email should have arrived.

### Step 4 — Download the affidavit

Because you included `Complete` in `AffidavitKinds`, an `AffidavitPublished` callback fires when the affidavit is ready. Query with `IncludeAffidavits=true` to get the affidavit metadata:


```bash
curl "https://api.evicertia.com/v1/EviMail/Query?WithLookupKeys=TEST-001&IncludeAffidavits=true" \
  -u "$EVI_USER:$EVI_PASS"
```

Look for `affidavits[]` in the response. Use the `uniqueId` of the `Complete` affidavit to download the PDF via the Affidavit endpoint (see the [API reference](/products/namirialnotify/apis/evimail-api)). Open it and confirm it is signed and shows the correct submission and delivery timestamps.

### Step 5 — Test the failure path

Resubmit using an address that will fail delivery — confirm this address with your Namirial Notify contact. Verify that:

1. The communication eventually reaches `"state": "Closed"` with `"outcome": "Failed"`.
2. You receive a `Kind: Failed` callback followed by `Kind: Closed` (not the other way around).
3. Your system handles `Outcome: Failed` without triggering an automatic resubmit.


This exercises your error-handling code before any real communications go through.

## Integration checklist

- Set `LookupKey` on every Submit and persist `eviId` from the response.
- Send `X-Evi-IdempotencyToken` (UUIDv4) on every Submit. Treat `202 Accepted` as success and `409 Conflict` as "retry with the same token after a short backoff".
- Use HTTPS for `PushNotificationUrl` so you receive `XmissionDetails` and `Comments`.
- Subscribe only to the callback kinds you act on. Each extra subscription multiplies retry traffic when your endpoint hiccups.
- Make your callback handler idempotent, keyed on `Identifier`. The platform retries on non-2xx and on timeouts.
- Store the raw callback body before parsing. This is invaluable for audit and for debugging shape drift.
- Use Query as a fallback when callbacks lag. Filter by `LookupKey` and use `IncludeAffidavits=true` when you need affidavit metadata.
- Include `OnDemand` in `AffidavitKinds` at submit time if you intend to call `AffidavitRequest` later. This cannot be added retroactively.
- Persist `AffidavitId` from `AffidavitPublished` callbacks. Affidavits are the legal artifact you will need during disputes.
- Validate `PushNotificationFilter` values at submit time. Unsupported or repeated values are rejected by the API, so catch typos before submitting.
- Test the happy path and the `Failed` → `Closed` (`Outcome: Failed`) path in pre-production before going live.


## Troubleshooting

| Symptom | Likely causes | What to check | What to do |
|  --- | --- | --- | --- |
| No callbacks received | `PushNotificationUrl` missing on Submit; `PushNotificationFilter` omits the `Kind` you expect; endpoint returned non-2xx and retries exhausted; firewall blocks Namirial Notify egress | Events via Query; server access logs for the callback `Identifier` | Confirm filter and URL; make endpoint publicly reachable; use Query polling as fallback |
| Message stays in `Dispatched` or `Sent` | Sender processing still pending; recipient MTA slow or greylisting; mailbox full | Timestamps in Query response: `dispatchedOn`, `sentOn`, `deliveredOn` | Wait for `Closed`; do not resubmit |
| `deliveredOn` is missing but `readOn` is present | `deliveredOn` depends on a DSN from the recipient's mail server, which many servers never send; `readOn` is tracked independently via MDN or the tracking pixel | Both timestamps in the Query response | This is expected — the open was tracked even though no delivery notification arrived. Treat the message as delivered. See [Why delivery and read tracking can be incomplete](/products/namirialnotify/user-guides/states-outcomes#why-delivery-and-read-tracking-can-be-incomplete) |
| Recipient says they did not get the email | Spam filter; recipient address typo; alias forwarding broke | `Delivered` callback and `deliveredOn` timestamp; `XmissionDetails` on the `Sent` callback (HTTPS only) | Confirm the address; ask recipient to check spam; if address is wrong, correct and resubmit with a new idempotency token |
| Duplicate callbacks | Endpoint timed out or returned non-2xx; platform retried | `Identifier` field — duplicates share the same value | Deduplicate on `Identifier`; make the callback handler idempotent |
| Affidavit not available | Generation is asynchronous; `AffidavitPublished` has not fired yet; `OnDemand` was not included in `AffidavitKinds` at submit (on-demand requests blocked) | Query with `IncludeAffidavits=true` | Wait for `AffidavitPublished` callback; for on-demand, if `RequestId` was returned but no callback arrived, contact support with that `RequestId` |
| Submit → `400 Bad Request` | Missing required field (`Subject`, `Body`, or `Recipient.EmailAddress`); invalid or unsupported enum value (e.g. `CertificationLevel` not on account); attachment too large | `responseStatus.errors[]` for field-level details | Fix the flagged field; retry with a **new** idempotency token |
| Submit → `401 Unauthorized` | Wrong environment base URL; credentials rotated | `Authorization` header value; confirm target environment | Re-issue credentials through your Namirial Notify contact |
| Submit → `403 Forbidden` | Account not provisioned for EviMail; `From` address not permitted; restricted header in request | `responseStatus.message` | Contact your Namirial Notify account manager |
| Submit → `409 Conflict` | Another Submit with the same `X-Evi-IdempotencyToken` is still in flight | — | Back off briefly; retry with the **same** token; do not generate a new one |
| Callback payload missing expected fields | Subscribed to a different `Kind` than the one you are reading; field is HTTPS-only and URL is HTTP; mis-routing on `Kind` | `Kind` and `EvidenceType` on the incoming payload | Route on `Kind` + `EvidenceType` first; switch `PushNotificationUrl` to HTTPS to unlock `XmissionDetails` and `Comments`; see [Callbacks and webhooks](/products/namirialnotify/dev/callbacks) for full field tables |


## Related

- [Services](/products/namirialnotify/services)
- [EviNotice](/products/namirialnotify/services/evinotice)
- [EviSMS](/products/namirialnotify/services/evisms)
- [EviPost](/products/namirialnotify/services/evipost)
- [EviMail API reference](/products/namirialnotify/apis/evimail-api)
- [Callbacks and webhooks](/products/namirialnotify/dev/callbacks)
- [States and outcomes](/products/namirialnotify/user-guides/states-outcomes)
- [EviMail evidence lifecycle](/products/namirialnotify/user-guides/states-outcomes#evimail-evidence-lifecycle)
- [Evidence and affidavits](/products/namirialnotify/user-guides/evidences-affidavits)
- [Error handling](/products/namirialnotify/dev/error-handling)
- [How to create a new EviMail (issuer UI)](/products/namirialnotify/issuer/create-evimail)