SettleMint
ChangelogDALP 3.0

Push signed events, stop polling

DALP now pushes signed events to your systems the moment they happen (transfers, approvals, deployments) with HMAC signatures, automatic retries, and delivery receipts so your integrations react rather than poll.

We added webhooks. DALP now pushes signed events to your registered URLs the moment a transfer settles, an approval clears, or a deployment finishes. No timer, no polling loop, no missed state change.

Webhooks

React to what happened, not to what you asked for

Register a URL, and DALP calls it the moment a subscribed event fires. Every request carries an HMAC-SHA256 signature you verify, so your system knows the event is genuine and untampered. When your server is briefly unavailable, DALP retries automatically and records the outcome of each attempt.

Signing
HMAC-SHA256
Delivery
At least once
Retries
Exponential backoff
Receipts
Counter-signed

Polling patches the absence of push. You set a timer, ask whether anything changed, handle the common case where nothing did, hope the interval is short enough to matter but not so short it becomes its own load. Webhooks remove the loop. When a token transfer settles or a compliance approval clears, DALP delivers the event to your URL in near real time. Your back-office system, reporting pipeline, or compliance integration reacts to what happened, not to when a job ran.

How delivery works

The contract is simple: register a URL and the event types you want, and we take responsibility for the rest. When a matching event fires, we sign the payload with your endpoint secret, attempt delivery, and keep retrying with exponential back-off if your server is temporarily unreachable. We record exactly what happened whether delivery succeeded or not. Your server never needs to track its own receipt history.

Delivery is durable. The retry schedule starts at roughly 30 seconds and doubles on each attempt (roughly 30s, 60s, 120s) with a 30% jitter spread to avoid thundering-herd behavior when consumers recover at the same time. We continue retrying for up to three days. After that, we abandon the event and fire a loud observability signal. Monitoring is the last line of defense for sustained outages, not a silent drop. Three days is enough time to recover, redeploy, or redirect traffic without losing events.

Delivery is at-least-once. On a brief outage followed by retry, your consumer may receive the same event more than once. Each event carries a stable evt_id field in the payload and a webhook-id header with the same value. Key your handler on evt_id to make processing idempotent. A lookup before you act costs a few microseconds; acting twice on a token transfer can cost much more.

Not all failures receive the same treatment. A 5xx or a connection timeout is transient: your server may be overloaded or mid-deploy, so we retry. A 4xx that is not a rate-limit signal (429) or a request timeout (408) is terminal: the same request would fail identically on retry, so retrying only burns the window. We record the failure class on every attempt so you can see exactly why a given call did not land.

  1. 1
    Event

    A state change you subscribed to: transfer settled, approval cleared, deployment finished. Triggers a delivery attempt.

  2. 2
    Sign

    The platform signs the request body with HMAC-SHA256 using your endpoint secret. The signature travels in the request header alongside the payload.

  3. 3
    Deliver

    The platform POSTs the signed payload to your registered endpoint. A 2xx response marks delivery successful.

  4. 4
    Retry

    On non-2xx or timeout, the platform retries with exponential back-off. You do not build a custom retry loop.

  5. 5
    Receipt

    Each attempt is logged with its status (succeeded, retried, or failed) and visible in the delivery history for the endpoint.

An event emitted, payload signed, delivered to your endpoint, retried on failure, delivery receipt written.

Why better than polling

Polling forces your integration to own the delivery loop. You schedule a job, handle empty responses, tune an interval that is either too aggressive (API saturation) or too relaxed (reacting late), then bolt on retry and deduplication logic. In a regulated context, the overhead compounds. A transfer event that arrives 90 seconds late because the interval was too conservative means 90 seconds of inconsistent back-office state.

Push inverts the responsibility. DALP is the source of truth for state changes, so it is the right place to own delivery. Your server reacts the moment an event fires, not the next time a job runs. We absorb the retry cost, maintain the history, and expose it for inspection. You write the handler; we manage the queue.

Polling on a timer
  • Run a scheduled job that asks the API whether anything changed.
  • Handle the common case where nothing changed. Empty responses on every tick.
  • Tune the interval: too short saturates the API, too long means your downstream system reacts late.
  • Build a custom retry and deduplication layer for the cases where the request itself fails.
With webhooks
  • Register an endpoint and the events you care about. Once.
  • The platform delivers the event the moment it occurs, not the next time you ask.
  • Your downstream system reacts in near real time without maintaining a polling loop.
  • Retries and delivery receipts are built in. Inspect what was sent, when, and whether it succeeded.

Verifying signatures

HMAC signing is the trust anchor for every delivery. Without it, your server cannot distinguish a genuine platform event from a spoofed request. In a regulated context, acting on an unverified transfer or compliance event is the kind of mistake that is hard to audit your way out of. The scheme is symmetric and stateless: no external PKI, no certificate chain, no token exchange. One secret per registered URL; DALP uses it to sign every call.

