Webhooks turn your licensing system from passive to proactive. Instead of customers polling your API to check license status, you push events to their systems in real time. License activated? Webhook. Payment failed? Webhook. License about to expire? Webhook.
Which Events to Send
Not every internal state change needs a webhook. Focus on events that trigger actions in your customer's systems:
| Event | When | Customer Action |
|---|---|---|
license.created | New license provisioned | Configure application, enable features |
license.activated | License activated on a domain | Log activation, update internal records |
license.expiring | 7 days before expiration | Show renewal prompt to end user |
license.expired | License expires | Disable premium features |
license.renewed | Payment processed, license extended | Re-enable features |
license.revoked | License manually or auto-revoked | Disable all features immediately |
license.upgraded | Plan tier changed | Enable new features, adjust limits |
domain.added | New domain authorized | Update domain whitelist |
domain.removed | Domain de-authorized | Revoke access for that domain |
payment.failed | Subscription payment fails | Show payment update prompt |
Webhook Payload Structure
Use a consistent payload structure across all events. Include enough context for the customer to process the event without making additional API calls.
{
"id": "evt_a1b2c3d4e5f6",
"type": "license.activated",
"created": "2026-03-15T14:30:00Z",
"data": {
"licenseKey": "TO-****-****-****-F6A8",
"plan": "professional",
"domain": "app.customer.com",
"activationsUsed": 3,
"activationLimit": 10,
"expiresAt": "2027-03-15T00:00:00Z",
"features": ["analytics", "api-access", "custom-domains"]
}
}
Delivery: Reliability Matters
At-Least-Once Delivery
Webhooks can fail — the customer's server might be down, overloaded, or misconfigured. Implement retry logic to ensure events are delivered:
- Attempt 1 — Immediate
- Attempt 2 — 1 minute later
- Attempt 3 — 5 minutes later
- Attempt 4 — 30 minutes later
- Attempt 5 — 2 hours later
- Attempt 6 — 24 hours later (final)
After all retries fail, disable the webhook endpoint and notify the customer via email.
Idempotency
Because you retry, customers may receive the same event multiple times. Include a unique event ID so customers can deduplicate:
// Customer's webhook handler — idempotent processing
app.post('/webhooks/traffic-orchestrator', async (req, res) => {
const eventId = req.body.id
// Check if already processed
if (await db.webhookEvents.findOne({ eventId })) {
return res.status(200).json({ received: true }) // Already processed
}
// Process the event
await handleLicenseEvent(req.body)
// Mark as processed
await db.webhookEvents.insert({ eventId, processedAt: new Date() })
res.status(200).json({ received: true })
})
Security: Don't Trust, Verify
HMAC Signatures
Sign every webhook payload with a shared secret using HMAC-SHA256. The customer verifies the signature to ensure the event came from your system, not an attacker.
// Your system: sign the payload
const crypto = require('crypto')
const signature = crypto
.createHmac('sha256', webhookSecret)
.update(JSON.stringify(payload))
.digest('hex')
// Send with signature header
headers['X-Signature-SHA256'] = signature
// Customer's system: verify the signature
const expectedSignature = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(rawBody)
.digest('hex')
if (signature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid signature' })
}
Timestamp Verification
Include a timestamp in the signed payload and reject events older than 5 minutes. This prevents replay attacks where an attacker captures a valid webhook and replays it later.
IP Allowlisting
Publish the IP addresses your webhooks originate from. Customers can allowlist these IPs in their firewalls for defense in depth.
Customer Dashboard
Give customers a webhook management interface:
- Endpoint configuration — Set the URL, select which events to receive
- Secret management — View and rotate the signing secret
- Event log — See recent deliveries with status, response code, and payload
- Test button — Send a test event to verify their endpoint works
- Retry control — Manually retry failed deliveries
Performance Considerations
- Async delivery — Never block your main API on webhook delivery. Use a queue.
- Timeout — Set a 5-second timeout for customer endpoints. If they don't respond, mark it as failed and retry.
- Circuit breaker — If a customer's endpoint fails 10 times in a row, disable it and notify them.
- Rate limiting — Don't overwhelm customers with events. Batch rapid-fire events (e.g., bulk imports).
Getting Started
Traffic Orchestrator includes webhook support out of the box. Configure your endpoint in the portal, select your events, and start receiving real-time license updates. HMAC signing, retry logic, and event logging are all built in.
Ship licensing in your next release
5 licenses, 500 validations/month, full API access. Set up in under 5 minutes — no credit card required.