Skip to Content

Webhooks

Webhooks deliver near-real-time notifications for events on your profile. Your server registers an HTTPS endpoint and a set of event types; The Vault delivers signed JSON payloads to it as POST requests.

Setup

Register webhooks in the dashboard (Settings → Webhooks): the URL, the event types to subscribe to, and an optional description. You can register multiple webhooks per profile — for example to split high-volume events (BALANCE_UPDATED, TRANSACTION_DETECTED) from low-volume administrative ones.

The API provides read access for monitoring:

GET /v1/profiles/{profile_id}/webhooks GET /v1/profiles/{profile_id}/webhooks/{webhook_id} GET /v1/profiles/{profile_id}/webhooks/{webhook_id}/notifications GET /v1/profiles/{profile_id}/webhooks/{webhook_id}/notifications/{notification_id}

The notifications endpoints list delivery attempts with their status (PENDING, DELIVERED, FAILED) — use them to debug missed deliveries.

Receiving events

Each delivery is a POST with these headers:

HeaderDescription
Content-TypeAlways application/json
X-Webhook-SignatureDetached JWS (ES256) over timestamp + payload — see Verification
X-Webhook-TimestampUnix timestamp (seconds) used during signing
X-Webhook-Event-TypeEvent type, e.g. TRANSFER_CREATED
X-Webhook-Event-IDUnique per-event UUID — use as your deduplication key

Body structure:

{ "event_id": "3b80e869-f64b-404f-9759-3a3b2efc7f03", "event_type": "TRANSFER_CREATED", "profile_id": "550e8400-e29b-41d4-a716-446655440000", "timestamp": "2026-07-20T12:00:00Z", "data": { } }

data is event-type-specific. Respond 2xx within 30 seconds; anything else counts as a failed delivery. Acknowledge as soon as you’ve verified the signature and persisted the event — process asynchronously.

Retries

Delivery is at-least-once — deduplicate on X-Webhook-Event-ID. Failed deliveries are retried with backoff, up to 5 attempts total:

AttemptDelay after previous
1immediate
230 s
32 min
410 min
530 min

After the 5th failure the delivery is marked FAILED and not retried automatically (it remains inspectable via the notifications endpoints).

Verifying signatures

Every delivery is signed with ECDSA P-256 / SHA-256 (ES256) as a detached JWS (RFC 7515 §A.5). The signature covers "<X-Webhook-Timestamp>.<raw body>", binding payload and timestamp together.

Public keys are published as a JWKS with automatic rotation:

GET https://<your-vault-domain>/.well-known/jwks.json

Verification steps:

  1. Read the raw request body bytes — do not parse and re-serialize first.
  2. Reject if |now − X-Webhook-Timestamp| exceeds your tolerance (≤ 5 minutes recommended).
  3. Split X-Webhook-Signature on .. into <headerB64> and <signatureB64>; the decoded header carries alg (ES256) and kid.
  4. Look up the key by kid in your cached JWKS (respect Cache-Control; on unknown kid, refresh the cache and retry — old and new keys overlap during rotation).
  5. Rebuild the signing input headerB64 + "." + base64url(timestamp + "." + rawBody) and verify with any JOSE library (jose for Node, jwcrypto for Python, lestrrat-go/jwx for Go). Signature bytes are raw R‖S (64 bytes), unpadded base64url — standard JWS ES256.

Reject any request whose signature does not verify, and treat unknown event types gracefully (log and ignore) — new event types are added over time and must not break your integration.

Event types

CategoryEvents
Transfer lifecycleTRANSFER_CREATED, TRANSFER_UPDATED, TRANSFER_DELIVERED, TRANSFER_FAILED
Balances & transactionsBALANCE_UPDATED, TRANSACTION_DETECTED
WalletsWALLET_CREATED, WALLET_UPDATED, WALLET_DELETED
PoliciesPOLICY_CREATED, POLICY_UPDATED, POLICY_DELETED
AddressesADDRESS_CREATED, ADDRESS_UPDATED, ADDRESS_DELETED
Wallet groupsWALLET_GROUP_CREATED, WALLET_GROUP_UPDATED, WALLET_GROUP_DELETED, WALLET_GROUP_USER_ASSIGNED
Address groupsADDRESS_GROUP_CREATED, ADDRESS_GROUP_UPDATED, ADDRESS_GROUP_DELETED
Servers & keysSERVER_CREATED, SERVER_DELETED, REFRESH_KEY_SHARES
DevicesDEVICE_CREATED, DEVICE_STATUS_CHANGED, DEVICE_REMOVED, CERTIFICATE_READY
UsersUSER_CREATED, USER_ROLE_UPDATED, USER_BLOCKED, USER_UNBLOCKED, USER_PASSWORD_RESET_INITIATED
SecurityPOLICY_SIGNATURE_VERIFICATION_FAILED, ROLE_SCOPE_LOGIN, GEO_ANOMALY_DETECTED

TRANSACTION_DETECTED fires when an on-chain transaction is first seen on one of your addresses (possibly unconfirmed); BALANCE_UPDATED fires after confirmation when a balance actually changes.

Integration checklist

  • Expose a globally reachable HTTPS endpoint; read the request body raw.
  • Verify X-Webhook-Signature on every request; enforce the timestamp window.
  • Deduplicate on X-Webhook-Event-ID with an appropriate TTL.
  • Respond 2xx fast; do business logic asynchronously.
  • Monitor your non-2xx rate — repeated failures exhaust the retry schedule.