Ideas Engineered for Tomorrow
We Engineer Services & Solutions for Your Business Needs
Consulting Services Hire Book Consulting

CRM Integration Services in India

CRM integration services in India for CTOs, founders and revenue operations leads in the US, UK, Canada, Australia and New Zealand who need Salesforce, HubSpot or Dynamics 365 to agree with the rest of their stack. We handle the parts that actually break: duplicate and merge rules, lead-to-account matching, two-way sync without loops, consent that travels with the record, and history that arrives intact.

Why Does Your CRM Stop Being Trustworthy Three Months After Go-Live?

The launch went fine. Records moved across, the sync ran, the dashboards populated, everyone signed off. Then somebody searches for a customer and finds four of them. One has the open opportunity, one has the support tickets, one has the billing address, and the fourth has an unsubscribe on it that nobody can explain. Sales stops trusting the search box. Within a month people are back in spreadsheets, and the CRM is a place where data goes to be re-entered rather than a place anyone reads.

This is what makes CRM integration a different job from wiring two APIs together. The technical connection is the easy half. A competent engineer can move contacts from a form into Salesforce in an afternoon. The hard half is that a CRM is a shared model of who your customers are, and every system you connect has a slightly different opinion about what a customer is. Your marketing platform thinks in email addresses. Your billing system thinks in legal entities. Your support desk thinks in whoever raised the ticket. Your CRM thinks in Accounts and Contacts, and none of those four things line up cleanly.

So the failures cluster in the same places every time. Duplicates, because two systems disagreed about whether two records are the same person. Overwrites, because both sides could write the same field and nobody decided who wins. Loops, because a change in system A triggered a write to system B, which echoed back to A. Silent field truncation, because a picklist gained a value on one side and the other side had nowhere to put it. Consent that stopped travelling with the record, which is a compliance problem rather than a data quality one. And attribution that quietly detaches, so the pipeline report and the ad spend report tell different stories and finance stops believing either.

None of that shows up in a connector test. The sync is green. The records moved. What broke is meaning, and meaning has to be designed before it can be transported.

The cost is worth stating plainly, because it rarely appears on anyone's budget line. Sales reps spend time re-keying and de-duplicating instead of selling. Marketing sends to addresses that should have been suppressed. Forecast accuracy drops because opportunities sit on the wrong account. Somebody builds a spreadsheet to work around the CRM, then a second spreadsheet to reconcile the first, and the organisation now has three versions of the truth and a meeting every week to argue about which one is right.

What a CRM Integration Engagement Actually Covers

Scope depends on how many systems touch the customer record and how much history you are carrying, but the shape below is what we build and what we hand over. Everything lives in your org, your tenant, your repository. Nothing runs on infrastructure only we can see.

A record inventory and an object model on one page

Before any code, we write down what each system calls a customer, which identifiers exist, and where they overlap. Not a diagram of boxes and arrows. A table with one row per object showing the natural key, the surrogate key, the system of record, and the fields each side is allowed to write. Most arguments about CRM data are actually arguments about this table, and having it written settles them faster than any amount of discussion.

Matching and survivorship rules you agreed to, not ones a tool guessed

What counts as the same person. What counts as the same company. What happens when the rules disagree. Which record wins each field on a merge. This is the part teams skip, and it is the reason most CRM data cleanups need doing twice.

Idempotent writes keyed on an external identifier

Every record we create carries the source system identifier in an indexed external ID field. Reruns update rather than duplicate. A failed batch can be replayed without cleanup. This one decision removes the largest single cause of duplicate creation, and it costs almost nothing to make on day one and a lot to retrofit on day two hundred.

Directional field mapping with an owner per field

A mapping document listing every field, its direction, its transformation, its owner and its behaviour when empty. Blank does not always mean clear. Sometimes blank means the source has nothing to say, and treating that as an instruction to wipe a populated field on the other side is how three years of notes disappear on a Tuesday.

Error handling that a human can act on

A failed record needs to land somewhere a person will see it, with the payload, the reason, and a retry button. Dead letter storage, alerting on failure rate rather than on individual errors, and a queue somebody actually owns. An integration that fails silently is worse than one that does not exist, because people carry on trusting it.

Reconciliation, because sync drift is inevitable

A scheduled job that counts records on both sides, samples field values, and reports the differences. Every long-running sync drifts. The question is whether you find out from a report or from a customer.

Handover your team can maintain

Runbook, mapping document, deployment steps, and the list of decisions with the reasoning attached. Every batch has to clear the same bar: your engineers have reviewed it, it runs in your environment, your pipeline deploys it, and the documentation is good enough that someone who was not in the room can pick it up.

Duplicates and Merges: The Hardest Problem in CRM Integration

If you only fix one thing, fix this. Duplicate handling is where CRM integration stops being a data movement exercise and becomes a judgement problem, and it is the reason two competent teams can build the same integration and get wildly different results.

Why exact matching fails immediately

Email address looks like a perfect key until you meet reality. People change jobs and keep the record. Shared inboxes like info@ and accounts@ get attached to a dozen humans. Plus addressing means sarah+newsletter@ and sarah@ are the same mailbox but not the same string. Gmail ignores dots in the local part, so s.mith@gmail.com and smith@gmail.com deliver to one person. Company matching is worse: Acme Ltd, Acme Limited, ACME LTD and Acme Ltd. are one company, and the last one has a trailing period that will defeat a string comparison forever.

