Webhooks & Events
Receive real-time HTTP callbacks whenever something happens in your flows. No polling required.
Event Types
Subscribe to any combination of these events when registering your webhook endpoint.
session.startedA new user session has been initiated on any of your flows
session.completedA user has reached a terminal node and the session has ended
session.abandonedA session timed out or the user disconnected without completing
user.responseA user submitted input at any step in the flow
flow.publishedA flow version was published and is now live
payment.completedA payment request within a session was confirmed
payment.failedA payment request was declined or timed out
error.occurredAn unhandled error occurred during flow execution
Setting Up Webhooks
Register your endpoint URL in the FlowApp Studio dashboard under Settings → Webhooks. Select the events you want to receive.
- 1
Go to Settings → Webhooks in your dashboard
- 2
Click Add Endpoint and enter your HTTPS URL
- 3
Select which events to subscribe to
- 4
Copy your webhook signing secret
Payload Structure
All events share a common envelope with event-specific data in the payload field.
{
"id": "evt_01J9XK...",
"type": "session.completed",
"created_at": "2024-01-15T10:32:00Z",
"account_id": "acc_xyz...",
"payload": {
"session_id": "ses_abc...",
"flow_id": "flow_01J9XK...",
"user_id": "user_123",
"channel": "ussd",
"duration_ms": 12400,
"terminal_node": "end",
"metadata": {}
}
}Signature Verification
Every payload is signed with HMAC-SHA256 using your webhook secret. Always verify before processing.
import crypto from 'crypto'
function verifyWebhook(rawBody: Buffer, signature: string, secret: string): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
// Use timingSafeEqual to prevent timing attacks
const sigBuf = Buffer.from(signature.replace('sha256=', ''), 'hex')
const expBuf = Buffer.from(expected, 'hex')
return sigBuf.length === expBuf.length && crypto.timingSafeEqual(sigBuf, expBuf)
}
// Usage in Express
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-flowapp-signature'] as string
const secret = process.env.FLOWAPP_WEBHOOK_SECRET!
if (!verifyWebhook(req.body, sig, secret)) {
return res.status(401).json({ error: 'Invalid signature' })
}
const event = JSON.parse(req.body.toString())
// process event...
res.json({ received: true })
})Retry Policy
If your endpoint returns a non-2xx response or times out, we retry up to 3 times with exponential backoff: 5 seconds, 30 seconds, then 5 minutes. After 3 failures the event is marked as failed and visible in your dashboard.