SettleMint
Reference

Webhook delivery receipts API

Read and submit counter-signed webhook delivery receipts through the DALP Platform API for non-repudiation proof that a consumer received and verified each event.

A delivery receipt is a signed record that a webhook consumer received a specific event and acknowledged it. When an integration needs an audit trail that proves delivery, not just that the platform sent an event but that the receiver got it and confirmed it, the consumer counter-signs each delivery and the platform stores the result. An auditor or operator then reads those receipts as the consumer-side half of the delivery chain of custody.

This surface has two roles. The consumer submits a receipt by counter-signing a delivery it received. The tenant operator reads the resulting receipts to confirm which deliveries were acknowledged and verified. Receipts are opt-in per endpoint: enable counter-signed receipts on the webhook endpoint before consumers can submit them.

Endpoints

EndpointUse it for
GET /api/v2/webhook-receiptsList the receipts recorded in the active tenant scope, newest first.
GET /api/v2/webhook-receipts/{id}Read one receipt by its identifier.
POST /api/v2/webhook-receiptsSubmit a consumer counter-signed receipt for a delivery the consumer received.

The list endpoint uses the collection envelope with data, meta, and pagination links. The read and submit endpoints use the single-resource envelope with data and links.self. The two read calls run in the active organisation and system context, as described in Organization and system scope. The submit call is the consumer's own and authenticates differently, as described under Submit a receipt. To configure delivery and the per-endpoint receipts setting, see Webhook endpoints.

Route handler semantics

Each endpoint is served by a dedicated route handler with a specific scope and authentication method.

RouteHandler scopeAuthenticationSemantics
GET /api/v2/webhook-receiptsTenant-scopedAPI keyReads receipts from the active tenant, ordered newest first by receivedAt. Supports filtering, sorting, and global search across the receipt collection.
GET /api/v2/webhook-receipts/{id}Tenant-scopedAPI keyReads a single receipt by primary key, constrained to the active tenant. Returns DALP-0529 when the identifier does not exist in the current scope.
POST /api/v2/webhook-receiptsPublic (no tenant scope)HMAC signature over the deliveryThe consumer proves it holds the endpoint signing secret by counter-signing the delivered event. The handler verifies the signature against the active and previous secrets so rotation in flight does not reject valid receipts, then recomputes the canonical hash of the delivered payload to confirm the consumer signed the exact bytes that were dispatched.

The list and read handlers are tenant-scoped: they enforce the organisation and system context from the API key session, as described in Organization and system scope. The submit handler is public: it does not use an API key. Instead, the HMAC signature over the (deliveryId, endpointId, evtId) capability triple is the authentication credential. The platform recognises the consumer because it signed the delivery with the endpoint's signing secret. The handler authorises against the delivery row's snapshot of counterSignedReceipts (not the live endpoint flag) so toggling the endpoint cannot retroactively change whether old deliveries accept receipts.

Receipt fields

Each receipt describes one consumer acknowledgement of one delivery.

FieldTypeDescription
idstringUnique identifier for the receipt.
deliveryIdstringThe delivery this receipt acknowledges. One receipt exists per delivery.
evtIdstringThe event identifier carried by the delivery.
endpointIdstringThe webhook endpoint that received the delivery.
consumerSignaturestringThe consumer's HMAC signature submitted with the receipt.
innerEventHashstringThe hash of the delivered event body that the consumer signed.
receivedAtstringWhen the platform recorded the consumer's submission.
verifiedAtstring or nullWhen the submission verified. Set on a successful receipt, null when the receipt records a verification failure.
verificationFailureClassstring or nullThe failure reason when verification did not pass: RECEIPT_INVALID_SIG or RECEIPT_HASH_MISMATCH. null when verified.

A receipt with verifiedAt set and verificationFailureClass of null is a confirmed acknowledgement. A receipt that instead carries a verificationFailureClass records a submission that reached the platform but did not verify, which is itself an audit signal.

List receipts

GET /api/v2/webhook-receipts returns the receipts in the active tenant scope, newest first by receivedAt.

The list supports pagination, sorting, filtering, and global search. Filter by evtId, endpointId, receivedAt, verifiedAt, or verificationFailureClass. Global search with filter[q] matches across the searchable fields. The default sort is newest first by receivedAt.

curl --globoff "https://your-platform.example.com/api/v2/webhook-receipts?filter[endpointId]=whe_01HXYZ" \
  -H "x-api-key: YOUR_API_KEY"
{
  "data": [
    {
      "id": "whr_01HXYZ",
      "deliveryId": "whd_01HABC",
      "evtId": "evt_123abc",
      "endpointId": "whe_01HXYZ",
      "consumerSignature": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "innerEventHash": "a3bf4f1b2b0b822cd15d6c15b0f00a08884c7d659a2feaa0c55ad0159f86d081",
      "receivedAt": "2024-01-01T00:00:01Z",
      "verifiedAt": "2024-01-01T00:00:01Z",
      "verificationFailureClass": null
    }
  ],
  "meta": {
    "total": 1,
    "facets": {}
  },
  "links": {
    "self": "/v2/webhook-receipts?page[offset]=0&page[limit]=50",
    "first": "/v2/webhook-receipts?page[offset]=0&page[limit]=50",
    "prev": null,
    "next": null,
    "last": "/v2/webhook-receipts?page[offset]=0&page[limit]=50"
  }
}

The list returns 50 receipts per page by default, up to 200. Use page[offset] and page[limit] to page through longer histories. Filter on verificationFailureClass to isolate the submissions that did not verify, which surfaces consumers that received a delivery but signed it with the wrong secret or against a changed payload.

Read one receipt

GET /api/v2/webhook-receipts/{id} returns a single receipt by its identifier, scoped to the active tenant.