Why fuzzy matching fails differently

Loosen the rule and you start merging people who should stay separate. Two engineers called David Chen at the same company. A father and son at a family business sharing a landline. Two branches of a franchise that are legally distinct and buy independently.

A false merge costs far more than a false duplicate. A duplicate is annoying. A bad merge destroys data that is hard to reconstruct. In HubSpot, merges cannot be undone. In Salesforce, merged records leave the losing record deleted with its fields discarded, and while you can recover from the recycle bin for a limited period, the field level survivorship decisions are gone.

The rule set that holds up in practice

We build matching in tiers rather than as a single rule. A deterministic tier handles the cases you can be certain about: matching on a normalised email, on a source system identifier, on a company registration number, on a verified domain. A probabilistic tier handles the rest with weighted scoring across normalised name, normalised domain, phone in E.164 format and address components, using a distance measure rather than equality. Then a threshold band in the middle where nothing merges automatically and a human decides. That band is where the value is. Teams that try to eliminate it end up with either duplicates or damage.

Normalisation does most of the work

Before any matching runs, both sides get normalised the same way. Lowercase and trim email. Strip plus addressing where the domain supports it. Reduce company names by removing legal suffixes and punctuation. Convert phone numbers to E.164 so that 020 7946 0000 and +442079460000 compare equal. Reduce free email domains to a flag rather than a match signal, because matching two contacts because they both use gmail.com is how you merge strangers. Most of the accuracy comes from normalisation, not from a clever algorithm.

Survivorship: what wins, field by field

Choosing which record survives is only the start. The interesting question is which value wins for each field. Most recent non-empty is a reasonable default for contact details. Most complete wins for address. The value from the system of record wins for anything that system owns. Consent is a special case: the most restrictive value always wins, because a merge that resurrects an unsubscribed address is a compliance incident rather than a data quality one. And the child records need a decision too. Activities, notes, opportunities and cases have to be reparented rather than lost, and in Salesforce a merge brings related records to the master while some fields on the losing records simply go.

Platform specifics that change the design

Salesforce gives you matching rules and duplicate rules as configuration, with fuzzy methods available on name and account fields, plus duplicate jobs for finding existing duplicate sets in bulk. The merge API handles a master and up to two others at a time, so mass cleanup is an orchestrated loop rather than a single call. HubSpot deduplicates contacts primarily on email and companies on domain, and will auto-associate on that basis whether you wanted it to or not. Dynamics 365 has duplicate detection rules and detection jobs, but for writes coming through the API duplicate detection is suppressed by default, which surprises teams who assume their published rules are protecting them. Knowing that difference before you build is worth more than any tool.

Lead-to-Account Mapping and the Object Model You Inherit

The second structural problem, and the one that quietly wrecks reporting. A lead arrives with an email address and a company name typed by a human in a hurry. Somewhere in your CRM there is already an account for that company, possibly with an open opportunity on it. Connecting those two is called lead-to-account matching, and getting it wrong costs you both the routing and the numbers.

Why the standard model fights you

Salesforce keeps Leads in a separate object from Contacts and Accounts, so an unconverted lead is invisible to account-based reporting. That design made sense when a lead was an anonymous name on a list. It fits badly with the way most teams now sell, where a new enquiry from an existing customer should attach to the account immediately, not sit in a lead queue waiting for conversion. HubSpot has no separate lead object in the same sense, so contacts and companies carry the whole model, which avoids the conversion problem and creates a different one: everything is a contact, including people who will never buy.

Matching a lead to an account without a domain

Email domain is the strongest signal and it is missing exactly when you need it. Someone fills in a form with a personal address. A consultant enquires on behalf of a client. A large group has forty subsidiaries on one domain and three brands on their own. We build the match as a cascade: verified domain first, then registration or tax identifier where you hold one, then normalised company name against account name and known aliases, then a hierarchy lookup so a subsidiary rolls to the right parent, and finally a review queue rather than a guess.

Conversion is where the history breaks

Salesforce lead conversion creates or links an Account, a Contact and optionally an Opportunity, and carries the lead activities across. What it does not do is preserve everything by default. Custom lead fields need explicit mapping to their contact or account counterparts or the values are lost. Campaign membership needs handling. And if the integration converts leads automatically without those mappings in place, you get clean-looking accounts with three months of missing context behind them.

Routing depends on the match, so build the match first

Territory assignment, round robin, named account ownership and any sort of account-based motion all sit downstream of knowing which account a lead belongs to. Teams often build routing first because it is visible, then discover the routing is only as good as the matching underneath it. Enquiries land with the wrong rep, two reps call the same company in one week, and the customer notices. Fix the matching, and routing becomes a small piece of configuration rather than an ongoing argument.

Salesforce Specifics: Limits, APIs, Events and Deploys

Salesforce is a multi-tenant platform with hard boundaries, and integrations that ignore them work in a sandbox with a hundred records and fail in production on the first real load. The constraints are documented and stable, so there is no excuse for meeting them by accident.

Governor limits and why your trigger dies at scale

Apex runs inside a transaction with caps on SOQL queries, DML statements, records processed per DML, CPU time and heap. The number that catches people is not the cap itself but the fact that a trigger receives records in batches. Code written as though it handles one record at a time issues one query per record, and at two hundred records it hits the query limit and the whole transaction fails. Bulkification is not an optimisation, it is a correctness requirement. The same applies to anything invoked by an integration: a synchronous callout chain that works for a single record will fall over the moment a data load arrives.

