Skip to main content

Using Webhooks

Add a new Secure Webhook

To create a new Secure Webhook go to Blockfrost Dashboard and click on Add webhook button. In the webhook settings, fill in the webhook name, select the trigger event your app needs, and create the necessary Trigger conditions. Do not worry about not getting everything right, you can update the settings anytime.

The webhook endpoint URL should be set to a publicly accessible HTTPS URL, where you will process the events sent via webhook request. If you do not have your backend exposed to the web, follow the steps in Test your webhook to test with a web server running on your local machine.

Rollbacks and a required number of confirmations

Events are sent to your webhook endpoint after reaching the number of confirmations you specified in Secure Webhook Settings.

During a rollback, multiple blocks may be invalidated and replaced simultaneously. This can result in a flurry of events being dispatched to your webhook endpoint in an unpredictable order. Developers should account for this behavior by implementing logic to handle out-of-order delivery of events.

Example

For instance, a notification about a block at a higher height might arrive before one for a lower height due to the asynchronous nature of these requests.

Due to a rollback, you may receive the same event multiple times.

Example

Let's say you set up a Secure Webhook for a transaction event with the number of required confirmations set to 0. The transaction you are interested in gets included in a block so you will be notified about the transaction. Then the block is rolled back and the same transaction is included in a new block. You will receive another transaction event with the same transaction hash, but the hash of the block in which the transaction was included will differ from the first event.

We recommend verifying that the event you received has not been rolled back or increasing the number of required confirmations before the event is sent to your endpoint.

Process a webhook request

Create an HTTP endpoint on your backend where you will process incoming events and trigger actions you want to execute. The endpoint must accept POST requests with data in JSON format and return a successful status code (2xx).

Each request body is an object with several fields. The event type is stored in type field. The event itself is included in payload field. For the complete structure of a webhook request, see Events overview.

Don't forget to verify that the event originated from Blockfrost, not a third party just pretending to be Blockfrost. See Check signatures.

Here is a quick example of a web application that implements /webhook endpoint and leverages the Blockfrost SDK to verify the webhook signature:

Missing an example for your favorite language?

Earn a bounty for contributing examples in your favorite language. Everyone can participate and earn rewards for contributions. Join the Blockfrost Bounty Program now!

// Example of Node.js Express app with /webhook endpoint
// for processing events sent by Blockfrost Secure Webhooks
const express = require("express");
const blockfrost = require("@blockfrost/blockfrost-js");
const { verifyWebhookSignature, SignatureVerificationError } = blockfrost;

// You will find the webhook secret auth token in the Webhook settings in the Blockfrost Dashboard
const SECRET_AUTH_TOKEN = "WEBHOOK_AUTH_TOKEN";

const app = express();

// Define endpoint /webhook that accepts POST requests
app.post(
"/webhook",
express.json({ type: "application/json" }),
(request, response) => {
const signatureHeader = request.headers["blockfrost-signature"];

// Make sure that Blockfrost-Signature header exists
if (!signatureHeader) {
console.log("The request is missing Blockfrost-Signature header");
return response.status(400).send(`Missing signature header`);
}

// Validate the webhook signature
try {
const event = verifyWebhookSignature(
JSON.stringify(request.body), // stringified request.body
signatureHeader,
SECRET_AUTH_TOKEN,
600 // optional param to customize maximum allowed age of the webhook event, defaults to 600s
);

// Signature is valid
const type = request.body.type;
const payload = request.body.payload;

// Process the incoming event
switch (type) {
case "transaction":
// process Transaction event
console.log(`Received ${payload.length} transactions`);
// loop through the payload (payload is an array of Transaction events)
for (const transaction of payload) {
console.log(`Transaction ${transaction.tx.hash}`);
}
break;

case "block":
// process Block event
console.log(`Received block hash ${payload.hash}`);
break;

case "delegation":
// process Delegation event
console.log(`Received ${payload.length} delegations`);
// loop through the payload, payload is an array of objects with fields: "tx" (an object) and "delegations" (an array)
for (const transaction of payload) {
for (const delegation of transaction.delegations) {
console.log(
`Delegation from address ${delegation.address} included in tx ${transaction.tx.hash}`
);
}
}
break;

case "epoch":
// process Epoch event
console.log(
`Epoch switch from ${payload.previous_epoch.epoch} to ${payload.current_epoch.epoch}`
);
break;

default:
console.warn(`Unexpected event type ${type}`);
break;
}

// Return status code 2xx
response.json({ processed: true });
} catch (error) {
// In case of invalid signature verifyWebhookSignature will throw SignatureVerificationError
// for easier debugging you can access passed signatureHeader and webhookPayload values (error.detail.signatureHeader, error.detail.webhookPayload)
console.error(error);
return response.status(400).send("Signature is not valid!");
}
}
);

app.listen(6666, () => console.log("Running on port 6666"));

Retries

Blockfrost will attempt to resend the webhook request up to two additional times (making a total of three attempts, including the initial try) if your endpoint returns any of the following HTTP error status codes:

  • 400 Bad Request
  • 408 Request Timeout
  • 413 Payload Too Large
  • 429 Too Many Requests
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout
  • 521 Web Server Is Down
  • 522 Connection Timed Out
  • 524 A Timeout Occurred

Blockfrost employs a backoff strategy, waiting a few seconds before attempting to resend the request. Body of the request is the same between retries, the ID of the retried request will be the same as the ID of the original request that failed.

tip

To help you with troubleshooting problems with your endpoint, check the list of recent failed requests in Secure Webhook settings in Blockfrost Dashboard. However, it's worth noting that if any of the retry attempts succeed, the operation is considered successful. Request is logged as failed only if all attempts to deliver the event fail.

If your webhook endpoint doesn't respond with a successful status code (2xx) within few seconds then the request will time out.

caution

To prevent unexpected issues, your application should be prepared for a scenario in which the webhook request never arrives. To keep your app fully functioning and still provide a good user experience, you can manually fetch the data via the traditional Blockfrost API.

Test your webhook

Webhook.site

The easiest way to explore the data sent via a webhook is to point it to a service such as Webhook.site. It generates a random URL and displays every request sent to this address and its body.

Local web server

In order to test Secure Webhooks with your endpoint implementation running on your local HTTP dev server, you need to expose it to the web. Fortunately, services like Loophole.cloud will set it up for you.

Follow Loophole documentation to download the CLI or the Desktop GUI app, create an account and expose your local HTTP server.

Example

To expose a local web server running on port 6666 via CLI, run the command loophole http 6666. In the Webhook settings, set the webhook endpoint to the URL you will see in the output (if you are using the example do not forget to append /webhook at the end).


$ loophole http 6666
Loophole - End to end TLS encrypted TCP communication between you and your clients
Registering your domain... Success!
Starting local proxy server... Success!
Initializing secure tunnel... Success!

Forwarding https://1a4ab363d5cfd01cccc9fba73777770c.loophole.site -> http://127.0.0.1:6666