Docs
⌘K
Platform operations / Webhooks & events

Webhooks & events

From settlement onward your platform is out of the transaction path. Webhooks are how you learn what happened without polling for it — settlements, dispute activity, verification decisions, and agent status changes, delivered to endpoints you control.

Push for freshness, pull for truth

Webhooks tell you something changed. The reporting endpoints tell you the current state. Most platforms subscribe for responsiveness and reconcile against reporting on a schedule — a webhook you never received is invisible, but a record you can pull is not.

Endpoints

An endpoint is the unit of configuration: one URL, one environment, its own event selection, its own signing secret, its own delivery history. You can register several — routing settlements to one service and disputes to another is a normal setup, and it means a compromised secret exposes one destination rather than all of them.

FieldNotes
urlMust be HTTPS and publicly resolvable. Private, loopback, link-local and cloud metadata addresses are rejected.
environmentsandbox or production. Immutable after creation — changing it means a new endpoint.
eventsThe event types this endpoint receives. Editable at any time.
secretThe signing secret. Readable and editable at any time; see below.
activeDelivery can be paused without deleting the endpoint and losing its history.
Nustro enforces

Environment is fixed at creation because a testnet settlement arriving at a production handler is the failure this separation exists to prevent. There is no patch that moves an endpoint between environments.

The signing secret

Your endpoint is a public URL accepting unauthenticated POSTs. TLS proves your identity to Nustro; it proves nothing about Nustro’s identity to you. Anyone who learns the URL could post a plausible dispute.resolved and watch your platform act on it. The signature is what makes a payload trustworthy.

Nustro generates a secret when you create an endpoint. You may replace it with your own — useful when migrating from another provider or matching a value already in your secret manager — provided it is at least 32 characters.

The secret stays readable

Unlike your management key, the signing secret is not hashed — Nustro needs the plaintext to compute the HMAC on every delivery. It is therefore visible in the dashboard and on read, and there is no one-time reveal to miss. Editing the secret is how you rotate it; there is no separate rotation call. The change takes effect on the next delivery, so deploy the new value at the same time.

Changing an endpoint’s URL or its event selection leaves the secret untouched — your receiver keeps verifying without a redeploy.

Delivery format

Every delivery is a POST with a JSON body and three headers.

Nustro-SignatureHex HMAC-SHA256 over timestamp + "." + raw_body, keyed with the endpoint’s secret.
Nustro-TimestampUnix seconds. Part of the signed material, which is what prevents replay.
Nustro-Event-IdStable identifier for this event. Your deduplication key.
{
  "event_id": "evt_9c3f8a12d40b",
  "type": "transaction.settled",
  "created_at": "2026-05-14T09:12:44Z",
  "environment": "sandbox",
  "principal_id": "prn_7c4e21",
  "agent_did": "did:aeap:d2146ca7-fbbd-4167-b725-b5ca2ebbb6da",
  "data": {
    "transaction_id": "txn_3a8e77",
    "amount": "120.00",
    "currency": "USDC",
    "network": "base-sepolia"
  }
}

Every payload carries principal_id and agent_did, so your platform can route the event to the right principal without a lookup.

Verifying a delivery

Three checks, in this order. Skipping any of them makes the signature decorative.

Read the raw body before parsing it. The signature covers the exact bytes Nustro sent. Re-serialising a parsed object produces different bytes and a signature that never matches — this is the most common integration failure.
Recompute and compare in constant time. HMAC-SHA256(secret, timestamp + "." + raw_body), compared with a timing-safe equality function.
Reject stale timestamps. More than five minutes old means a captured request is being replayed. Without this check the signature alone does not stop replay.
import hmac, hashlib, time

def verify(raw_body, timestamp, signature, secret):
    if abs(time.time() - int(timestamp)) > 300:
        return False          # stale — possible replay
    signed = f"{timestamp}.{raw_body}".encode()
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
import crypto from 'node:crypto'

function verify(rawBody, timestamp, signature, secret) {
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
    return false          // stale — possible replay
  }
  const signed = `${timestamp}.${rawBody}`
  const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}

Retries and duplicates

BehaviourDetail
SuccessAny 2xx. Respond fast — acknowledge first, process asynchronously.
TimeoutAround 10 seconds per attempt.
RetriesExponential backoff for up to 24 hours, then the event is dropped.
Delivery guaranteeAt least once. Duplicates are expected and normal.
Sustained failureAn endpoint failing continuously is disabled and surfaced in the dashboard rather than retried indefinitely.
OrderingNot guaranteed. Use created_at if sequence matters to you.
Nustro enforces

At-least-once delivery is a contract, not an edge case. Deduplicate on Nustro-Event-Id and make your handler idempotent — a handler that credits a principal twice on a redelivered transaction.settled is a bug in the receiver, not in delivery.

Event types

EventFires whenWhy you want it
platform.verification_decidedYour KYB review is accepted or rejectedThe one event with no good polling alternative — it gates production access.
transaction.settledA transaction settles for one of your agentsLedger updates, principal-facing activity, revenue reconciliation.
dispute.filedA dispute is opened against or by one of your agentsNotify the principal while the window is still open.
dispute.resolvedA dispute reaches a decisionEscrow has moved; your records and the principal both need to know.
agent.status_changedAn agent is activated, suspended, or revokedA suspended agent cannot transact — stop routing to it.
Event names are a contract

Once you switch on these strings, renaming one breaks every receiver. Treat additions as backwards compatible and removals as breaking, and ignore event types you do not recognise rather than failing on them — new types may be added.

Migrating from v0.2: receivers switching on customer.verification_decided must update the string to platform.verification_decided — see the changelog.

Operational notes

SituationWhat to do
Rotating a secret with no downtimeThere is no dual-secret window. Either accept a brief verification gap, or add a second endpoint with the new secret, deploy, then delete the old one — both fire during the overlap, and your deduplication handles the rest.
Replacing an endpoint URLAdd the new endpoint first, confirm deliveries, then delete the old one. Deleting first loses every event fired in the gap; they are not queued.
New endpointsStart from now. Past events are not replayed on creation.
Auditing changesURL, secret, and event-selection changes are written to your activity log. A repointed endpoint is the one webhook change worth watching — signatures protect your receiver from forged senders, not your data from being sent somewhere new.

Next