Skip to main content
Receive real-time notifications when events occur in your NAWA account. Webhooks use HMAC-SHA256 signatures for security.

Event types

EventDescriptionTrigger
classification.completedA classification request succeededAfter /v1/classify completes
classification.failedA classification request failedOn provider or internal errors
comment.newA new comment was ingestedWhen a connected platform receives a comment
comment.repliedA reply was postedAfter /v1/comments/:id/reply succeeds
credits.lowCredit balance below thresholdBalance drops below $5
credits.exhaustedCredit balance is $0Balance reaches $0

Webhook payload

{
  "id": "evt_abc123",
  "type": "classification.completed",
  "created_at": "2025-01-15T12:00:00Z",
  "data": {
    "request_id": "req_def456",
    "text": "متى الجزء الثاني؟",
    "intent": "question",
    "sentiment": "neutral",
    "dialect": "gulf"
  }
}

Webhook signing secret

Your webhook signing secret starts with nawa_wh_ and is generated when you register a webhook endpoint. Store it securely - it’s shown only once.

Signature verification

Every webhook request includes an X-NAWA-Signature header containing the HMAC-SHA256 signature of the request body.
import crypto from 'crypto'
import express from 'express'

const app = express()

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
  const body = req.body

  // Verify timestamp is within 5 minutes
  const now = Math.floor(Date.now() / 1000)
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return res.status(400).send('Timestamp too old')
  }

  // Compute expected signature
  const payload = `${timestamp}.${body}`
  const expected = crypto
    .createHmac('sha256', process.env.NAWA_WEBHOOK_SECRET!)
    .update(payload)
    .digest('hex')

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(400).send('Invalid signature')
  }

  const event = JSON.parse(body)
  console.log('Received event:', event.type)

  // Handle the event
  switch (event.type) {
    case 'classification.completed':
      // Process classification result
      break
    case 'credits.low':
      // Alert team about low balance
      break
  }

  res.status(200).send('OK')
})
Always verify webhook signatures before processing events. Never trust the payload without signature validation.

Retry policy

If your endpoint returns a non-2xx status code, NAWA retries with exponential backoff:
AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry8 hours
Continues…Up to 3 days
After 10 consecutive failures, the webhook endpoint is automatically disabled and you’ll receive an email notification. Re-enable it from the dashboard after fixing the issue.

Testing webhooks

A dedicated webhook test-fire endpoint is planned. In the meantime, you can test your webhook handler by sending a POST request directly to your own endpoint with a sample payload matching the format above.