Platform

Webhooks API

8 endpoints under https://api.mailneo.co/api/v2. Every request carries your key in the X-API-Key header. The scopes used here: webhooks:read, webhooks:write. Each endpoint below states the one it requires. Start with the guide →

Webhooks
listWebhookEndpoints

List webhook endpoints

Cursor-paginated. The signing secret is never included — it is returned once, in the response to the POST that created the endpoint, and cannot be read back afterwards. Endpoints created from the newsletter interface are not listed here.

https://api.mailneo.co/api/v2
GET/webhookswebhooks:read

Example request

curl "https://api.mailneo.co/api/v2/webhooks?limit=25&order=desc&sort=updated_at" \
  -H "X-API-Key: $MAILNEO_API_KEY"
Authorization
Required scope
webhooks:read. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Query parameters7
NameTypeRequiredNotes
limitintegerNodefault 25 · 1–100
cursorstringNomax 256 chars
order"asc" | "desc"Nodefault "desc"
updated_sincestring · date-timeNoISO 8601
sort"updated_at" | "created_at"Nodefault "updated_at"
active"true" | "false"No
eventstringNomax 64 chars
Response · 200

Success

data[]Each element of the array.

FieldTypeAlways presentNotes
object"webhook_endpoint"Yes
idstringYes
namestringYes
urlstringYes
eventsstring[]Yes
activebooleanYes
descriptionstring · nullableYes
signature_versionstringYes
api_versionstringYes
header_namesstring[]Yes
consecutive_failuresintegerYes
last_success_atstring · nullableYes
last_failure_atstring · nullableYes
disabled_atstring · nullableYes
disabled_reasonstring · nullableYes
created_atstringYes
updated_atstringYes

meta

FieldTypeAlways presentNotes
request_idstringYes
has_morebooleanYes
next_cursorstring · nullableYes
synced_throughstringNo
Webhookswrite
createWebhookEndpoint

Register a webhook endpoint

The response contains secret, and it is the only time it is ever returned. Store it before you do anything else: it is encrypted at rest and no endpoint on this API will return it again. Use it to verify the X-Mailneo-Signature header on every delivery.

The URL is validated at registration and must be publicly resolvable — an address inside a private range, a loopback address or a name that resolves to one is refused here rather than failing silently at delivery time. Subscribing to events that carry contact data (contact.*, event.tracked, suppression.added, unsubscribe.created) additionally requires contacts:read or suppressions:read on the key: a webhook delivers that data to an address you choose, so it may not grant a reach the key does not already have. Event names are validated against the catalog at GET /v2/webhooks/events; ["*"] subscribes to everything live today and is stored as that explicit list, so a later addition to the catalog does not silently change what you receive.

https://api.mailneo.co/api/v2
POST/webhookswebhooks:write

Example request

curl -X POST "https://api.mailneo.co/api/v2/webhooks" \
  -H "X-API-Key: $MAILNEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "<string>",
    "events": [
      "<string>"
    ]
  }'
Authorization
Required scope
webhooks:write. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Idempotency
Idempotency-Key is optional.
Query parameters1
NameTypeRequiredNotes
dry_runbooleanNoReport what this call WOULD do and commit nothing. Returns data: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.
Header parameters1
NameTypeRequiredNotes
Idempotency-KeystringNoA caller-chosen unique string, at most 255 characters. Retrying with the same key replays the first response instead of repeating the work; the replay carries Idempotent-Replayed: true. Reusing a key with a different body is a 409 idempotency_key_reused. Keys are remembered for 24 hours. · max 255 chars
Request body

Body

FieldTypeAlways presentNotes
urlstringYesmax 2048 chars
eventsstring[]Yes
namestringNomax 200 chars
descriptionstring · nullableNomax 1000 chars
headersobjectNoopen keys
Response · 200

Success

dataMay be null.

FieldTypeAlways presentNotes
object"webhook_endpoint"Yes
idstringYes
namestringYes
urlstringYes
eventsstring[]Yes
activebooleanYes
descriptionstring · nullableYes
signature_versionstringYes
api_versionstringYes
header_namesstring[]Yes
consecutive_failuresintegerYes
last_success_atstring · nullableYes
last_failure_atstring · nullableYes
disabled_atstring · nullableYes
disabled_reasonstring · nullableYes
created_atstringYes
updated_atstringYes
secretstringYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes
Response · 201

Created

dataMay be null.

FieldTypeAlways presentNotes
object"webhook_endpoint"Yes
idstringYes
namestringYes
urlstringYes
eventsstring[]Yes
activebooleanYes
descriptionstring · nullableYes
signature_versionstringYes
api_versionstringYes
header_namesstring[]Yes
consecutive_failuresintegerYes
last_success_atstring · nullableYes
last_failure_atstring · nullableYes
disabled_atstring · nullableYes
disabled_reasonstring · nullableYes
created_atstringYes
updated_atstringYes
secretstringYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes
Webhooks
listWebhookEventTypes

