Skip to content

Webhook Best Practices

This guide covers essential patterns for implementing reliable, secure, and scalable webhook handlers.

1. Respond Quickly

Your endpoint must respond within 20 seconds. Aim for under 1 second by acknowledging immediately and processing asynchronously.

javascript
app.post("/webhooks/trst", async (req, res) => {
	try {
		verifySignature(req, process.env.TRST_WEBHOOK_SECRET)
		const event = JSON.parse(req.body.toString())

		// Acknowledge IMMEDIATELY
		res.status(200).send("OK")

		// Process asynchronously (don't await!)
		processWebhookEvent(event).catch((error) => {
			logger.error("Webhook processing failed", {
				error: error.message,
				eventId: event.event_id,
			})
		})
	} catch (error) {
		logger.error("Webhook verification failed", { error: error.message })
		res.status(401).send("Unauthorized")
	}
})

async function processWebhookEvent(event) {
	// Now you have time to do slow operations
	await updateDatabase(event.data)
	await sendNotifications(event.data)
	await callExternalAPI(event.data)
}

For high-volume webhooks, use a message queue (Redis, RabbitMQ, etc.) to decouple receipt from processing.

2. Handle Duplicates (Idempotency)

You may receive the same event multiple times. Always check event_id before processing.

3. Handle Unknown Event Types

Your endpoint may receive new event types we add in the future. Always handle unknown events gracefully.

javascript
function handleEvent(event) {
	switch (event.event_type) {
		case "enrollment.completed":
			return handleEnrollment(event.data)
		case "demo.scan.success":
			return handleScan(event.data)
		case "webhook.test":
			logger.info("Test webhook received")
			return Promise.resolve()
		default:
			// Don't crash - log and ignore
			logger.warn("Unknown event type (ignoring)", {
				eventType: event.event_type,
				eventId: event.event_id,
			})
			return Promise.resolve()
	}
}

4. Implement Error Handling

Catch errors to prevent crashes and avoid exposing internal details.

javascript
app.post("/webhooks/trst", async (req, res) => {
	try {
		verifySignature(req, process.env.TRST_WEBHOOK_SECRET)
		const event = JSON.parse(req.body.toString())

		logger.info("Webhook received", {
			eventId: event.event_id,
			eventType: event.event_type,
		})

		res.status(200).send("OK")

		processWebhookEvent(event).catch((error) => {
			logger.error("Processing failed", {
				eventId: event.event_id,
				error: error.message,
			})
		})
	} catch (error) {
		logger.error("Verification failed", { error: error.message })
		res.status(401).send("Unauthorized")
	}
})

5. Secure Your Endpoint

Always Verify Signatures

See the Security Guide for complete verification implementation.

IP Allowlisting (Coming Soon)

IP Allowlist Not Yet Available

TRST has not yet published webhook source IP ranges. IP allowlisting will be available in a future update. For now, rely on HMAC signature verification for security.

Contact support if you need IP ranges for your security requirements.

6. Monitor Your Webhooks

Track key metrics to detect issues early:

  • Success rate - Percentage of successful webhook processing
  • Error rate - Failed processing attempts
  • Processing time - How long events take to process
  • Pending deliveries - Backlog of undelivered webhooks
javascript
// Basic monitoring example
async function checkWebhookHealth(webhookId) {
	const deliveries = await getRecentDeliveries(webhookId, { limit: 10 })

	const failures = deliveries.filter((d) => d.status === "failed")

	if (failures.length > 3) {
		await sendAlert({
			message: "High webhook failure rate",
			failures: failures.length,
		})
	}
}

7. Test Before Production

Always test webhooks in a staging environment:

  • [ ] Use real HTTPS endpoint (not localhost)
  • [ ] Verify signature verification works
  • [ ] Test duplicate event handling
  • [ ] Test all event types
  • [ ] Monitor logs for errors

Use the test endpoint to verify your integration:

bash
curl -X POST https://api.prod.trstinc.ca/v1/project/{project_id}/webhooks/{webhook_id}/test \
  -H "Authorization: Bearer YOUR_API_KEY"

8. Handle Downtime

Plan for webhook endpoint downtime.

Production Checklist

Before going live:

Security

  • [ ] Signature verification implemented
  • [ ] Secrets stored securely (environment variables or secrets manager)
  • [ ] HTTPS endpoint with valid certificate
  • [ ] Rate limiting configured
  • [ ] Error messages don't expose internal details

Reliability

  • [ ] Responds within 1-2 seconds
  • [ ] Async processing (queue or background jobs)
  • [ ] Duplicate detection using event_id
  • [ ] Handles unknown event types gracefully
  • [ ] Comprehensive error handling and logging

Monitoring

  • [ ] Log all webhook events
  • [ ] Track success/error rates
  • [ ] Alert on high error rates
  • [ ] Monitor pending deliveries

Testing

  • [ ] Tested in staging environment
  • [ ] Test webhook endpoint verified
  • [ ] All event types handled
  • [ ] Failure scenarios tested

Quick Tips

DO:

  • ✅ Respond with 200 OK immediately
  • ✅ Process events asynchronously
  • ✅ Check event_id for duplicates
  • ✅ Verify HMAC signature on every request
  • ✅ Log all webhook events
  • ✅ Handle unknown event types gracefully

DON'T:

  • ❌ Process events synchronously before responding
  • ❌ Skip duplicate checking
  • ❌ Skip signature verification
  • ❌ Crash on unknown event types
  • ❌ Expose internal errors in responses
  • ❌ Use HTTP (must use HTTPS)

Next Steps