Choosing between REST, Bulk API 2.0 and streaming

These are not interchangeable and picking wrong is the most common architectural mistake we see. REST and SOAP are synchronous and suit interactive volumes, with sObject Collections and the composite endpoints letting you fold multiple operations into fewer calls, which matters because your org has a 24 hour API request allocation derived from edition and licence count. Bulk API 2.0 is asynchronous and built for volume, taking a CSV upload and processing it in the background with its own separate allocation, which is what a nightly load or a migration should use. Streaming is the other direction entirely: Change Data Capture and Platform Events push changes out to you rather than making you poll. If your integration polls Salesforce every five minutes asking what changed, you are burning your API allocation to discover that nothing did.

Platform Events and Change Data Capture

Change Data Capture emits an event whenever a record changes, with a header carrying the change type, the fields that changed and the user who committed the change. That last field is the single most useful thing in the payload for a two-way integration, because it lets you ignore echoes of your own writes. Platform Events are for the cases where you want to publish an explicit business event rather than a row change, and they let you decouple the publisher from the consumer. Both use a replay identifier so a subscriber that goes offline can resume from where it stopped, within the retention window the platform provides. Build the replay handling on day one. Reconnecting from the current position and silently losing an hour of changes is a bug that shows up as a data quality complaint weeks later.

Sandboxes, and the refresh interval nobody plans around

Developer and Developer Pro sandboxes contain metadata without production data and can be refreshed daily. Partial Copy carries a sample of data and refreshes less often. Full sandboxes carry everything and have a much longer refresh interval, which is the constraint that catches integration projects: if a Full sandbox refresh takes you out of a realistic test environment for weeks, the test plan has to account for it before the project starts, not when someone asks for a refresh. We plan sandbox usage in week one, including which environment carries data volumes realistic enough to catch limit problems.

Metadata deploys and the test coverage gate

Anything you build has to reach production through change sets, the Salesforce CLI with source-tracked deploys, or packaged metadata. Apex deployed to production has to meet the platform's test coverage requirement, and that requirement is checked at deploy time, which means the deployment fails at the worst possible moment if the tests were an afterthought. We write Apex tests alongside the code, keep metadata in version control rather than clicking in production, and rehearse the deploy in a sandbox that matches production configuration.

The integration user, and least privilege

Integrations should authenticate as a dedicated user with a profile scoped to exactly what they touch, using a connected app and OAuth rather than a shared password. Named credentials keep the secret out of your code. The dedicated user matters for a second reason: it makes every write attributable, so loop suppression, audit and troubleshooting all become straightforward. Running an integration as a departing administrator's account is how a sync stops one Monday for reasons nobody can explain.

HubSpot Specifics: Associations, Custom Objects and Rate Tiers

HubSpot is friendlier to start with and has its own set of edges. Teams arriving from Salesforce tend to assume the models map cleanly, and they do not.

Associations are a first-class object, and that changes your writes

In HubSpot the link between a contact and a company, or a deal and a contact, is an association with its own type and optional label. A contact can be associated with several companies while one of them is marked primary, and reporting frequently keys off the primary rather than the set. Integrations that create records and forget to create or correct associations produce a CRM where every object exists and nothing connects. Association labels let you record the nature of the relationship, decision maker against billing contact for example, which is genuinely useful and is also another thing your mapping document has to cover.

Custom objects, and when a property is the better answer

Custom objects are available on the higher subscription tiers and are the right tool when you have a genuinely separate entity with its own lifecycle: a subscription, a property listing, a shipment. They are the wrong tool when what you actually have is an attribute of a contact, and teams reach for them too early. Every custom object adds association management, reporting work and permission configuration. If the data has no independent life of its own, a property on the existing object costs far less to maintain.

Rate limits and the batch endpoints

HubSpot enforces a short-window burst limit measured in requests per ten seconds along with a daily cap, both tiered by subscription and by whether you are using a private app or an OAuth app. Hitting the burst limit returns a 429, and a well-behaved client backs off rather than retrying immediately into the same wall. The bigger lever is the batch endpoints, which accept up to a hundred records per call for read, create, update and upsert on the standard CRM objects. An integration that loops through five thousand contacts one call at a time will throttle, while the same work through batch calls will not come close. We build the batching in from the start because retrofitting it means rewriting the write path.

Properties, internal values and the drift you cannot see

HubSpot dropdown and radio properties have a label and an internal value, and only the internal value matters to the API. Someone renames a stage in the UI, the label changes, the internal value does not, and your mapping keeps working. The reverse also happens: an option gets deleted and recreated, the internal value changes, and writes start silently failing validation. We read the property definitions through the API and validate the mapping against them rather than against a screenshot taken during discovery.

Webhooks and change sources

HubSpot webhook payloads identify what changed the record, distinguishing a change made in the CRM interface from one made by an import, a workflow or an integration. That field is your loop suppression mechanism, and it is cheaper and more reliable than trying to infer the origin from timestamps.

Dynamics 365 and Dataverse: Where the Model Differs

Dynamics 365 sits on Dataverse, which is closer to a relational database with a security model bolted on than to either of the other two platforms. That difference works in your favour for integration, provided you use it.

Alternate keys make upserts genuinely idempotent

