Webhooks
Verify and parse the sms.delivered/sms.failed events. The signature is the HMAC-SHA256 hex digest of the raw body in the X-SendAfrica-Signature header — verify before parsing JSON.
import crypto from 'node:crypto'
import { NextResponse } from 'next/server'
export async function POST(request: Request) {
const rawBody = await request.text()
const expected = crypto
.createHmac('sha256', process.env.SENDAFRICA_WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex')
const actual = request.headers.get('x-sendafrica-signature') ?? ''
if (
actual.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(actual))
) {
return NextResponse.json({ error: 'invalid signature' }, { status: 401 })
}
const event = JSON.parse(rawBody)
console.log(event.event, event.message_id, event.to, event.status)
return NextResponse.json({ ok: true }) // durable work before this return
}Correct response semantics
- Return
2xxonly after the event is durably processed — persist or queue before the200. - Ack fast, process heavy work asynchronously.
- Deduplicate on
(message_id, event)— retries can deliver duplicates.
See Webhooks for the full contract, retry backoff, and endpoint-management API.
Last updated on