Skip to main content

Webhook integration guide

Set up real-time event notifications from NAWA to your application. This guide covers endpoint creation, signature verification, and event handling for popular frameworks.

Overview

1

Create your endpoint

Build an HTTP endpoint that accepts POST requests and returns a 200 status.
2

Register the webhook

Register your endpoint URL in the NAWA dashboard and save the signing secret.
3

Verify signatures

Validate the HMAC-SHA256 signature on every incoming webhook to prevent forgery.
4

Handle events

Process events based on their type and respond within 30 seconds.

Step 1: Create your endpoint

Your webhook endpoint must:
  • Accept POST requests with application/json body
  • Return a 200 status code within 30 seconds
  • Be publicly accessible via HTTPS

Step 2: Register the webhook

  1. Go to trynawa.com/developers/keys
  2. Click Add Endpoint
  3. Enter your endpoint URL (e.g., https://yourapp.com/webhooks/nawa)
  4. Select the events you want to receive
  5. Copy the signing secret (nawa_wh_xxx) - it’s shown only once

Step 3: Verify signatures and handle events

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 verification
app.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.

Testing

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.