Webhooks
Listen for events so you can automatically trigger reactions.
Last updated
Listen for events so you can automatically trigger reactions.
Last updated
When integrating with Dfns, you might want your applications to receive events as they occur in your Dfns account, so that your backend systems can execute actions accordingly.
To start being notified about Webhook Events, you first need to register webhooks. After you register them, Dfns can push real-time event data to your application’s webhook endpoint when events happen in your Dfns account. Dfns uses a POST
http/https request to send webhook events to your app, as a JSON payload which includes a .
Receiving webhook events are particularly useful for listening to asynchronous events, such as doing a wallet transfer request.
When an event occurs in the system which a Webhook is subscribed to, we create a Webhook Event object containing data around the event that happened. We then:
Send the event to your Webhook endpoint
Capture a trace of the event so you can later check all Webhook Events sent to your webhooks (through or endpoints)
We only keep a trace of Webhook Events in our system for a retention period of 31 days. Past that, they are discarded, so you cannot see them using or endpoints.
Here's an example of a Webhook Event of kind "wallet.transfer.requested
" delivered to your webhook:
wallet.blockchainevent.detected
wallet.created
wallet.exported
wallet.delegated
wallet.signature.requested
wallet.signature.failed
wallet.signature.rejected
wallet.signature.signed
wallet.transaction.requested
wallet.transaction.failed
wallet.transaction.rejected
wallet.transaction.broadcasted
wallet.transaction.confirmed
wallet.transfer.requested
wallet.transfer.failed
wallet.transfer.rejected
wallet.transfer.broadcasted
wallet.transfer.confirmed
policy.triggered
A policy got triggered upon some activity (the policy rule got evaluated, and it triggered)
policy.approval.pending
policy.approval.resolved
Each webhook event has a "data
" property, which shape depends on its kind. Here's an overview of the shape of the data for each kind:
For wallet.blockchainevent.detected
various event kinds are available depending on the indexed chain:
NativeTransfer
All
Aip21Transfer
Aptos
AsaTransfer
Algorand
CoinTransfer, LockedCoinTransfer
Iota
Erc20Transfer, Erc721Transfer
Evm
Tep74Transfer
Ton
Trc10Transfer, Trc20Transfer, Trc721Transfer
Tron
Sep41Transfer
Stellar
SplTransfer, Spl2022Transfer
Solana
UtxoTransfer
Bitcoin, Litecoin, Dogecoin, Kaspa
For policy.triggered
Your endpoint shouldn’t expect delivery of these events in this order, and needs to handle delivery accordingly.
During an webhook event delivery attempt, if we cannot reach your webhook endpoint, or if your endpoint returns anything else than a 200 status code in the response, we consider the delivery of this event has failed.
In such a case, Dfns is going to retry delivering it to your webhook, up to 5 total attempts over 24 hours, with an exponential backoff (delays from first attempt: 1m, 12min, 2h, 1d).
The event that your webhook handler will receive (in your server), will include the attempt number in the payload (deliveryAttempt: 1
for the first attempt). Also, if it includes the field retryOf: "whe-xxxxxxx"
, it indicates that this event you are receiving, is a "retry of" a previous Webhook Event which failed delivering.
If your webhook has been disabled or deleted when Dfns attempts a retry, future retries of that event are prevented. However, if you disable and then re-enable a webhook endpoint before Dfns can retry, you can still expect to see future retry attempts.
Review these best practices to make sure your Webhooks remain secure and work properly.
The webhook event handler defined on your server, when it receives a new event from Dfns, should be quick to handle it, and return quickly a 200
status code to indicate that the event was indeed delivered. If your handler expects to do some processing taking longer than a few seconds, you should consider adding this to a queue for processing on your side. Otherwise the request delivering the event might timeout.
Also, your handler should catch any error that happens on your side, so it still respond with a 200
response to Dfns, indicating that you received it already, otherwise the same event might be retried a few times and fail all the same.
Verify webhook signatures to confirm that received events are sent from Dfns. Dfns signs webhook events it sends to your endpoints by including a signature in each event’s X-DFNS-WEBHOOK-SIGNATURE
header. This allows you to verify that the events were sent by Dfns, not by a third party.
Here is an example function showing how you can validate the webhook event signature:
Configure your webhook endpoints to receive only the types of events required by your integration. Listening for extra events (or all events) puts undue strain on the server and we don’t recommend it.
Webhook endpoints might occasionally receive the same event more than once
We don't guarantee webhook event uniqueness. You should guard against duplicate event by making sure your your event processing is idempotent.
If you use an HTTPS URL for your webhook, we validate that the connection to your server is secure before sending your webhook data. For this to work, your server must be correctly configured to support HTTPS with a valid server certificate.
To add a new webhook, you first need a http server which can receive requests from public internet.
As an example, here are steps to create a basic Express server in NodeJS:
Create a new npm project, install express
Add a new file index.js
:
In one terminal, run your server
If you use a free Ngrok account, every time you re-launch the tunnel you'll get a new url, so make sure you update the Dfns webhook url to test.
The list below shows which event kinds webhooks can subscribe to ⬇️ (see section for details on each event kind)
A wallet event has been detected on chain (eg. a deposit). Note: This is only available for .
A wallet has been .
A wallet has been .
A wallet has been .
A request has been created.
A request has failed to process.
A request with a policy approval has been rejected.
A request has completed.
A request has been created.
A request has failed to process.
A request with a policy approval has been rejected.
A request has been submitted to the mempool.
A request has been confirmed on chain. Note: This is only available for .
A request has been created.
A request has failed to process.
A request with a policy approval has been rejected.
A request has been submitted to the mempool.
A request has been confirmed on chain. Note: This is only available for .
A new process has been created and is pending.
A new process is finalized: it's either approved or rejected.
For wallet.created
, wallet.exported
, wallet.delegated
see the :
For wallet.transfer.requested
, wallet.transfer.failed
, wallet.transfer.rejected
, wallet.transfer.broadcasted
, wallet.transfer.confirmed
see the :
For wallet.transaction.requested
, wallet.transaction.failed
, wallet.transaction.rejected
, wallet.transaction.broadcasted
, wallet.transaction.confirmed
see the :
For wallet.signature.requested
, wallet.signature.failed
, wallet.signature.rejected
, wallet.signature.signed
see the :
for example, see the :
For policy.approval.pending
and policy.approval.resolved
, see the :
Dfns doesn’t guarantee delivery of events in the order in which they’re generated. For example, when a wallet is picked up on-chain by our blockchain indexers, we might generate the following events:
wallet.transfer.confirmed
- this event notifies you that the that you made has been confirmed on chain
wallet.blockchainevent.detected
- this event notifies you of the new Blockchain Event detected and added to your blockchain events
Every event delivery attempt will create a new Webhook Event, with its own unique ID, containing the same data
than the previous event which failed delivering. So in the endpoint, every Webhook Event you will see is a unique delivery attempt (potentially of the same original event).
Additionally, if you fetch Webhook Events we tried delivering, using the or , you will be able to see the deliveryFailed
boolean field indicating if delivery succeeded or not, as well as the nextAttemptDate: "2024-01-24-xxxxxx"
date showing you around which time the next delivery attempt to your webhook will occur (if delivery failed).
If you want to fetch all Webhook Events which failed delivering to your webhook, you can use the with the query parameter deliveryFailed=true
. And amongst all those returned, you can see those which failed delivering, and will not retry in future (because reached maximum retry attempt), by filtering those which have no nextAttemptDate
.
Dfns signatures is a of the received event payload, using hash function and the webhook secret as the secret. It has this shape:
If you’re using Rails, Django, or another web framework, your site might automatically check that every POST request contains a CSRF token. This is an important security feature that helps protect you and your users from attempts. However, this security measure might also prevent your site from processing legitimate events. If so, you might need to exempt the webhooks route from CSRF protection.
During development, one way to achieve this (without deploying a new server on a cloud provider), is to spin up a local server on your machine (localhost), and then use a tunnel services (eg ) to create a public url pointing to your local server.
In another terminal, start a tunnel pointing to your local server
Now, you can using the url displayed in the result of above the terminal (looking like "https://xxxxxx.ngrok-free.dev
"), and test that the webhook is setup properly by using . If properly setup, you should see incoming events received by your local server.