Dataverse lets you define an alternate key on a table using one or more columns, then address a row by that key instead of by its internal identifier. Combined with the Web API upsert, this gives you an integration that can be replayed as many times as you like without creating a second row. If you build one thing before anything else in a Dynamics integration, build the alternate key on your source system identifier. Almost every duplicate problem in Dynamics traces back to an integration that had to search first and then decide, because searching and deciding is not atomic and two concurrent runs will both decide to create.

Service protection limits and the 429 you should expect

Microsoft publishes service protection limits per user per web server, covering the number of requests in a five minute sliding window, the combined execution time in that window, and the number of concurrent requests. Exceeding them returns a 429 with a Retry-After header, and the correct behaviour is to honour that header rather than to spread the same work across more threads. Teams that respond to throttling by increasing parallelism make the problem worse. The right answers are batching through $batch, spreading long-running work across a longer window, and using an application user rather than piling everything onto one named account.

Plugins, the execution pipeline and depth

Business logic in Dynamics often lives in plugins registered against pipeline stages, running before validation, before the operation or after it, synchronously or asynchronously. Synchronous plugins run inside the caller's transaction and are subject to a two minute execution ceiling, so a plugin that calls an external service is a design decision with consequences for every integration write. The execution context also carries a depth counter, and the platform stops runaway recursion once depth passes its limit. Relying on that limit as your loop protection is a bad idea, because it stops the loop by failing the operation, which means data is left half written.

Choice columns and integer values

Dataverse choice columns store integer values behind their labels, and integrations that map on the label break the first time somebody edits the text. Map on the integer, read the option set metadata at deploy time, and fail the build when a mapped option no longer exists rather than discovering it in production.

Solutions and environment strategy

Customisations travel as solutions, managed for anything promoted between environments and unmanaged only in development. Layering matters: an unmanaged change in production sits on top of everything and cannot be removed cleanly, which is how a Dynamics environment becomes undeployable. We keep development in its own environment, promote through test, and deploy with Power Platform pipelines or Azure DevOps rather than by exporting a zip file and hoping.

Two-Way Sync and the Loop Problem

One-way sync is straightforward. Two-way sync is where integrations go to die, and the reason is a feedback loop that looks obvious once you have seen it and is invisible until you have.

How the loop forms

A user edits a phone number in the CRM. The CRM fires a change event. Your integration writes the new number to the marketing platform. The marketing platform records a change and fires its own webhook. Your integration receives it and writes back to the CRM. The CRM fires another change event. Nothing stops. In a quiet system this shows up as a modest increase in API usage. Under load it exhausts your rate limits, floods the audit log with thousands of modifications by the integration user, and makes every record look like it was updated seconds ago, which destroys any report or workflow that keys off last modified date.

Four mechanisms that actually stop it

Attribution filtering is the first and best. Write through a dedicated integration user, and ignore any inbound change whose originating user or change source is that same integration. Salesforce Change Data Capture gives you the committing user in the event header. HubSpot webhooks tell you the change source. Dynamics plugin context carries the initiating user. Use them.

Payload comparison is the second. Before writing, compare the incoming value with what is already stored, and skip the write when they match. This is cheap, and it also stops the noise of an integration rewriting identical values every cycle.

A processed-change ledger is the third. Record the identifier of every change event you have handled, with a short retention, and drop repeats. This covers redelivery, which every event system does at least occasionally, and it means an at-least-once delivery guarantee stops being your problem.

Explicit direction per field is the fourth and the one that removes whole categories of the problem. If a field is one-way, no loop is possible on that field. Most fields in most integrations do not need to be bidirectional, and marking them one-way is free.

Conflict resolution when both sides really did change

Two-way means simultaneous edits are possible, and you need an answer before it happens rather than after. Last writer wins is the default everyone reaches for and it depends on clocks agreeing across two systems you do not control, which they do not, and on the assumption that later means better, which is not always true. Field-level ownership is stronger: each field has a system of record, and writes from anywhere else are rejected or queued for review. For the genuinely contested fields, a review queue beats silent overwriting. When a customer success manager and a marketing automation rule both change lifecycle stage in the same hour, the right answer is usually to ask, not to guess.

Ordering, and why timestamps lie

Events do not always arrive in the order they happened, particularly across queues and retries. Sequence numbers or replay identifiers from the source platform are the reliable ordering signal. Timestamps are not, both because of clock skew and because two changes in the same second are common in an automated system. We compare against the version we last saw rather than against wall clock time, so a late-arriving old event gets discarded instead of overwriting a newer value.

Field Mapping, Picklist Drift and the Slow Corruption of a Schema

Field mapping sounds like the boring part. It is the part that decays, and the decay is invisible until somebody runs a report and the numbers are wrong in a way nobody can explain.

Picklist drift

The scenario repeats across every platform. An administrator adds a new value to a dropdown in the CRM because a sales manager asked for it in a meeting. The integration has a mapping table that does not know about the new value. Now every record with that value either fails validation, which at least is visible, or silently falls into a default bucket, which is not. Six months later the pipeline report shows a category with an implausible count and nobody can reconstruct what happened.

The mechanical fix is to read the option set or property definition from the platform API at build time and at deploy time, compare it against the mapping, and fail the pipeline when there is a value with nowhere to go. That turns an invisible data quality problem into a build failure on the day the change was made. The organisational fix is a named owner for picklist changes on both sides, which is harder and matters more.

Type coercion and the values that look fine

