Real-Time Billing Events for Your Application
Stop polling for subscription changes. Webhooks notify your application the moment something happens, from subscription creation to usage recording.
Your customer just upgraded their plan, but your application still shows them the old features. Maybe your cron job will catch it in an hour. Maybe tomorrow. Polling for subscription changes works until it doesn't, and when it fails, customers notice. Webhooks flip the model: instead of asking "has anything changed?" every few minutes, your application receives a notification the moment a change occurs. Subscription created, payment failed, usage recorded: your system knows immediately. The customer upgrades at 2:47 PM and their new features are live by 2:47 PM.
The polling approach seemed reasonable when you built it. Every fifteen minutes, your background job queries the billing API for subscriptions modified since the last check. It processes any changes, updates your local database, and goes back to sleep. For low volumes, it worked fine.
Then you scaled. The fifteen-minute window started feeling long when customers complained about delayed access. You shortened it to five minutes, then one minute. Now your job runs constantly, making API calls that usually return empty results. Rate limits are stressed, billing API costs have risen, and the fundamental problem remains: customers still wait up to a minute for changes to propagate.
Webhooks eliminate this latency entirely. The billing system pushes events to your application as they occur. The upgrade happens, the event fires, your handler runs, and the features unlock. The delay drops from minutes to milliseconds.
The Event-Driven Model
Understanding webhooks requires shifting from pull to push thinking. In the polling model, your application is the active party, asking the billing system for updates on a schedule you control. In the webhook model, the billing system is the active party, pushing updates to an endpoint you've registered.
This inversion has profound implications for how you architect your application. Polling code typically lives in a scheduled job that reads data and updates state. Webhook code lives in an HTTP handler that receives data and updates state. The logic is similar, but the trigger is different.
Webhooks arrive as HTTP POST requests to an endpoint you specify. The request body contains a JSON payload describing what happened: the event type, the relevant objects (subscription, invoice, customer), and timestamps. Your handler parses the payload, validates that it came from Salable, and processes the event.
The key mental model is that webhooks are facts about things that happened. "Subscription SUB-123 was upgraded to Professional plan at 14:47:23 UTC." Your handler's job is to update your system's state to reflect this fact: Professional plan in the database, Professional entitlements in the access layer.
Event Types That Matter
Salable sends webhooks for all significant billing events. Knowing which events to handle lets you build responsive applications that stay synchronised with billing state.
Subscription lifecycle events tell you when subscriptions are created, updated, or cancelled. A new subscription means a new customer to provision. An updated subscription might mean changed entitlements, seat counts, or a payment status transition. A cancelled subscription means access should end at the appropriate time. These events fire for both standard paid subscriptions and Salable Only Subscriptions.
Usage events for metered billing confirm activity at the end of each billing cycle. usage.recorded fires when a usage subscription cycles and the last period's usage has been processed. usage.finalised fires when a usage subscription ends and the final usage record is closed. These events provide an audit trail and confirm your metered billing is being processed correctly.
Receipt events notify you when a customer purchases a one-off item. receipt.created fires when a one-off line item purchase completes, giving you a hook to trigger fulfilment, send confirmation emails, or unlock non-subscription content.
Access control events keep your records in sync when customer details change. owner.updated fires when an owner's email address is updated after a successful plan purchase.
Each event type has a defined payload structure documented in Salable's webhook reference. The structure includes the event type identifier, a timestamp, and the relevant objects with their current state.
What a Reliable Handler Looks Like
A production webhook handler involves more than a simple HTTP endpoint. Several concerns determine how reliable it is in practice.
Signature verification ensures that webhooks actually came from Salable and weren't forged by attackers. Each webhook includes two headers: x-salable-signature (an HMAC-SHA256 signature computed from the timestamp and request body) and x-salable-timestamp (when the request was sent). Verifying both before processing is essential — the signature confirms authenticity, and the timestamp prevents replay attacks by rejecting requests older than five minutes. The webhooks documentation covers the full verification implementation.
Idempotent processing handles the case where the same event arrives multiple times. Network issues, retries, and edge cases can cause duplicate delivery. Because the same event can arrive more than once, handlers need to be idempotent, producing the same result regardless of how many times a webhook is delivered. The standard approach is checking whether you've already processed an event ID before taking action.
Response timing matters because webhook delivery expects a quick response. A 200 status code within a few seconds acknowledges receipt; if processing takes longer, returning 200 immediately and offloading the event to a queue for background processing prevents unnecessary retries and duplicate delivery. A queue also gives you durability — if the worker crashes mid-processing, the event stays in the queue until it succeeds.
Error handling determines what happens when processing fails. A 5xx status tells Salable to retry. A 4xx indicates the payload is malformed and retries won't help. Either way, logging errors with enough context to reconstruct what happened matters for later debugging.
Common Event Sequences
Certain event sequences appear repeatedly across applications. Understanding these patterns helps you implement correct handling.
Subscription creation typically triggers user provisioning. When you receive a subscription.created event, you might create a workspace for the customer, initialise their settings, send a welcome email, and update your customer database. Operations with dependencies (sending a welcome email with a workspace link, for instance) need to run in the right order.
Subscription upgrade means entitlement changes. The customer's old features continue working while new features become available. The event payload includes what changed, so your handler can update entitlements and access levels accordingly.
Payment status transitions are surfaced through subscription.updated. When a payment fails, Stripe retries automatically via Smart Retries and Salable transitions the subscription to past_due status, which arrives as a subscription.updated event. You can configure whether customers retain entitlement access during this window, and send payment update prompts from your handler. If retries succeed, another subscription.updated brings the subscription back to active.
Subscription cancellation has immediate and scheduled variants. An immediate cancellation means access ends now. A scheduled cancellation (at period end) means access continues until the paid period expires. The handler reads the cancellation timing and acts accordingly.
Testing Handlers
Webhook handlers are notoriously difficult to test because they receive external events that are hard to simulate. Several strategies make testing tractable.
Local testing with webhook forwarding lets you receive real webhooks on your development machine. Tools like ngrok or Cloudflare Tunnel expose your localhost to the internet, giving you a public URL to configure as your webhook endpoint. This lets you trigger real events and see how your handler responds without deploying to a server.
Payload capture during local development gives you test fixtures. Logging the raw payloads your handler receives produces representative examples that can be replayed in unit tests to verify parsing, validation, and processing logic without network dependencies.
Integration tests in staging environments verify the full flow. Create a test subscription, observe the webhook arrive, verify your handler processed it correctly. Automating these tests to run on deployment catches regressions early.
Monitoring in production catches issues that testing misses. Tracking reception rates, processing times, and error rates surfaces problems early. Alerts on sudden drops or spikes in errors let you investigate before customers report them.
Webhook Reliability and Retries
Network failures happen. Servers go down. Bugs cause handlers to crash. A robust webhook system handles these failures gracefully.
Salable's webhook delivery includes automatic retries with exponential backoff. If your handler returns an error or times out, the webhook is retried after a delay. The delay increases with each retry, preventing retry storms from overwhelming a struggling server. Most transient failures resolve within the first few retries; persistent failures eventually stop retrying and are available for investigation in the Salable dashboard.
Your handler should be idempotent to manage retries gracefully. If the first attempt partially succeeded before failing, the retry shouldn't double-count the action. Checking whether the event was already processed (or designing your operations to be naturally idempotent by setting state rather than incrementing counters) keeps retries safe.
Visibility Into the Event Flow
Webhook-driven architectures require visibility into the event flow. When something goes wrong, you need to understand what events arrived, how they were processed, and what state resulted.
Logging every event provides an audit trail. Logging the event type, relevant IDs, timestamp, and processing outcome creates a complete record. When investigating an issue, you can trace from a customer complaint to the events that should have fired to how your handler processed them.
Monitoring delivery health catches systemic issues. Volume tracked over time establishes a baseline. A sudden drop might indicate a configuration problem or a Salable infrastructure issue. A sudden spike might indicate unusual activity worth investigating.
Dashboard visibility through the Salable dashboard shows recent webhooks, their delivery status, and response codes from your handler. When debugging, you can see exactly what was sent and whether your endpoint acknowledged receipt.
Alerts on failures catch problems before customers report them. If your handler starts returning errors, you want to know immediately. Alerts on error rate thresholds let your team investigate before customers notice the impact.
Moving Beyond Polling
Migrating from polling to webhooks typically happens incrementally. You add webhook handlers alongside existing polling, verify they work correctly, then remove the polling once you trust the new system.
During the transition, reconciliation jobs help catch discrepancies. A periodic job comparing your local state against the billing API surfaces any differences: missed webhooks or handler bugs that slipped through. As your webhook handling matures, these runs should find fewer issues until you're confident enough to remove them.
The operational benefits of webhooks compound over time. Lower API usage reduces costs. Faster propagation improves customer experience. Event-driven architecture enables real-time features that polling can't support. The initial investment in building reliable handlers pays dividends in everything built on top of them.
What Webhooks Make Possible
Webhooks enable application patterns that aren't possible with polling.
Instant feature unlocks let customers use new features the moment they pay. No more "your access will be updated within the hour" messages. The upgrade event fires, your handler runs, and the feature is live.
Payment status responses can be immediate and contextual. When a subscription.updated event signals a transition to past_due, you can show an in-app notification prompting the customer to update their payment method while they're actively using your product, rather than waiting for a batch email hours later.
Usage dashboards can update in real-time as metered events are recorded. The customer watches their usage count increment with each API call, building confidence that billing will be accurate.
Automated workflows trigger on billing events without delay. A new subscription could kick off an onboarding sequence. A cancellation could trigger a feedback survey. A payment failure could assign a task to your success team. The speed of webhooks makes these workflows feel automatic rather than batched.
Your billing system becomes a real-time data source rather than a database you periodically sync. Events flow through your application as they happen, keeping every system aligned without manual intervention or scheduled jobs. The customer upgraded at 2:47 PM, and by 2:47 PM everything in your application reflects that upgrade. That's the power of webhooks.
Join the Community
A growing community of developers helping each other navigate SaaS monetisation and billing.
Related Posts
The Webhook Mistakes That Cost Companies Real Money
Webhooks deliver billing events in real-time, but they break in subtle ways. Avoid the pitfalls of duplicates, ordering, and downtime.
SaaS Billing Doesn’t Need More Plans, It Needs a Cart
SaaS add-on billing without subscription chaos. A cart system lets customers bundle core products with add-ons in a single subscription.
Why Monthly Subscriptions Are a Bad Default
Monthly billing is the SaaS default because it's easy, not optimal. The right billing interval unlocks better cash flow and lower churn.