API integration between two systems, and what it really costs
Two systems, one API each, a week of work. That estimate is honest and wrong, and the gap is entirely made of things that never appear in a demo.
Somebody demonstrates it in an afternoon. An order is created in one system and appears in the other, everyone agrees it is straightforward, and a number gets quoted. The number is usually about a third of what the work takes, and the missing two thirds are not complexity anybody hid. They are the cases that only happen once the integration is carrying real volume.
What follows is what those cases are, taken from the published rules of two platforms that document their behaviour openly, because the honest way to estimate this work is to read what the other end actually promises rather than what you assume it does.
The demo runs on the happy path and nothing else
In a demo, one record moves, both systems are up, the network holds, and the person watching is looking at the record rather than at what happens to the next thousand. Every difficult part of an integration is about the second thousand: the message that arrives twice, the one that arrives out of order, the one that does not arrive at all, and the hour last Tuesday when the other end was down.
None of those are exotic. They are the documented, expected behaviour of the systems you are connecting to.
Delivery is not guaranteed, and vendors say so
Stripe's webhook documentation states that it attempts delivery for up to three days with exponential back off in live mode. That is generous, and it also means an event you expected on Monday can legitimately land on Wednesday, after your reconciliation job ran and after somebody has already phoned support about a missing order.
Shopify is blunter still. Its webhook documentation states that delivery is not always guaranteed and that an app can miss or mishandle events for other reasons such as handler failures or downtime, and it recommends running reconciliation jobs as a fallback. That recommendation is the line most estimates leave out: a correct integration is a live feed plus a periodic job that goes and checks, and the second one is real work with its own failure modes.
Ordering is not guaranteed either
This is the one that produces the strangest bugs. Stripe states plainly that it does not guarantee delivery of events in the order they were generated, and gives the example of a subscription that emits creation, invoice and charge events, warning that a destination must not depend on receiving them in a specific order. It goes further and says not to use the created timestamp to determine order or whether an event was already processed, because distinct events can share a timestamp to the second.
Shopify says the same in its own words, that ordering is not guaranteed within a topic or across topics for the same resource, and points to timestamp headers as a way to organise events chronologically after the fact.
The practical consequence is that your handler cannot assume a lifecycle. An update for a record you have not created yet is normal traffic, not an error, and deciding what to do about it is a design decision somebody has to make deliberately.
Duplicates are routine, so writes have to be idempotent
Stripe warns that endpoints might occasionally receive the same event more than once and advises logging the event IDs you have processed and skipping ones already seen. Shopify provides an X-Shopify-Webhook-Id header for exactly the same purpose.
Both vendors are describing the same requirement: every write your integration performs has to be safe to perform twice. That is not a large amount of code, but it is a constraint on the design of everything downstream, and retrofitting it into an integration that assumed exactly-once delivery is considerably more expensive than building it in.
An integration is not a pipe between two systems. It is a small, stateful program that has to be correct when the world misbehaves.
The unglamorous requirements that eat the estimate
Beyond the four big behaviours, the same documentation sets out a list of obligations that each cost an afternoon and collectively cost a fortnight.
- Return a 2xx immediately and do the work asynchronously. Stripe is explicit that the endpoint must return a successful status before any complex logic that could time out, which means a queue, which means somewhere to run the queue.
- Verify signatures. Stripe signs every event and its libraries apply a default tolerance of five minutes between the signed timestamp and now, so your server clock is now part of your integration.
- Do not redirect. Stripe treats redirect responses to webhook requests as failures, so a stray trailing-slash rule in front of your endpoint silently breaks delivery.
- Mind the limits. Stripe allows up to 16 registered webhook endpoints and supports only TLS 1.2 and 1.3.
- Know your replay window. A Stripe event can be resent from the dashboard for up to 15 days after creation and through the CLI for up to 30, which bounds how long you have to notice a problem before the data is gone.
None of these is hard. All of them are invisible in the demo, and together they are most of the difference between the quoted estimate and the real one.
A more honest way to estimate
The question that produces a realistic number is not how many endpoints there are. It is how many distinct record types move, in which directions, and what the correct behaviour is when each one conflicts.
| Question | What the answer changes |
|---|---|
| One direction or two? | Two directions needs a rule for which system wins, and a way to stop an echo loop. |
| How many record types? | Each type is its own mapping, its own edge cases and its own tests. This is the real multiplier. |
| What is the volume? | Under a few hundred a day, a periodic sync may beat webhooks entirely and cost far less. |
| Who owns each field? | Unowned fields are where silent overwrites come from, and they surface months later. |
| What happens on conflict? | Needs a decision from the business, not from the developer. Usually the longest conversation. |
Note the third row, because it is the one that most often saves money. Webhook infrastructure is the right answer at volume and an expensive answer below it. A scheduled job that pulls changes every fifteen minutes is dramatically simpler, has no signature verification, no queue and no replay window, and for a great many internal integrations it is entirely sufficient. The same instinct to reach for the more sophisticated option is covered in no code versus custom development.
The part nobody budgets: it needs an owner
An integration is not finished when it works. It is a running system with a dependency on somebody else's API, and that API will change. Fields get deprecated, rate limits change, credentials expire, and the failure mode is usually silence rather than an error, because the thing that broke is the delivery of messages that now never arrive.
That means monitoring for absence rather than for errors, which is a different and slightly unnatural thing to build: an alert when the expected number of events in an hour is zero. It also means a named person who receives that alert. Budgeting the build without budgeting the ownership is how integrations quietly stop working, which is a specific and expensive form of technical debt, and it belongs in the running cost conversation covered in software maintenance cost after launch.
There is a data quality dimension too, and it is usually underestimated. Connecting a CRM to anything else will expose every inconsistency that has accumulated in it, and the integration gets blamed for problems it merely revealed. Worth cleaning first: bdg-labs sets out a practical version of that in six CRM hygiene rules that hold up.
The short version
Assume delivery is unreliable, ordering is arbitrary and duplicates are normal, because the vendors you are integrating with say so in their own documentation. Build every write to be repeatable, add a reconciliation job, monitor for silence, and give it an owner.
Do that and the estimate roughly triples against the demo, which is uncomfortable to present and considerably cheaper than the alternative. The integrations that cost the most are the ones quoted as a week and then maintained, badly, for three years.
Describe it. We build it.
Seven or twelve days, pay on delivery, a year of maintenance included. Bring the problem, not a spec.
Book a meetingRead next
A restaurant order management system, scoped around the channels
A restaurant order management system earns its keep by consolidating channels, not by taking orders. What it must own, and what the tax receipt forces.
ReadAn AI chatbot for a business website, and where it loses the lead
An AI chatbot for a business website helps or loses the lead on three things: grounding, escalation and disclosure. What to build, and what to skip entirely.
Read