Set up real-time event notifications from NAWA to your application. This guide covers endpoint creation, signature verification, and event handling for popular frameworks.
import crypto from 'crypto'import express from 'express'const app = express()const WEBHOOK_SECRET = process.env.NAWA_WEBHOOK_SECRET!// IMPORTANT: Use raw body for signature verificationapp.post( '/webhooks/nawa', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-nawa-signature'] as string const timestamp = req.headers['x-nawa-timestamp'] as string // 1. Check timestamp freshness (prevent replay attacks) const age = Math.abs(Date.now() / 1000 - parseInt(timestamp)) if (age > 300) { return res.status(400).json({ error: 'Timestamp expired' }) } // 2. Verify HMAC-SHA256 signature const payload = `${timestamp}.${req.body}` const expected = crypto .createHmac('sha256', WEBHOOK_SECRET) .update(payload) .digest('hex') if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) { return res.status(400).json({ error: 'Invalid signature' }) } // 3. Parse and handle the event const event = JSON.parse(req.body.toString()) switch (event.type) { case 'classification.completed': handleClassification(event.data) break case 'comment.new': handleNewComment(event.data) break case 'credits.low': alertLowCredits(event.data) break case 'credits.exhausted': alertCreditsExhausted(event.data) break } res.status(200).json({ received: true }) })
Always use the raw request body for signature verification. If your framework parses JSON before you can access the raw body, the signature won’t match.
A dedicated webhook test-fire endpoint is planned. In the meantime, test your handler by sending a POST request directly to your endpoint with a sample payload.
Use a tool like ngrok during development to expose your local endpoint to the internet for webhook testing.