Suppressions & delivery feedback
The suppression list is a per-(org, env) blocklist of email addresses that Notavia will not deliver to. Addresses are added automatically when SES reports a permanent bounce or a spam complaint, and can also be added or removed manually via the API or dashboard.
All four endpoints require a full-access API key (nsk_live_... or
nsk_test_... with full_access scope).
The suppression object
Section titled “The suppression object”{ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "address": "alice@example.com", "reason": "hard_bounce", "source": "provider", "diagnostic_detail": "550 5.1.1 The email account does not exist.", "triggering_notification_id": "8d3a1c2e-4f5b-6789-abcd-ef0123456789", "suppressed_at": "2026-05-30T14:22:00Z"}| Field | Type | Notes |
|---|---|---|
id | UUID | Unique identifier for this suppression entry. |
address | string | The suppressed email address. |
reason | string enum | One of hard_bounce, complaint, manual_add. |
source | string enum | provider — added by the SES feedback pipeline; manual — added via the API or dashboard. |
diagnostic_detail | string | null | The raw bounce or complaint diagnostic string from SES, if available. Null for manually added entries. |
triggering_notification_id | UUID | null | The ID of the notification that caused the suppression, if applicable. Null for manually added entries. |
suppressed_at | ISO 8601 | UTC timestamp of when the address was suppressed. |
GET /v1/suppressions
Section titled “GET /v1/suppressions”Returns a cursor-paginated list of suppressed addresses for the authenticated environment.
Query parameters
Section titled “Query parameters”| Parameter | Type | Default | Notes |
|---|---|---|---|
address | string | — | Filter to entries whose address contains this value (case-insensitive substring match). |
reason | string | — | Filter by reason. Accepted values: HardBounce, Complaint, ManualAdd (case-insensitive). |
limit | integer | 25 | Maximum number of results per page. |
cursor | string | — | Opaque pagination cursor from the previous page’s next_cursor field. |
Response — 200 OK
Section titled “Response — 200 OK”{ "data": [ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "address": "alice@example.com", "reason": "hard_bounce", "source": "provider", "diagnostic_detail": "550 5.1.1 The email account does not exist.", "triggering_notification_id": "8d3a1c2e-4f5b-6789-abcd-ef0123456789", "suppressed_at": "2026-05-30T14:22:00Z" } ], "next_cursor": "eyJpZCI6IjNmYTg1ZjY0In0", "has_more": true}Example
Section titled “Example”curl https://api.notavia.saas-infrastructure.com/v1/suppressions?reason=HardBounce&limit=50 \ -H "Authorization: Bearer nsk_live_..."GET /v1/suppressions/{address}
Section titled “GET /v1/suppressions/{address}”Returns the suppression entry for a specific address.
Path parameters
Section titled “Path parameters”| Parameter | Notes |
|---|---|
address | The email address to look up. URL-encode the value if it contains special characters. |
Response — 200 OK
Section titled “Response — 200 OK”Returns the suppression object.
Response — 404 Not Found
Section titled “Response — 404 Not Found”{ "error": { "type": "not_found", "code": "suppression_not_found", "message": "No suppression found for address 'alice@example.com'.", "param": null }}Example
Section titled “Example”curl https://api.notavia.saas-infrastructure.com/v1/suppressions/alice%40example.com \ -H "Authorization: Bearer nsk_live_..."POST /v1/suppressions
Section titled “POST /v1/suppressions”Manually adds an address to the suppression list. If the address is already suppressed, the existing entry is returned unchanged.
Request body
Section titled “Request body”{ "address": "alice@example.com"}| Field | Required | Notes |
|---|---|---|
address | yes | The email address to suppress. |
Response — 201 Created
Section titled “Response — 201 Created”Returned when the address was not previously suppressed. The Location header
is set to /v1/suppressions/{address}.
Returns the suppression object with reason: "manual_add" and source: "manual".
Response — 200 OK
Section titled “Response — 200 OK”Returned when the address was already suppressed. Returns the existing suppression object unchanged.
Example
Section titled “Example”curl -X POST https://api.notavia.saas-infrastructure.com/v1/suppressions \ -H "Authorization: Bearer nsk_live_..." \ -H "Content-Type: application/json" \ -d '{ "address": "alice@example.com" }'DELETE /v1/suppressions/{address}
Section titled “DELETE /v1/suppressions/{address}”Removes an address from the suppression list. Subsequent sends to this address will no longer be blocked by the suppression gate (though preference opt-outs are evaluated separately).
Path parameters
Section titled “Path parameters”| Parameter | Notes |
|---|---|
address | The email address to remove. URL-encode the value if it contains special characters. |
Response — 204 No Content
Section titled “Response — 204 No Content”The address was removed from the suppression list.
Response — 404 Not Found
Section titled “Response — 404 Not Found”{ "error": { "type": "not_found", "code": "suppression_not_found", "message": "No suppression found for address 'alice@example.com'.", "param": null }}Example
Section titled “Example”curl -X DELETE https://api.notavia.saas-infrastructure.com/v1/suppressions/alice%40example.com \ -H "Authorization: Bearer nsk_live_..."SDK examples
Section titled “SDK examples”using NotifyService.Sdk.Suppressions;
// List with optional filtersPageResult<SuppressionResponse> page = await notify.Suppressions.ListAsync( reason: SuppressionReason.HardBounce, limit: 50);
// Look up a single addressSuppressionResponse? entry = await notify.Suppressions.GetAsync("alice@example.com");
// Manually suppressSuppressionResponse added = await notify.Suppressions.AddAsync("alice@example.com");
// Removeawait notify.Suppressions.RemoveAsync("alice@example.com");TypeScript
Section titled “TypeScript”import { createNotifyClient } from "notavia-sdk";
const notify = createNotifyClient({ apiKey: process.env.NOTIFY_API_KEY! });
// Listconst { items, nextCursor } = await notify.suppressions.list({ reason: "HardBounce", limit: 50 });
// Getconst entry = await notify.suppressions.get("alice@example.com");
// Addconst added = await notify.suppressions.add("alice@example.com");
// Removeawait notify.suppressions.remove("alice@example.com");Delivery feedback webhook events
Section titled “Delivery feedback webhook events”Notavia fires outbound webhooks to your configured endpoint when SES reports the final delivery status of a managed email send. These are standard outbound delivery webhooks signed with HMAC-SHA256 (same scheme as all other Notavia webhooks — see the Webhooks guide for signature verification).
The data field in every delivery feedback event payload is the notification
object for the send that triggered the event.
notification.delivered
Section titled “notification.delivered”Fired when SES confirms successful delivery to the recipient’s mail server.
{ "id": "evt_01j2k3m4n5p6q7r8s9t0u1v2w", "type": "notification.delivered", "created_at": "2026-05-30T14:22:10Z", "data": { ... }}notification.bounced
Section titled “notification.bounced”Fired when SES reports a permanent (hard) bounce. The recipient’s address is automatically added to the suppression list before this webhook fires.
{ "id": "evt_01j2k3m4n5p6q7r8s9t0u1v2w", "type": "notification.bounced", "created_at": "2026-05-30T14:22:10Z", "data": { ... }}notification.complained
Section titled “notification.complained”Fired when SES reports a spam complaint. The recipient’s address is automatically added to the suppression list before this webhook fires.
{ "id": "evt_01j2k3m4n5p6q7r8s9t0u1v2w", "type": "notification.complained", "created_at": "2026-05-30T14:22:10Z", "data": { ... }}All four suppression endpoints require a full-access API key. The send_only
scope is not sufficient.
Deliver feedback webhook events are sent from Notavia to your endpoint
and do not require API-key auth on your side — verify them using the
NotifyService-Signature header instead.