Currency stored as a decimal on one side and a string with a symbol on the other. Dates stored as dates in one system and as date-times in UTC in another, which shifts a record by a day for anyone in a timezone behind UTC and produces the classic bug where a close date drifts backwards every sync. Booleans represented as true and false in one place and as Yes and No in another. Multi-select fields stored as semicolon-delimited strings in Salesforce and as arrays elsewhere. None of these are hard. All of them are silent when they go wrong.

Blank, null and the difference between them

The single most damaging mapping decision is what an empty incoming value means. If it means clear the target field, then any source system that does not populate a field will wipe it on the other side. We default to ignoring blanks and treating clearing as an explicit instruction, then list the exceptions where a genuine clear has to propagate.

Getting this backwards is how a company loses three years of account notes in one overnight run.

Field limits and truncation

Text fields have different maximum lengths across platforms, and an integration that truncates without complaining is corrupting data politely. Long descriptions, addresses with several lines, and anything pasted from a document are the usual casualties. Validate length before writing and route the overflow somewhere rather than trimming it. Salesforce also limits how many fields per object can carry history tracking, so if your audit story depends on field history, you need to choose which fields matter rather than assuming everything is tracked.

Consent, Marketing Permission and What Has to Travel With the Record

Consent is the one area where a data quality bug becomes a regulatory exposure, and it is handled badly more often than any other part of a CRM integration. We build the mechanism. Whether your lawful basis, retention periods and disclosures are correct is a question for your own counsel, and this page is not legal advice.

Consent is a record, not a checkbox

A boolean called opted_in tells you nothing useful when someone asks you to prove it. What you need is the state, the channel it applies to, when it was captured, what the person was shown at the time, where it came from, and what changed it last. Each platform has its own model for this. Salesforce has an individual and consent model spanning contact point types. HubSpot has subscription types with a recorded legal basis. Dynamics has consent handling within its marketing and journey products. They do not map one to one, so the mapping has to be written deliberately rather than inferred.

Propagation speed is part of the requirement

An unsubscribe that reaches the CRM on tonight's batch is a problem, because a rep can pull a list and send to a suppressed address between the click and the sync. Suppression changes belong on the real-time path even when everything else runs on a schedule. This is one of the few places where we will argue for event-driven propagation regardless of the rest of the design.

Merges must not resurrect suppression

When two records merge and one carried an unsubscribe, the merged record keeps the unsubscribe. Always. The most restrictive value wins on every consent field, and that rule sits outside the normal survivorship logic where most recent or most complete usually decides. We have seen the opposite behaviour ship, and the result is emailing people who told you to stop, which is exactly the complaint that gets escalated.

Erasure has to fan out

A deletion request that only removes the CRM record leaves copies in the marketing platform, the support desk, the data warehouse, the middleware message store and any backup. An integration that moves personal data is part of your erasure path whether you designed it that way or not, so the deletion flow needs to be built and tested rather than assumed. Also worth checking with your counsel: where records physically sit, since a support team in one country accessing personal data belonging to residents of another has transfer implications that are a contractual and legal matter rather than a technical one.

Least privilege on the integration itself

The integration user should read and write the fields it needs and nothing more. Special category data, if you hold any, should be excluded from the sync entirely unless there is a documented reason for it to move. Scoped tokens, field-level permissions and a written record of what crosses the boundary make the eventual audit conversation short.

Attribution Data and Why Your Pipeline Report Disagrees With Your Ad Spend

Marketing attribution lives or dies on whether the source information stays attached to the record through every hop. It usually does not, and the breakage happens in predictable places.

Where the source data gets lost

The first touch is captured on a form fill with UTM parameters and a click identifier. Then the contact fills in a second form, the integration updates the record, and the source fields get overwritten with the newer, less interesting values. Or the record gets merged and the surviving record keeps its own blank source field. Or a lead converts and the custom source fields were never mapped to the contact and account, so the history is sitting on a converted lead nobody queries. Each of these is a one-line decision in the mapping document, and each of them silently detaches your reporting from reality.

First touch and last touch are different fields

Store both, write first touch once and never again, and let last touch update freely. HubSpot maintains its own original source properties on the contact, which are set at creation and behave differently from properties you populate yourself, so an integration that writes over them is fighting the platform. On the Salesforce side, campaign influence gives you a model for multi-touch, but it only works if campaign membership is actually created when the touch happens rather than reconstructed later from a spreadsheet.

Offline conversions close the loop

The reason this matters commercially is that ad platforms optimise on the conversions you send back. Storing the click identifier on the record at capture, then sending a conversion event when the deal closes, is what lets the platform learn which clicks produce revenue rather than which produce form fills. That requires the identifier to survive every merge, conversion and sync in between, and it requires the upload to happen inside the platform's lookback window, which is bounded and worth checking against current documentation for whichever ad platform you use before you design around it.

Reconciliation as a habit

The practical test is simple. Take last month's closed won opportunities, trace each one back to a first touch, and count how many you cannot. If the answer is more than a few, the attribution chain is broken somewhere specific and findable. We run that check as part of the build rather than leaving it as a reporting problem for someone else to discover.

Migrating History: Records, Activities and What Never Comes Across

Most CRM integration projects arrive with a migration attached, either from a legacy system or from a second CRM after an acquisition. The record migration is usually fine. The activity history is where projects overrun.

Why activity history is harder than the records

