Guides
Part 5: Real-time Updates with Blockfrost#
Introduction#
In this section, we'll enhance our transaction list by implementing real-time updates using Blockfrost webhooks. This will provide a much better user experience by showing immediate updates when transactions are confirmed on the blockchain. However note that in order to implement this in a production application, you will need to implement proper rollback protection and handle lost transactions.
Unlock Enterprise-Grade Blockchain Data Services with Anvil
While this tutorial uses Blockfrost webhooks for basic transaction monitoring, enterprise applications often require more sophisticated chain indexing solutions tailored to specific business needs.
Contact Anvil today (hello@ada-anvil.io) to discuss your specific chain indexing requirements and elevate your blockchain application to enterprise standards.
Important Security Notice: This tutorial demonstrates basic transaction monitoring with Blockfrost webhooks but does not implement proper rollback protection required for production applications.
In Cardano's Ouroboros protocol, transaction finality is probabilistic:
- Absolute finality is mathematically guaranteed after the security parameter k blocks.
- For practical applications, 6 confirmations provide reasonable security for most use cases
- Exchanges and financial applications typically wait for more confirmations (30+ blocks)
A production-ready implementation should:
- Track transaction inclusion block height
- Monitor for potential chain reorganizations (rollbacks)
- Implement confirmation counting with configurable thresholds
- Only consider high-value transactions completely final after sufficient depth
Webhook Overview#
Webhooks provide a mechanism for receiving real-time notifications about blockchain events. Instead of constantly polling for transaction confirmation updates, our application will receive instant notifications when transactions are confirmed (Based on the confirmation threshold set in the webhook configuration) on the Cardano blockchain.
Our hybrid approach uses:
- Initial polling: For pending transactions when they're first created
- Webhooks: For confirmation notifications when the blockchain confirms transactions
Key advantages of this hybrid approach:
- Real-time Updates: Instant notifications when transactions are confirmed, without waiting for the next poll cycle
- Reduced API Usage: Significantly fewer polling requests compared to a pure polling solution
- Better User Experience: More immediate UI updates for confirmed transactions
- Server Resource Efficiency: Lower overall load on both client and server
Implementation Steps#
- Install ngrok (a tunneling service) and start your development server at
http://localhost:3000 - Set up Blockfrost webhook and have them send notifications to your ngrok URL with the endpoint
/api/webhooks/blockfrost - Implement Next.js route handler for webhook endpoint
src/app/api/webhooks/blockfrost/route.ts - Handle webhook events in the endpoint
1. Install and Configure ngrok#
Since Blockfrost webhooks require a public URL to send notifications to, first we'll use ngrok to expose your local development server.
Installing ngrok#
Sign up for a free ngrok account at dashboard.ngrok.com
Install ngrok using npm:
npm install -g ngrok- Configure ngrok with your auth token (found in your ngrok dashboard):

Ngrok Auth Token
ngrok config add-authtoken YOUR_AUTH_TOKEN- Start your Next.js development server if it's not already running:
npm run dev- In a separate terminal, start ngrok to expose your local server:
ngrok http 3000Windows Troubleshooting: If you encounter 'ngrok is not recognized' errors on Windows, either add ngrok to your PATH or use this alternative command instead:
npx ngrok http 3000This is a common issue on Windows systems where global npm installations sometimes aren't properly added to the system.
After Starting ngrok#
Once ngrok is running, you'll see a terminal display with connection information
Look for the 'Forwarding' URL that looks like
https://xxxx-xxx-xxx-xxx-xxx.ngrok-free.appCopy this URL - you'll need it for configuring your Blockfrost webhook
Keep this terminal window open as long as you need the tunnel active
Update Blockfrost Webhook URL when restarting ngrok: The free tier of ngrok generates a new URL each time you restart it. For development purposes, you'll need to update your Blockfrost webhook URL whenever you restart ngrok.
2. Set Up Blockfrost Webhook#
- Create a Blockfrost account at blockfrost.io
- Navigate to the Webhooks section in your Blockfrost dashboard
- Click the 'Create webhook' button to create a new webhook
- Configure your webhook with these settings:

