Praneet Sah
Service

Enterprise integrations that don't lose data

Salesforce, HubSpot, NetSuite, Stripe, and your custom system, talking to each other reliably. Webhooks, idempotent sync, retry logic, no silent data loss.

SalesforceHubSpotWebhooksOAuth

Most integration projects don't fail loudly. They fail quietly, six weeks after launch, when someone in finance notices the numbers in NetSuite don't match the numbers in Salesforce and nobody can say when they stopped matching.

That's the actual job here. Not "connect these two systems" — anyone can POST a payload at an endpoint. The job is building the thing that keeps them connected on the day a vendor has an outage, on the day someone bulk-edits ten thousand records, on the day a token silently expires at 2am on a Sunday.

What "talking to each other reliably" actually requires

There are three pieces, and every serious integration needs all three.

Webhooks, handled properly. Webhooks are how you find out something happened without polling. They're also the single most misimplemented part of most integrations. A webhook endpoint has to acknowledge fast, verify the signature, and do the real work asynchronously — because if your handler takes eight seconds to run a Salesforce upsert, the sending system will time out, mark the delivery failed, and retry. Now you've done the work twice. Do that under load and you've got duplicates.

Idempotent sync. Idempotency means processing the same message twice produces the same result as processing it once. It's not a nice-to-have. Every delivery mechanism worth using is at-least-once, not exactly-once — Stripe retries failed webhook deliveries with backoff, and so does everyone else. If your handler isn't idempotent, retries are your bug. In practice this means a durable record of every event ID you've already processed, a unique constraint that the database enforces rather than your application code hoping, and external IDs on every record you create in a downstream system so you can find it again instead of creating a second one.

Retry logic with real backoff and a dead-letter path. Things fail. APIs rate-limit you, deploys restart your workers mid-job, a required field gets renamed by a Salesforce admin who didn't tell anyone. The question isn't whether a sync fails, it's what happens next. Exponential backoff with jitter, a bounded retry count, and then — critically — a dead-letter queue that a human actually looks at. An integration with no dead-letter queue isn't reliable, it's just quiet.

The systems

Salesforce is where the hard edges live. Governor limits, API request allocations, the difference between the REST, Bulk and Streaming APIs, sandbox-vs-production behaviour drift, and the fact that a validation rule or required custom field added by an admin can break your integration without a single line of your code changing. Bulk operations go through the Bulk API, not a for-loop over REST — that distinction alone is the difference between a sync that finishes and one that burns your daily allocation by lunchtime.

HubSpot is friendlier but has its own shape: per-app rate limits with both a short burst window and a daily cap, associations between objects that behave differently from Salesforce relationships, and a property model where the difference between an internal name and a label will cost you an afternoon at least once.

NetSuite is the one that surprises people. SuiteTalk and RESTlets are governed by account-level concurrency rather than simple request-per-minute counting, which means your integration's throughput depends partly on what everyone else in the account is doing. Subsidiary, currency and posting-period rules mean a record that's valid in one context is rejected in another. NetSuite is also where the money lives, so it's the one where a duplicate record is a real financial problem rather than a cosmetic one.

Stripe is the best-designed API of the four and still the one where I see the most bugs — because payments have money-shaped consequences and because webhook ordering is not guaranteed. You can receive invoice.paid before you've finished processing the customer.subscription.updated that explains it. Handlers have to be order-independent, or you need to reconcile against the API rather than trusting the event payload as the whole truth.

And then there's your system — the internal tool, the custom database, the thing that predates everyone currently employed. That's usually where the real work is, because it's the one with no SDK and no docs.

The failure mode nobody plans for

Duplicate records from non-idempotent retries, and silent data loss. Those two account for most of the integration cleanups I get called into.

The duplicate case goes like this: a sync job creates a contact in the downstream system, the response times out on the way back, the job retries, and now there are two contacts. Nothing errored. Nothing alerted. Six months later someone is deduplicating forty thousand records by hand. I wrote up the mechanics of this — including how to fix an existing pipeline that's already producing them — in the Salesforce and NetSuite duplicate-records guide, and the Stripe-specific version in idempotent Stripe webhooks. No point duplicating them here.

The silent-loss case is worse because there's nothing to count. A webhook delivery fails all its retries and lands nowhere. A field gets truncated because the downstream schema is shorter. A record is skipped because it didn't match a filter someone tightened last quarter. Every one of those is invisible unless you built the thing that makes it visible: reconciliation jobs that compare record counts and checksums between systems on a schedule, and an alert when they diverge. That's the piece DIY integrations almost never have, and it's the piece I insist on.

OAuth token lifecycle, the underrated hard problem

Everyone builds the OAuth connect flow. Almost nobody builds what happens after.

Access tokens expire in minutes to hours. Refresh tokens expire too — and they can be invalidated early by an admin password reset, a connected-app policy change, a revoked user, or simple inactivity. When that happens your integration doesn't crash in a useful way; it starts returning 401s on every call, and if the retry layer treats a 401 like a transient failure it will retry forever and get nowhere.

Doing this properly means: encrypted token storage with the refresh token treated as a credential (not a config value in an env file), refresh-ahead rather than refresh-on-failure so you're not racing every request, a single-flight lock so ten concurrent workers don't all try to refresh at once and invalidate each other's tokens, and a distinct un-retryable failure path for "reauthorization required" that pages a human and tells the customer to reconnect. Multi-tenant makes it harder again — every connected account has its own token lifecycle and its own way of going stale.

If you're working under SOC 2 or HIPAA, token storage and the audit trail around it become part of your control set, not just an engineering detail. That's covered in compliance-ready software.

Where the project actually starts

Not with code. It starts with deciding which system is the source of truth for each field, because that decision is the integration. If both Salesforce and NetSuite can edit a customer's billing address, you don't have a sync, you have a race — and whichever system writes last wins, arbitrarily, forever. Most of the "integration bugs" I get handed are really unresolved ownership questions that got papered over with a bidirectional sync.

So the first pass is a field-level map: what flows where, in which direction, what wins on conflict, and what happens to a record that exists on one side and not the other. It's an unglamorous document and it saves weeks. After that comes the shape of the pipeline — event-driven where the volume is spiky and the source emits reliable webhooks, scheduled batch where the volume is large and the freshness requirement is loose, usually both, because the batch job is what catches whatever the event stream dropped.

How I work

$1,000 a day, flat. No hourly billing, no timesheets, no estimate that quietly doubles. Most integrations are measured in days, not months — and I'll tell you the honest number before we start rather than after.

What you get is the whole thing: the sync itself, the idempotency layer, the retry and dead-letter handling, the token lifecycle, the reconciliation job, and the monitoring that tells you when something diverges before your finance team does. You message me directly. There's no account manager.

If you've got two systems that are supposed to agree and don't, tell me what's out of sync and I'll tell you what it'll take.

Tell me what you're building.

Book a call

Praneet Sah

Independent app developer. Builds full-stack products end to end — web, iOS, Android, AI agents, telecom — and has shipped every project referenced on this page personally.