Contacts and accounts are flat. Activities are a long tail: emails, calls, meetings, notes, tasks, attachments, each attached to a parent that has to exist first and to an owner who may have left the company. Volume is also an order of magnitude higher. A migration of eighty thousand contacts might carry several million activities behind it, which turns a straightforward load into a batched, resumable job with its own error handling. Salesforce activities also have both a who and a what relationship, and getting those wrong puts a call against the right person on the wrong account.

Preserving created dates

An activity that arrives with today's date is worse than useless, because it makes every record look like it was touched this week. Salesforce allows audit fields to be set on insert once the relevant permission is enabled for the loading user, which is a setting somebody has to turn on before the load and turn off afterwards. Dataverse has a dedicated column for overriding the created-on value. HubSpot allows the create date to be set on import. All three work. None of them work by default, and finding out mid-migration means reloading.

Owners who no longer exist

Historical records belong to people who have left. Assigning them to an inactive user is often not permitted, and assigning them all to one administrator destroys the history of who actually did the work. The usual answer is a mapping from departed users to their current team owner, plus a text field preserving the original name so the history is still readable. It costs one column and it saves the argument later.

What does not survive, and say so early

Email threading rarely comes across intact, so a conversation becomes a set of separate messages. Inline images and some attachment types get lost or arrive as separate files. Rich text formatting in notes gets flattened. Custom report definitions, dashboards and saved views do not migrate at all and have to be rebuilt. Say all of this in week one. A migration where expectations were set honestly is a success; the identical migration where they were not is a dispute.

Reruns without duplicates

A migration is never one run. It is a trial run, three more trial runs, and a final cutover, plus a delta load for everything that changed between the last extract and go-live. That only works if every record carries its source identifier in a unique external ID field so loads upsert rather than insert. Build that before the first trial load, not after the second one produced duplicates.

Should You Build the Integration or Buy an iPaaS?

There is no universally right answer, and the deciding factor is not technical. It is who maintains the thing after the project ends.

The case for a platform

Workato, Boomi, MuleSoft, Tray and Celigo all give you connectors, retry handling, an error console and a visual view of what ran. The real value is that a revenue operations lead can look at a failed run, read the error and retry it without opening a ticket with engineering. If your integration is owned by an operations team rather than a development team, that alone justifies the licence. Native connectors have a place too: the packaged Salesforce integration inside HubSpot handles straightforward field sync perfectly well, and building a custom replacement for it is usually a waste.

The case for code

Custom code gives you version control, tests, code review, and behaviour you can specify exactly. Complex survivorship logic, multi-tier matching and anything with genuine business rules in it tends to be easier to express in a language than in a canvas of connected boxes. There is no per-task billing, so a high volume sync does not get more expensive as you grow. And there is no ceiling: when a platform cannot express something, you are stuck, whereas code just gets a new function.

The case for warehouse-first, which is increasingly the right one

If your data already lands in Snowflake or BigQuery, the identity resolution can happen there where you have full SQL, proper testing with a tool like dbt, and history you can query. The CRM then becomes a destination rather than a participant in a mesh of point-to-point syncs. Reverse ETL tools such as Hightouch and Census exist for exactly this shape. It is more infrastructure up front and it fixes the duplicate problem at the root, because matching runs once in a place designed for it instead of separately inside every connector.

How we usually decide

Low volume, standard objects, operations-owned: buy the platform. Complex matching, real business logic, engineering-owned: write the code. Existing warehouse and more than three systems in play: do identity resolution in the warehouse and push out. We will tell you which of those you are before we quote anything, including when the answer is that your native connector is fine and the real problem is your matching rules.

Three Situations We See Repeatedly

Composites built from recurring patterns across engagements, not any single client's story.

Marketing in HubSpot, sales in Salesforce, nobody trusts the numbers

The native sync is running. Marketing reports 400 qualified leads last quarter. Sales reports 260. The gap is real and it has three causes. First, contacts arriving on free email domains create a new Salesforce Account each time because the matching rule keyed on domain and gmail.com is not a company. Second, lifecycle stage is writable on both sides, so a HubSpot workflow moves a contact forward and a Salesforce update moves it back on the next cycle, and the record oscillates. Third, leads convert without custom source fields mapped, so attribution stops at conversion.

The work is a tiered matching rule that treats free email domains as a non-signal, a decision that lifecycle stage is owned by one side only, a lead-to-account cascade that falls back to a review queue rather than creating, and source fields mapped through conversion. The reporting gap closes because the underlying records finally agree.

Dynamics 365 and an in-house ordering system in a write loop

A plugin on the account table pushes changes to the ordering system. The ordering system pushes order totals back. Each write triggers the plugin again. Nobody noticed for weeks because the volume was low, then a bulk price update touched nine thousand accounts and the environment started returning 429s across the board. Users saw timeouts in the interface, and the audit log showed every account modified hundreds of times.

The fix has three parts. An application user dedicated to the integration, with the plugin ignoring any change initiated by it. A comparison step so identical values are not rewritten. And moving the outbound push from a synchronous plugin to an asynchronous path so a slow external call cannot hold a Dataverse transaction open. Throttling then handled by honouring Retry-After instead of adding threads.

Two Salesforce orgs after an acquisition

Both companies sold to overlapping customers, so the same account exists in both orgs with different names, different owners and different opportunity histories. Neither org is obviously the survivor. Sales leadership wants one pipeline view by the start of next quarter, and both teams are still selling throughout.