List subscribable event types

Every event this API can deliver, with the name to put in an endpoint's events array. Discover the list rather than hard-coding it: an event name outside this catalog is rejected at registration, and the catalog grows. The response is complete — has_more is always false and there is no cursor, because the catalog is a small fixed set.

https://api.mailneo.co/api/v2
GET/webhooks/eventswebhooks:read

Example request

curl "https://api.mailneo.co/api/v2/webhooks/events" \
  -H "X-API-Key: $MAILNEO_API_KEY"
Authorization
Required scope
webhooks:read. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Response · 200

Success

data[]Each element of the array.

FieldTypeAlways presentNotes
object"webhook_event_type"Yes
typestringYes
descriptionstringYes
object_typestringYes

meta

FieldTypeAlways presentNotes
request_idstringYes
has_morebooleanYes
next_cursorstring · nullableYes
Webhooks
getWebhookEndpoint

Retrieve a webhook endpoint

The signing secret is not included and cannot be retrieved. If you have lost it, delete this endpoint and register a new one.

https://api.mailneo.co/api/v2
GET/webhooks/{webhookId}webhooks:read

Example request

curl "https://api.mailneo.co/api/v2/webhooks/{webhookId}" \
  -H "X-API-Key: $MAILNEO_API_KEY"
Authorization
Required scope
webhooks:read. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Path parameters1
NameTypeRequiredNotes
webhookIdstringYesmax 64 chars
Response · 200

Success

data

FieldTypeAlways presentNotes
object"webhook_endpoint"Yes
idstringYes
namestringYes
urlstringYes
eventsstring[]Yes
activebooleanYes
descriptionstring · nullableYes
signature_versionstringYes
api_versionstringYes
header_namesstring[]Yes
consecutive_failuresintegerYes
last_success_atstring · nullableYes
last_failure_atstring · nullableYes
disabled_atstring · nullableYes
disabled_reasonstring · nullableYes
created_atstringYes
updated_atstringYes

meta

FieldTypeAlways presentNotes
request_idstringYes
Webhookswrite
updateWebhookEndpoint

Update a webhook endpoint

Merge-patch: omitted fields are left alone. events and headers are the exceptions — each REPLACES its stored value wholesale, because a subscription list you can only add to is one you can never leave. Send headers: {} to remove every custom header. A subscription to events that carry contact data — and repointing the url of an endpoint already subscribed to them — additionally requires contacts:read or suppressions:read, because a webhook delivers that data to an address you choose. A changed url is re-validated exactly as it was at registration. Setting active: true on an endpoint the platform switched off also clears its failure counter, so it is not disabled again by the next single failure. The signing secret is neither writable nor returned.

https://api.mailneo.co/api/v2
PATCH/webhooks/{webhookId}webhooks:write

Example request

curl -X PATCH "https://api.mailneo.co/api/v2/webhooks/{webhookId}" \
  -H "X-API-Key: $MAILNEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "<string>",
    "events": [
      "<string>"
    ],
    "name": "<string>",
    "description": "<string>",
    "headers": {},
    "active": true
  }'
Authorization
Required scope
webhooks:write. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Idempotency
Idempotency-Key is optional.
Path parameters1
NameTypeRequiredNotes
webhookIdstringYesmax 64 chars
Query parameters1
NameTypeRequiredNotes
dry_runbooleanNoReport what this call WOULD do and commit nothing. Returns data: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.
Header parameters1
NameTypeRequiredNotes
Idempotency-KeystringNoA caller-chosen unique string, at most 255 characters. Retrying with the same key replays the first response instead of repeating the work; the replay carries Idempotent-Replayed: true. Reusing a key with a different body is a 409 idempotency_key_reused. Keys are remembered for 24 hours. · max 255 chars
Request body

Body

FieldTypeAlways presentNotes
urlstringNomax 2048 chars
eventsstring[]No
namestringNomax 200 chars
descriptionstring · nullableNomax 1000 chars
headersobjectNoopen keys
activebooleanNo
Response · 200

Success

data

FieldTypeAlways presentNotes
dataobject | object · nullableYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes
Webhookswrite
deleteWebhookEndpoint

Delete a webhook endpoint

Permanent. Deliveries stop immediately and the endpoint's delivery log is removed with it. Re-registering gives you a NEW signing secret, so a consumer verifying signatures must be updated. Call with ?dry_run=true first if you are not certain. To stop deliveries reversibly, PATCH with active: false instead.

https://api.mailneo.co/api/v2
DELETE/webhooks/{webhookId}webhooks:write

Example request