DALP follows the Standard Webhooks specification for headers and signing. Every request arrives with three headers: webhook-id (the stable event identifier, identical to evt_id in the payload), webhook-timestamp (Unix seconds), and webhook-signature (a v1,<base64> value). The signed string is ${webhookId}.${timestamp}.${rawBody}, keyed with the base64-decoded secret material after stripping the dalp_whsk_ prefix. Verify the signature before processing the payload. A mismatch means the call did not originate from DALP, or the body was altered in transit.

Replay protection is built into the signing contract. The webhook-timestamp header is concatenated into the signed string. Your verification logic should reject any request where that timestamp is more than five minutes old. An intercepted request cannot be replayed once the timestamp falls outside that window, even with a valid signature. For delayed retries, DALP re-signs with a fresh timestamp as the original approaches the tolerance boundary, so a legitimate retry delayed by back-off always carries a current value.

The TypeScript SDK handles signature verification for you:

import { verifyWebhook } from "@settlemint/dalp-sdk";

// In your endpoint handler, read the raw request body, the request headers,
// and your endpoint secret (Hono, Express, or any Node or Bun HTTP server).
async function handleDalpWebhook(rawBody: string, headers: Headers, secret: string) {
  const result = verifyWebhook({
    rawBody,
    headers,
    secret,
    // For secret rotation, pass an array and the SDK tries each in turn:
    // secret: [currentSecret, previousSecret],
  });

  if (!result.ok) {
    // result.code is "TIMESTAMP_SKEW" | "SECRET_MISMATCH" | "BODY_HASH_MISMATCH".
    throw new Error(`Webhook verification failed: ${result.code}`);
  }

  // result.event is the verified, typed event union. Act on event.type, and key
  // your handler on the event id to guard against at-least-once redelivery.
  return result.event;
}

If you are not using the SDK, strip the dalp_whsk_ prefix from the secret, base64-decode the remainder, and compute an HMAC-SHA256 over ${webhookId}.${timestamp}.${rawBody}. The expected webhook-signature value is v1, followed by the base64 digest.

What you can subscribe to

Not every integration needs every event. A back-office system reacting to transfer settlements has no business receiving custody signing requests. Subscriptions scope per URL for exactly that reason: each registered address gets its own filtered view of the event stream. Route transfer events to your reporting pipeline, compliance events to your operations queue, and deployment confirmations to your CI environment. Each webhook carries only what its consumer acts on.

Filtering at subscription time also reduces blast radius when your server is briefly down. A narrow subscription means fewer events queued against a recovering server, a shorter catch-up window, and a smaller history to inspect. A URL subscribed to everything accumulates full event volume while unreachable; one subscribed only to transfer settlements accumulates only those.

Webhooks fire on any platform event, not only asset-level state changes. Subscribe at the URL level so a single registered address receives exactly the types you need.

Thin references or full payloads, your choice

A registered URL can carry thin event references or full, PII-inclusive payloads. Switching to full payloads requires a deliberate, in-product GDPR acknowledgement. DALP records that someone with authority to handle personal data made that call, so the privacy-sensitive default never flips silently. Registration is validated too: a subscription referencing an unknown event name or a malformed wildcard pattern is rejected at creation time, not at delivery.

Delivery receipts, circuit breaking, and test events

DALP records every delivery attempt with its outcome, failure class, HTTP status, and exact payload sent. A 2xx marks the attempt successful. Anything else triggers classification: transient errors (5xx, timeouts) retry on the exponential curve; terminal errors (a deterministic 4xx, a counter-signed receipt mismatch) are recorded as-is. Both successful deliveries and failed attempts stay in history so you can inspect exactly what happened.

For higher-confidence acknowledgment, configure your URL to require a counter-signed receipt. In this mode, a 2xx response alone is not enough. Your consumer has a configurable window (30 seconds by default, up to 60) to POST a receipt back to DALP containing a hash of the event payload, signed with your secret. If the receipt does not arrive in time, DALP marks the attempt RECEIPT_TIMEOUT and retries. If the receipt arrives but the signature or hash does not match, the attempt is marked terminal: that signals a configuration or key mismatch that retrying cannot fix.

DALP tracks consecutive failures per registered URL. After 30 consecutive failures, the circuit breaker opens. New events are held rather than dispatched against a URL that has repeatedly failed. Once open, the platform sends probe deliveries. Two successive probe successes bring the URL back to active. Test events are excluded from failure accounting so validation runs do not affect a live URL's circuit state.

Before routing live traffic to a new URL, send a test event from the Console, or call POST /api/v2/webhooks/{id}/test-events, to confirm your handler receives and verifies signatures correctly. Test events appear in delivery history with a test flag and do not affect the circuit breaker.

Register webhook endpoints →

Availability

Webhooks shipped as Generally Available in DALP 3.0. Existing polling patterns continue to work. Webhooks are an additive delivery channel, not a replacement for the query API.

Signature verification, automatic retries with exponential back-off, delivery receipts, and test-event dispatch are all included with no additional tier requirement.

Webhooks cover one half of a tighter integration loop. Pair them with the Ledger Index for the full picture: webhooks tell your systems what just happened in near real time; the Platform API lets them ask what happened at any point in the past. Together they remove both the polling loop and the separate indexing stack that most regulated integrations carry alongside the platform.

On this page