The consumer is not idempotent
Chargebee retries a webhook that does not return a 2xx, with increasing delays, up to seven times over roughly two days. A consumer that provisions or charges on every delivery will do it again on the first retry.
Most billing integration bugs are not API bugs. They are consumers written as if an event arrives exactly once, against a platform that documents retries with increasing delays for up to two days. We build the integration code that handles the retry, the replay and the out-of-order delivery without charging anyone twice.
Trusted by 500+ organizations — including SaaS, subscription and usage-based businesses running their quote-to-revenue operations on Salesforce, HubSpot and Chargebee with Twopir Consulting.
Engineering Scope
These are not exotic. They are the predictable consequences of building against a billing API as if it were a database, and every one of them has cost somebody money.
Chargebee retries a webhook that does not return a 2xx, with increasing delays, up to seven times over roughly two days. A consumer that provisions or charges on every delivery will do it again on the first retry.
A subscription is changed twice in quick succession and the deliveries arrive reversed. Code that applies the payload blindly ends up with the older state winning.
The consumer catches an exception, returns 200 to stop the retries, and logs nothing. The event is gone permanently and nobody finds out until a reconciliation months later.
A timeout on a create call is retried, and the resource is created twice. Chargebee supports an idempotency key header precisely so a retry is safe — it just has to be used.
Proration, entitlement rules and price calculation re-implemented in product code, so the application and the billing system drift apart on every pricing change.
Something was wrong for a week. Without stored events and a replay path, correcting the downstream state means a bespoke script written under pressure.
These are documented platform behaviours, not opinions. A consumer that respects all five is dull to operate; one that respects four is an incident waiting for volume.
Chargebee notifies your systems through webhooks. Events record changes on your site, and each event carries data about the affected resources plus the time it occurred and an id that uniquely identifies it.
A webhook is only treated as delivered when your endpoint returns a status in the 2xx range. If it does not, Chargebee retries with increasing delays for up to two days, with up to seven retries per failed webhook. That is the reliability model your code has to be written against.
| Platform behaviour | What it means for your code | What happens if it is ignored |
|---|---|---|
| Webhooks retry on any non-2xx response | Return 2xx as soon as the event is durably stored, and do the work afterwards. Never do slow work before acknowledging. | A slow downstream call causes a timeout, which causes a retry, which causes duplicate work. |
| Up to seven retries with increasing delays, over about two days | Duplicate delivery is normal operation, not an edge case. | Double provisioning, duplicate notifications, and in the worst case duplicate charges. |
| Each event has an id that uniquely identifies it | De-duplicate on the event id before processing. That is the documented mechanism. | There is no other reliable way to tell a retry from a genuine second event. |
| The last retry lands roughly three days and seven hours after the trigger | Keep processed event ids for at least that window before purging them. | Purging earlier reopens the duplicate-processing window you just closed. |
| API requests accept an idempotency key header | Send one on every create or charge call, so a client-side retry after a timeout is safe. A replayed request is flagged in the response. | A network timeout on a create call produces two subscriptions or two charges. |
Check whether a supported connector already covers it — Chargebee integration services — connector and iPaaS options, configured rather than built Chargebee integration architecture guide — the ownership and consistency model this code implements Chargebee customization — and where configuration genuinely stops Chargebee consulting services — the wider Chargebee practice this work sits inside
We are a development team telling you to write less code. Each step down this list costs more to build and considerably more to own.
The Chargebee managed package for Salesforce, the HubSpot integration, the accounting connectors. Configured properly these cover the standard flows, and the vendor maintains them across their own releases rather than you.
Workato, Celigo or similar give you retry queues, error visibility and a place to change logic without a deployment. For multi-system flows this is usually the right answer even for teams who could build it themselves.
Where your own logic genuinely has to sit in the path — a usage pipeline, a provisioning service, latency requirements an iPaaS cannot meet. This is code you own permanently, and we build it to be handed over.
Six things we are routinely asked to build, and what a correct implementation of each one involves.
The service that receives Chargebee events and turns them into changes in your systems. Correct means idempotent, fast to acknowledge, and safe to replay.
Getting raw product events into Chargebee as clean, billable usage — aggregated, de-duplicated and reconcilable against the source, because a usage bug is a billing bug your customer sees.
The bridge between a subscription change and what your application allows, so an upgrade takes effect immediately and a downgrade actually restricts access.
The code that calls Chargebee, written with the failure modes handled rather than discovered — timeouts, rate limits, partial failures and retries.
The CRM half, where the managed package does not reach: custom objects, flows, Apex and Lightning components that make billing data usable inside the CRM.
Custom-coded workflow actions, private apps and CRM cards where HubSpot's native Chargebee integration stops short of the requirement.
You are going to own this after we leave. These are the standards that make that realistic rather than theoretical.
Every consumer de-duplicates on event id with a retention window covering the full retry period, and every create or charge call carries an idempotency key so a client-side retry after a timeout cannot produce a second resource.
The endpoint stores the event durably and returns 2xx immediately, then processes from a queue. Slow downstream work never happens inside the webhook request, because a timeout there manufactures the retry storm.
Poison messages go to a dead-letter queue with the payload intact and an alert attached. Returning 200 to make retries stop is how a week of missing events becomes a reconciliation project.
Structured logging keyed on event id and resource id, so a support question about one customer's invoice can be answered by searching rather than by reasoning about what the code probably did.
Stored raw events and tooling to reprocess a time range. When something was wrong for a week, correction should be a documented operation rather than a script written under pressure.
Chargebee provides a test site, and we exercise the awkward paths against it — duplicate delivery, out-of-order events, partial failures — because those are the paths production will find.
We review the requirement, the existing systems and the data contracts, and establish whether code is genuinely needed or whether configuration covers it.
Events consumed, API calls made, failure behaviour, idempotency strategy, retention windows and the replay path — documented before implementation starts.
Implementation against a Chargebee test site, with the duplicate, out-of-order and partial-failure paths exercised explicitly rather than assumed.
Released with logging, alerting and dashboards in place from day one, so the first production anomaly is visible rather than inferred later.
Runbook, tests, documented interfaces and a walkthrough with your engineers. Code only we understand is a dependency we would rather not create.
Custom development across connected commercial systems is a core part of Twopir's delivery work.
Custom integration work connecting quoting to the systems downstream, with the data contracts between them designed rather than improvised.
Two platform APIs reconciled with explicit ownership, conflict handling and sync direction rather than a bidirectional sync left on defaults.
Financial data synchronised bi-directionally with reconciliation designed into the flow rather than performed afterwards.
We help growing and mid-market companies solve complex CRM, integration and business system challenges, and we work with enterprise engineering teams on the same problems at higher transaction volume.
Every requirement is tested against configuration and supported connectors before we quote development. A development team that never recommends less development is selling, not advising.
Every consumer we write de-duplicates on event id with a retention window covering the full retry period. We treat duplicate delivery as normal operation, because the platform documents it as such.
Duplicate delivery, out-of-order events, partial failures and timeouts, exercised against a real test site. The happy path is not a test plan.
As a Salesforce Partner and HubSpot Partner we write the Apex, flows, private apps and custom actions alongside the billing-side service — so there is no seam between two vendors for defects to live in.
Tests, documented interfaces, structured logging, a dead-letter queue and a replay path, plus a walkthrough with your engineers. You should be able to operate it without us, and choose to keep us anyway.
A webhook is only considered successful when your endpoint returns an HTTP status in the 2xx range. If it does not, Chargebee retries the call with increasing delays for up to two days, with up to seven retries per failed webhook at exponential intervals. That means your consumer will receive the same event more than once as a matter of normal operation, not as a rare failure.
De-duplicate on the event id, which uniquely identifies the event. Store the ids you have processed and skip anything you have seen before. Because the last retry lands roughly three days and seven hours after the original trigger, keep processed ids for at least that window before purging — purging earlier reopens the duplicate window you closed.
Use an idempotency key. Chargebee accepts one in a request header, and a replayed request is flagged as such in the response, so a client-side retry after a network timeout will not create a second subscription or a second charge. Without it, a timeout on a create call is genuinely ambiguous — you cannot tell whether the resource was created.
For anything beyond simple reads, a service. It gives you one place for idempotency keys, backoff, rate-limit handling and error classification, and one place to change when the API surface evolves. Scattering direct API calls through application code means every one of those concerns is implemented differently in each caller, usually incompletely.
Yes, and it is one of the most common genuinely-custom builds. Usage can be sent to Chargebee by API, bulk upload or the UI, and metered features aggregate those events into billable units. The engineering is in what happens before that: collecting product events, normalising and de-duplicating them, handling late arrivals, and reconciling what you sent against your own source of truth — because a usage bug becomes a billing error the customer sees.
Usually that is the arrangement we prefer. We typically own the integration surface and the billing-side services while your team owns the product, with the interface contract documented between us. We also do pure advisory engagements — design review, code review of an existing consumer, or a second opinion on an architecture before it is built.
You own it outright. It ships with tests, a runbook, documented interfaces, structured logging, a dead-letter queue and a replay path, plus a handover session with your engineers. We keep the custom surface as small as the requirement genuinely allows, because every line we write is a line you maintain.
We build Chargebee integration code that handles retries, replays and out-of-order delivery without charging anyone twice — and we hand it over with the tests, the runbook and the replay tooling.
Design review, build, or a second opinion before you commit