This is a survivorship exercise before it is a migration. Match on registration number where available and normalised name plus domain elsewhere, produce a scored candidate list, and put the middle band in front of a human from each side. Then decide field by field which org contributes what, reparent activities and opportunities rather than dropping them, resolve consent to the most restrictive value across both, and carry every legacy record identifier so the trial runs can be repeated safely.

How We Run CRM Delivery From India

This work touches your production customer data, so the delivery model matters as much as the engineering. Here is how it runs, including the parts that are inconvenient.

The overlap window, honestly

A standard Indian working day of 09:30 to 18:30 IST gives London roughly 09:00 to 14:00 local, which is a comfortable four to five hours depending on the time of year. Sydney gets about 14:00 to 17:30 their time, so their afternoon. Auckland gets an hour or two at the end of their day and less again during their summer. For US Eastern the honest answer is that a standard IST day is almost entirely outside your working hours, and for Pacific it is entirely outside them. Nobody should pretend otherwise.

There are two ways to deal with that, and both cost something. Shifting the team later, so the day runs into the evening in India, buys a genuine morning overlap with the US East Coast. That is a real imposition on the people doing it and needs to be agreed rather than assumed. The alternative is to run written-first and treat overlap as a bonus, which works well for build work and badly for anything requiring live decisions. We agree the window and any shift pattern with you before work starts, and we write it down.

What written-first actually means here

Every decision that shapes the integration lands in a document before it lands in code: the object model table, the field mapping, the matching rules, the survivorship rules. Those documents are the interface between your team and ours, and they are how a question asked at 17:00 in New York gets answered by 09:00 the next morning without a meeting. Daily written handover covering progress made, anything stuck, and any call that needs your input, posted where your team already reads.

Sandboxes, access and the production question

Build happens in your sandbox or development environment using a scoped integration user, never with an administrator credential shared over chat. Salesforce access through a connected app with the profile limited to the objects in scope. HubSpot through a private app with only the required scopes. Dynamics through an application user with a security role built for the integration. Production access is separate, requested explicitly, and used in a scheduled window. Where your policy requires it, we work through your VPN or jump host and on your device standards.

Review gates before anything touches live data

Code review by your engineers, not just ours. A dry run in a sandbox with production-like volumes, because a matching rule that behaves on two thousand records can behave differently on two hundred thousand. A sampled comparison of before and after for any bulk update. And for merges, an approval step with a human in it, because merges in most platforms are difficult or impossible to reverse.

Cutover, and the day after

Cutovers get scheduled in your business hours, not ours, which usually means an early start or a late finish for the India team. There is a written rollback plan, a delta load for anything that changed during the freeze, and a defined period of monitoring after go-live with someone watching the error queue. Commercial arrangements including notice, team composition and support terms are agreed with you directly and written into the MSA before work starts.

Why Do CRM Integrations Fail Even When the Sync Works Perfectly?

Because the sales team stops using it. A CRM nobody updates is an expensive read-only archive, and this is the failure mode nobody writes into the scope document. It is also the one that decides whether the project was worth doing.

Every field you add is a tax on somebody's day

An integration is often the moment a company adds twenty fields to the opportunity record because a report needs them. Each one is a thing a rep has to fill in during or after a call. Past a certain number they stop, or they type anything to get past the validation, and your data quality is now worse than before you had the integration. If a field cannot be populated automatically and is not used in a decision, it should not be required. We push back on required fields during design, and we would rather derive a value from something the rep already does than ask them for it.

Automation that visibly moves things is automation people fight

A rep sets a stage. Overnight, an integration moves it back because another system said so. The rep sets it again. After the third time they stop caring about stages, and your forecast is based on a field nobody maintains. If automation is going to change something a human set, it should be visible and it should be explainable. Silent correction is how trust dies.

Speed is a feature

If a rep updates a record and the change takes twelve hours to appear in the tool they actually work in, they will use the other tool. Latency expectations belong in the design, and different objects can have different answers. Consent changes propagate immediately. Deal amounts within minutes. Enrichment data overnight is fine.

Somebody has to own the data model after we leave

Integrations rot when there is no named person who approves changes to fields, picklists and matching rules on either side. Without that role, an administrator adds a picklist value on a Thursday and the integration starts silently bucketing records the following Monday. This is an organisational fix rather than a technical one, and we raise it during design because it is the difference between an integration that lasts three years and one that needs rebuilding in eighteen months.

Risks We Name Before You Sign

Some of these will apply to you. It is better to hear them now than in week six.

Your existing data quality sets the ceiling

If the source system has thirty percent duplicates and half the company names are typed inconsistently, no matching rule will produce a clean result. Cleanup is a workstream with its own effort, and it needs someone on your side who can make decisions about which record is right. We can automate the mechanics. We cannot decide which of two conflicting addresses is your customer's actual address.

Discovery usually finds more systems than you listed

Almost every engagement turns up an integration nobody mentioned: a Zapier automation someone in marketing built two years ago, a spreadsheet upload that runs monthly, an old connector still writing to a field that was supposed to be retired. These have to be found before you build, because an unknown writer will fight your integration and win at unpredictable intervals.

Licences and edition limits are real constraints

Custom objects, API allocations, sandbox types, marketing contact tiers and various platform features are gated by edition and licence count. Discovering mid-build that a design needs a subscription tier you do not have is an avoidable delay. We check entitlements in week one and design inside them, or tell you plainly what a design would require.

Time zones cost coordination, not quality

