Set Up Webhooks
Configure webhooks to receive real-time notifications when objects change state.
What You'll Build
Webhooks let your server receive real-time HTTP callbacks when events occur in Dual, like object transfers, property changes, or new mints. In this tutorial you'll register a webhook endpoint, configure event filters, and handle incoming payloads.
Step 1, Create Your Endpoint
Set up an HTTPS endpoint on your server that accepts POST requests. Every webhook handler should verify the HMAC-SHA256 signature before processing, this ensures the request genuinely came from Dual.
const express = require('express');const crypto = require('crypto');const app = express();app.use(express.json());const WEBHOOK_SECRET = process.env.DUAL_WEBHOOK_SECRET;function verifySignature(body, timestamp, signature) {const message = \`\${timestamp}.\${body}\`;const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(message).digest('hex');return crypto.timingSafeEqual(Buffer.from(signature),Buffer.from(expected));}app.post('/webhooks/dual', (req, res) => {const sig = req.headers['x-dual-signature'];const ts = req.headers['x-dual-timestamp'];if (!sig || !ts || !verifySignature(JSON.stringify(req.body), ts, sig)) {return res.status(401).json({ error: 'Invalid signature' });}const event = req.body;console.log('Event:', event.event_type);console.log('Object:', event.object_id);console.log('Data:', event.payload);res.status(200).json({ received: true });});app.listen(3000, () => console.log('Webhook server on :3000'));
Security: Your webhook secret is in your org settings under Developer → Webhook Secret. Never hardcode it, use environment variables.
Step 2, Register the Webhook
Tell Dual to send events to your endpoint:
curl -X POST https://api-testnet.dual.network/webhooks \\-H "Authorization: Bearer $DUAL_TOKEN" \\-H "Content-Type: application/json" \\-d '{"webhook": {"url": "https://your-server.com/webhooks/dual","events": ["object.transferred", "object.updated"],"active": true}}'
Step 3, Test with a Transfer
Transfer an object to trigger a webhook delivery:
curl -X POST https://api-testnet.dual.network/ebus/actions \\-H "Authorization: Bearer $DUAL_TOKEN" \\-H "Content-Type: application/json" \\-d '{"action_name": "Transfer","object_id": "your-object-id","new_owner": "recipient-wallet"}'
Within seconds your endpoint should receive a POST with a payload like:
{"event_type": "object.transferred","object_id": "abc-123","timestamp": "2026-03-13T10:30:00Z","payload": {"previous_owner": "wallet-a","new_owner": "wallet-b"}}
Step 4, Manage Your Webhooks
List all registered webhooks:
curl https://api-testnet.dual.network/webhooks \\-H "Authorization: Bearer $DUAL_TOKEN"
Delete a webhook by ID:
curl -X DELETE https://api-testnet.dual.network/webhooks/{webhookId} \\-H "Authorization: Bearer $DUAL_TOKEN"
Retry Policy: Dual retries failed webhook deliveries with exponential backoff (1s, 5s, 30s, 5m). After 5 consecutive failures, the webhook is automatically deactivated. Re-enable it via PATCH /webhooks/{id}.
What's Next?
Ready to add monetization? Head to Integrate Payments.