curl -X DELETE "https://api.mailneo.co/api/v2/webhooks/{webhookId}" \
  -H "X-API-Key: $MAILNEO_API_KEY"
Authorization
Required scope
webhooks:write. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Path parameters1
NameTypeRequiredNotes
webhookIdstringYesmax 64 chars
Query parameters1
NameTypeRequiredNotes
dry_runbooleanNoReport what this call WOULD do and commit nothing. Returns data: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.
Response · 200

Success

dataMay be null.

FieldTypeAlways presentNotes
object"webhook_endpoint"Yes
idstringYes
deletedtrueYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes
Webhooks
listWebhookDeliveries

List an endpoint's deliveries

The delivery log for one endpoint, newest first, cursor-paginated. There is no updated_since: the log has no index supporting an incremental poll, and advertising a watermark that cannot be honoured would make a sync client silently miss rows forever. The event payload is not included — fetch the object from its own endpoint using event_id for correlation.

https://api.mailneo.co/api/v2
GET/webhooks/{webhookId}/deliverieswebhooks:read

Example request

curl "https://api.mailneo.co/api/v2/webhooks/{webhookId}/deliveries?limit=25&order=desc" \
  -H "X-API-Key: $MAILNEO_API_KEY"
Authorization
Required scope
webhooks:read. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Path parameters1
NameTypeRequiredNotes
webhookIdstringYesmax 64 chars
Query parameters4
NameTypeRequiredNotes
limitintegerNodefault 25 · 1–100
cursorstringNomax 256 chars
order"asc" | "desc"Nodefault "desc"
status"PENDING" | "SUCCEEDED" | "FAILED" | "RETRYING" | "BLOCKED"No
Response · 200

Success

data[]Each element of the array.

FieldTypeAlways presentNotes
object"webhook_delivery"Yes
idstringYes
webhook_idstringYes
event_idstringYes
event_typestringYes
attemptintegerYes
statusstringYes
response_statusinteger · nullableYes
response_bodystring · nullableYes
error_messagestring · nullableYes
duration_msinteger · nullableYes
is_replaybooleanYes
replay_of_idstring · nullableYes
next_attempt_atstring · nullableYes
created_atstringYes
updated_atstringYes

meta

FieldTypeAlways presentNotes
request_idstringYes
has_morebooleanYes
next_cursorstring · nullableYes
Webhookswrite
replayWebhookDelivery

Replay a delivery

Queues the original payload for re-delivery as a NEW delivery row, so the log shows both attempts. The X-Mailneo-Event-Id header is unchanged, so a consumer that deduplicates on it — the documented contract — correctly treats the replay as the event it already saw. The timestamp and signature are freshly computed: re-sending the original signature would either fall outside the five-minute tolerance or prove that a captured signature stays valid forever. Idempotency-Key is required, because a retried replay that already ran must not deliver twice.

https://api.mailneo.co/api/v2
POST/webhooks/{webhookId}/deliveries/{deliveryId}/replaywebhooks:write

Example request

curl -X POST "https://api.mailneo.co/api/v2/webhooks/{webhookId}/deliveries/{deliveryId}/replay" \
  -H "X-API-Key: $MAILNEO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
Authorization
Required scope
webhooks:write. A key without it is refused before the endpoint runs.
Credential
X-API-Key: mk_live_...Authorization: Bearer mk_live_...
Idempotency
Idempotency-Key is required.
Path parameters2
NameTypeRequiredNotes
webhookIdstringYesmax 64 chars
deliveryIdstringYesmax 64 chars
Query parameters1
NameTypeRequiredNotes
dry_runbooleanNoReport what this call WOULD do and commit nothing. Returns data: null and a meta.impact block. Requires the resource's :read scope in addition to the scope this endpoint declares. Must be exactly true or false.
Header parameters1
NameTypeRequiredNotes
Idempotency-KeystringYesA caller-chosen unique string, at most 255 characters. Retrying with the same key replays the first response instead of repeating the work; the replay carries Idempotent-Replayed: true. Reusing a key with a different body is a 409 idempotency_key_reused. Keys are remembered for 24 hours. · max 255 chars
Response · 200

Success

data

FieldTypeAlways presentNotes
dataobject | object · nullableYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes
Response · 201

Created

data

FieldTypeAlways presentNotes
dataobject | object · nullableYes

meta

FieldTypeAlways presentNotes
request_idstringYes
dry_runbooleanYes
impactobjectNo
impact.object"impact"Yes
impact.would_createintegerYes
impact.would_updateintegerYes
impact.would_deleteintegerYes
impact.affected_countintegerYes
impact.affected_idsstring[]Yes
impact.quotaobject · nullableYes
impact.quota.kindstringYes
impact.quota.would_consumeintegerYes
impact.quota.limitinteger · nullableYes
impact.quota.currentintegerYes

Next Steps