curl "https://your-platform.example.com/api/v2/webhook-receipts/whr_01HXYZ" \
  -H "x-api-key: YOUR_API_KEY"

A receipt identifier that does not resolve in the current tenant scope returns DALP-0529 with status 404. The response does not reveal whether the receipt exists in another tenant, so confirm the receipt identifier and the active organisation before retrying.

Submit a receipt

POST /api/v2/webhook-receipts is the consumer's call. After the consumer receives a delivery, it counter-signs the delivery and submits the receipt to confirm acknowledgement.

The submission carries no API key. The HMAC signature over the delivered event authenticates the request: the platform recognises the consumer because it signed the delivery with the endpoint's signing secret. Identify the delivery by the combination of deliveryId, endpointId, and evtId, then prove possession of the secret and the delivered bytes through consumerSignature and innerEventHash.

FieldTypeDescription
deliveryIdstringThe delivery being acknowledged.
endpointIdstringThe endpoint that received the delivery.
evtIdstringThe event identifier carried by the delivery.
consumerSignaturestringThe consumer's HMAC-SHA256 of innerEventHash under the endpoint signing secret, as a hex string. An optional sha256= prefix is accepted.
innerEventHashstringThe hash of the delivered event body, used to prove the consumer signed the bytes it received.

The platform verifies the submission in two steps. First it checks consumerSignature against the endpoint's signing secret, accepting both the active secret and the previous one during a rotation overlap so a rotation in flight does not reject a valid receipt. Then it recomputes the canonical hash of the delivered event body and compares it to innerEventHash, which proves the consumer signed the exact bytes that were delivered rather than only proving it holds the secret.

curl "https://your-platform.example.com/api/v2/webhook-receipts" \
  -H "Content-Type: application/json" \
  -d '{
    "deliveryId": "whd_01HABC",
    "endpointId": "whe_01HXYZ",
    "evtId": "evt_123abc",
    "consumerSignature": "sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
    "innerEventHash": "a3bf4f1b2b0b822cd15d6c15b0f00a08884c7d659a2feaa0c55ad0159f86d081"
  }'
{
  "data": {
    "id": "whr_01HXYZ",
    "deliveryId": "whd_01HABC",
    "evtId": "evt_123abc",
    "endpointId": "whe_01HXYZ",
    "consumerSignature": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
    "innerEventHash": "a3bf4f1b2b0b822cd15d6c15b0f00a08884c7d659a2feaa0c55ad0159f86d081",
    "receivedAt": "2024-01-01T00:00:01Z",
    "verifiedAt": "2024-01-01T00:00:01Z",
    "verificationFailureClass": null
  },
  "links": {
    "self": "/v2/webhook-receipts/whr_01HXYZ"
  }
}

Submission is idempotent

A consumer can retry the same submission safely. The platform records one receipt per delivery, so an at-least-once retry of an identical receipt converges to the same record rather than creating duplicates. A successful submission that follows an earlier hash-mismatch attempt for the same delivery clears the mismatch and confirms the delivery.

Submission outcomes

ResultWhat it means
DALP-0527 (404)No delivery matches the deliveryId, endpointId, and evtId combination, or counter-signed receipts were not enabled on the endpoint when that delivery was dispatched. Confirm the three identifiers and that the endpoint has receipts enabled.
DALP-0510 (401)The signature did not verify, the payload hash did not match the delivered body, or the receipt window for the delivery has closed. Verify the signing secret and that the signed body matches the delivered bytes, then acknowledge the next retry attempt if the window has closed.

The receipt window matters for late submissions. When a delivery's deadline passes before the consumer acknowledges it, the platform treats that delivery as timed out and schedules a retry. Submitting a receipt after the window has closed returns DALP-0510; acknowledge the next retry attempt instead.

Error registry

All errors that the webhook-receipts v2 routes can return are listed below. The list and read endpoints are tenant-scoped and can return the standard scope and not-found errors. The submit endpoint is public and returns authentication and delivery-specific errors.

CodeStatusEndpointWhat it meansHow to resolve
DALP-0529404ReadNo receipt with that identifier exists in the active tenant scope.Verify the receipt identifier belongs to the authenticated tenant. Use the list endpoint to retrieve valid identifiers.
DALP-0527404SubmitNo delivery matches the submitted deliveryId, endpointId, and evtId combination, or counter-signed receipts were not enabled on the endpoint when that delivery was dispatched.Confirm the three identifiers are correct and that the endpoint has receipts enabled.
DALP-0510401SubmitThe signature did not verify, the payload hash did not match the delivered body, or the receipt window for the delivery has closed.Verify the signing secret and that the signed body matches the delivered bytes, then acknowledge the next retry attempt if the window has closed.

The read endpoint returns DALP-0529 when the receipt identifier does not resolve in the current tenant scope. The response does not reveal whether the receipt exists in another tenant, so confirm the receipt identifier and the active organisation before retrying.

The submit endpoint returns DALP-0527 for an unknown delivery or one that was not dispatched with counter-signed receipts enabled. It returns DALP-0510 when the consumer signature fails verification, the inner event hash does not match the delivered payload, or the delivery's deadline has already passed and the platform has scheduled a retry. In the timeout case, the consumer should wait for the next retry delivery and acknowledge that instead.

When to use it

Use this surface when you need to:

  • Prove that a consumer received and acknowledged a specific event delivery, not just that the platform attempted to send it.
  • Build the consumer-side half of a webhook delivery chain of custody for an audit.
  • Isolate deliveries that reached a consumer but failed verification, through the verificationFailureClass filter.
  • Let an external receiver confirm deliveries without holding a platform API key, using its endpoint signing secret as proof.

To configure delivery, signing, retries, and the per-endpoint receipts setting, see Webhook endpoints.

On this page