The overlap arithmetic above is the honest version. Build work does not need overlap. Decisions do, and decisions that wait a day accumulate. The projects that run smoothly are the ones where one person on your side can decide, or where decisions get batched into the overlap window deliberately rather than arriving one at a time.

Vendor platform changes will happen mid-project

API versions get deprecated, connectors change behaviour, and a platform release can alter something you depend on. We pin API versions explicitly, subscribe to the release notes for the platforms in scope, and treat a deprecation notice as a scheduled piece of work rather than an emergency.

Engagement Models

Assessment

A short piece of work against your existing setup. We map what writes to your CRM today, quantify the duplicate rate, find the fields with contested ownership, and produce the object model table and mapping document. Useful on its own, and it usually changes what people thought the project was. Deliverable is a document you own, whether or not you build with us.

Project

A defined outcome with a start and an end. A HubSpot to Salesforce integration built properly, a Dynamics 365 sync with an order system, a migration from a legacy CRM with activity history, or a post-acquisition consolidation of two orgs. Scope agreed up front, built in your environment, deployed by your pipeline, with the runbook and mapping document as deliverables rather than afterthoughts.

Dedicated engineers

CRM and integration engineers who join your existing team: same standups, same backlog, same sprint board. This shape fits an estate where the object model is still moving, with new systems arriving and ownership rules changing every quarter. Notice, support hours and how the arrangement can flex are set out in the MSA once you are ready to start.

Where This Sits Alongside Our Other Work

CRM integration rarely arrives alone. Where the CRM is one node in a wider estate that includes an ERP, a billing platform and a warehouse, the design question is broader than any single connector and belongs with our enterprise integration services. If the CRM is one of several systems being moved off ageing infrastructure at the same time, sequencing matters, and that work overlaps with cloud migration services in India, particularly where the system feeding the CRM is the thing that actually needs replacing.

Once the sync exists, keeping it honest is a testing problem as much as a build one, and the contract between your CRM and everything writing to it is worth covering properly through API testing services in India, particularly the schema and error path cases that break quietly. And if what you need is engineers inside your team rather than a scoped engagement, hiring dedicated .NET developers in India suits a Dynamics and Dataverse estate, while Node.js developers in India fit teams building middleware around HubSpot and Salesforce APIs.

Frequently Asked Questions About CRM Integration in India

Our HubSpot and Salesforce are already connected by the native sync. Why is the data still wrong?

Because a connector moves fields, and most CRM problems are not field problems. The native sync will happily create a second Salesforce Account for a company that already exists under a slightly different name, because it has no view of your matching rules. It also cannot decide which side owns lifecycle stage. Those are modelling decisions, and a connector has no opinion about them until you give it one.

How do you decide which system wins when both sides change the same field?

Per field, not per record, and written down before any code is built. Marketing consent is owned by the marketing platform. Deal amount and close date are owned by the CRM. Billing address is usually owned by the finance system. Last writer wins is only acceptable for fields where nobody cares, and clock skew between systems makes it unreliable even then.

Can you fix duplicates we already have, or only stop new ones?

Both, and they are separate jobs. Stopping new ones is matching rules plus idempotent writes keyed on an external identifier. Cleaning history is a survivorship exercise: deciding field by field which record contributes the winning value, what happens to the activity timeline, and which merges a human has to approve. In HubSpot a merge cannot be undone, so that approval gate is not optional.

What breaks when we hit Salesforce governor limits during a sync?

Usually a nightly job that has been fine for a year until one customer uploads forty thousand rows. Synchronous Apex caps SOQL queries and DML statements per transaction, so a trigger written for one record at a time fails the moment a bulk load arrives. The fix is bulkified triggers, Bulk API 2.0 for volume rather than record by record REST calls, and queueing work instead of doing it inline.

Do we need an iPaaS licence, or can you build this with code?

It depends on who maintains it after we leave. A platform like Workato or Boomi gives your operations team a place to see a failed run and retry it without a developer. Code gives you version control, tests and no per task billing. If your revenue operations lead will own the integration day to day, buy the platform. If your engineers will own it, code is usually cheaper and easier to reason about.

Will our historical activity history survive a migration into a new CRM?

Partly, and you should plan for what will not. Emails, calls, tasks and meetings can be migrated as activity records against the right parent. Created dates can usually be preserved, though in Salesforce that needs the audit field permission enabled first. What rarely survives intact is threading, inline images in old emails, and ownership by people who have already left.

How do you handle marketing consent when a contact exists in three systems?

Consent gets one owner and everything else reads from it. An unsubscribe recorded in the marketing platform has to reach the CRM and the support desk within minutes, not on the nightly run, and a merge must never resurrect a suppressed address. We build the mechanism and the audit trail. Whether your lawful basis and retention policy are correct is a question for your own counsel.

What does the overlap with an India team actually look like for a CRM project?

A 09:30 to 18:30 IST day gives London roughly 09:00 to 14:00, Sydney about 14:00 to 17:30 their time, and New Zealand very little. For US Eastern it is close to nothing without shifting hours, so we agree the window and any shift pattern with you before work starts. Sandbox work carries on regardless. Production cutovers get scheduled in your business hours, not ours.

Tell Us What Your CRM Is Connected To

Send us the platforms, roughly how many records you carry, and the last thing that went wrong. We will come back with where the duplicates are coming from, which fields have contested ownership, and what we would fix first.

Start the Conversation