Blockfrost Webhook Configuration Interface
- Webhook name: Give it a descriptive name (e.g., 'my-app-escrow-lock-trigger-ngrok')
- Endpoint URL: Enter your ngrok URL followed by your webhook endpoint path (e.g., 'https://xxxx-xxx-xxx-xxx-xxx.ngrok-free.app/api/webhook/blockfrost')
- Network: Select 'Cardano preprod' for testnet development
- Status: Ensure it's enabled
- Trigger: Select 'Transaction'
- Required confirmations: Set to your desired confirmation threshold (typically 1-3 for testing)
- Trigger conditions:
- Set condition type to 'recipient'
- Set operator to 'equal to'
- Enter your escrow script address (this can be found in the script deployment data or derived from the validator hash using the Anvil API)
Deriving Script Address: You can derive the script address from your validator hash using the Anvil API endpoint /validators/{hash}/address. For more details, see the Smart Contract Utilities.
Important: Save the 'Auth token' provided by Blockfrost, and update your local
.env.localfile with yourBLOCKFROST_WEBHOOK_SECRET.
3. Create the Webhook Endpoint#
Let's create the webhook endpoint src/app/api/webhooks/blockfrost/route.ts that will receive notifications from Blockfrost:
// src/app/api/webhooks/blockfrost/route.ts
import { NextResponse } from "next/server";
import { upsertWallet, upsertTx } from "@/lib/db";
import { TX_STATUS } from "@/lib/types";
export async function POST(request: Request) {
try {
const body = await request.json();
for (const event of body.payload) {
const wallet = event.inputs?.[0]?.address;
const txHash = event.tx.hash;
const amount = event.outputs?.[0]?.amount.find(
(a: { unit: string; quantity: string }) => a.unit === "lovelace"
)?.quantity;
if (wallet && amount) {
upsertWallet(wallet);
upsertTx(txHash, wallet, Number(amount), TX_STATUS.CONFIRMED);
}
}
} catch (err) {
console.error("Failed to handle webhook:", err);
}
return NextResponse.json({ ok: true });
}5. Update Your BlockfrostWebhook URL#
Now that your webhook endpoint is set up and ngrok is running:
- Return to the Blockfrost dashboard
- Update your webhook with the new ngrok URL:
https://[your-ngrok-url]/api/webhooks/blockfrost - Make sure you press 'Save Webhook' to save the changes
6. Webhook Payload Structure#
Blockfrost will send notifications with a payload structure like this:
{
"id": "webhook-id",
"webhook_id": "your-webhook-id",
"created": 1625558414,
"api_version": 0,
"type": "transaction",
"payload": [
{
"tx": {
"hash": "transaction_hash_here"
},
"inputs": [
{
"address": "wallet_address_here"
}
],
"outputs": [
{
"amount": [
{
"unit": "lovelace",
"quantity": "amount_in_lovelace"
}
]
}
]
}
]
}7. Smart Polling Implementation#
Let's implement a smart polling mechanism in src/hooks/useTransactions.ts that only activates when there are pending transactions. This approach conserves resources while still providing timely updates:
Add the React useEffect hook and the usePollingTransactions hook to the useTransactions hook:
// src/hooks/useTransactions.ts
// Import React useEffect hook
import { useEffect, useState } from "react";
/**
* Hook to manage transaction data with polling that automatically
* activates only when pending transactions are detected
* @param wallet - Wallet address to fetch transactions for
* @returns Transaction data with loading and error states
*/
export function usePollingTransactions(wallet?: string) {
// Track if we have any pending transactions that need polling
const [hasPendingTx, setHasPendingTx] = useState(false);
const query = useQuery<Transaction[], Error>({
queryKey: ['transactions', wallet],
queryFn: async () => {
if (!wallet) throw new Error("Wallet is required");
const response = await fetch(`/api/escrow/transactions?wallet=${wallet}`);
return response.json();
},
enabled: !!wallet,
// Only activate polling when wallet is connected AND we have pending transactions
refetchInterval: wallet && hasPendingTx ? 5000 : false,
});
// Monitor transaction data for pending status changes
useEffect(() => {
if (query.data) {
// Check if any transactions have PENDING status
const isPending = query.data.some(tx => tx.status === TX_STATUS.PENDING);
// Only update state if the pending status changed
// This prevents unnecessary re-renders
if (isPending !== hasPendingTx) {
setHasPendingTx(isPending);
}
}
}, [query.data, hasPendingTx]);
return query;
}7. Update MyTransactions Component#
Finally, we need to update our MyTransactions component src/components/MyTransactions.tsx to use the new polling hook instead of the regular transactions hook:
// src/components/MyTransactions.tsx
"use client";
// Update this import to use the new polling hook instead
import { usePollingTransactions } from '@/hooks/useTransactions';
import { useWallet } from '@ada-anvil/weld/react';
import { Transaction, TransactionStatus, TX_STATUS } from '@/lib/types';
// Rest of component code remains the same...
export default function MyTransactions() {
const wallet = useWallet();
const address = wallet.changeAddressBech32;
// Replace useTransactionsByWallet with usePollingTransactions
const { data: transactions = [], error, isLoading } = usePollingTransactions(address);
// Rest of component remains the same...
}This change enables smart polling that automatically activates when there are pending transactions, and disables when all transactions are confirmed, providing real-time updates while conserving resources.
8. Test the Webhook Integration#
Let's test the complete flow with webhooks:
- Ensure your Next.js server is running
- Verify ngrok is properly exposing your local server
- Check that the Blockfrost webhook is configured and enabled
- Connect your wallet in the application
- Lock some funds using the Lock Funds form
- Observe how the transaction initially shows as "Pending"
- Wait for Blockfrost to detect the transaction confirmation and send a webhook
- Verify that the transaction status updates to "Confirmed" immediately after the webhook is received
Understanding the Webhook Flow#
The complete flow with webhooks works as follows:
- Transaction Creation: User locks funds, creating a transaction
- Initial Status: Transaction is stored with "PENDING" status
- Initial Polling: UI polls frequently at first to provide responsive feedback
- Blockchain Confirmation: Transaction is confirmed on the Cardano blockchain
- Webhook Notification: Blockfrost detects the confirmation and sends a webhook
- Status Update: Our webhook handler updates the transaction status to "CONFIRMED"
- UI Update: The next poll or React Query invalidation refreshes the UI
This hybrid approach combines the best of both worlds:
- Immediate feedback through initial polling
- Reliable long-term updates through webhooks
- Fallback to extended polling for resilience
Monitoring Webhook Activity#
To help troubleshoot and verify webhook activity, let's add some simple logging:
// src/app/api/webhooks/blockfrost/route.ts
// Add this to the existing file
// Add at the top of the POST function
console.log(`Received webhook at ${new Date().toISOString()}`);
// Add inside the try block after processing
console.log(`Processed ${body.payload.length} webhook events`);
// Add inside the catch block
console.error("Webhook error details:", JSON.stringify(err));Webhook Security Considerations#
In a production environment, you would want to add security to your webhook endpoint:
- Webhook Secrets: Use a shared secret with Blockfrost to verify webhook authenticity
- IP Filtering: Restrict access to known Blockfrost IP addresses
- Rate Limiting: Protect against DoS attacks by implementing rate limiting
- Request Validation: Validate the webhook payload structure before processing
For our tutorial, we've kept it simple, but consider these enhancements for production use.
Benefits of Real-time Updates#
With the webhook implementation, users will experience:
- Immediate Feedback: Transaction status changes are reflected immediately
- Reduced Network Traffic: No need for constant polling requests
- Better Battery Life: Mobile devices benefit from fewer background requests
- More Responsive UI: Status changes happen in near real-time
Troubleshooting#
Webhook Not Receiving Events#
If your webhook isn't receiving events:
- Check ngrok status - ensure the tunnel is active
- Verify your webhook URL in the Blockfrost dashboard
- Check Blockfrost webhook logs for delivery attempts
- Ensure your application is properly handling POST requests
Inconsistent Status Updates#
If transaction statuses are not updating correctly:
- Enable detailed logging in your webhook handler
- Verify the transaction hash matching in your database operations
- Check that the database is successfully updated when webhooks are received
During development, Blockfrost may take a few minutes to start sending webhooks for newly configured endpoints. Be patient during initial testing.
Best Practices for Webhook Implementation#
- Idempotency: Ensure your webhook handler can safely process the same event multiple times
- Async Processing: For high-volume applications, consider processing webhooks asynchronously
- Monitoring: Implement logging and monitoring to track webhook health
- Fallback Strategy: Always maintain a fallback mechanism (like polling) for resilience
Congratulations! You've completed Part 5 of the guide. Your application now receives real-time updates when transactions are confirmed on the blockchain, providing a much better user experience.