Skip to content

Merge.dev vs Truto: The 2026 Migration & Performance Playbook

A vendor-neutral technical playbook for migrating off Merge.dev. Learn how to extract OAuth tokens, handle API rate limits, and map custom objects without downtime.

Nachi Raman Nachi Raman · · 12 min read
Merge.dev vs Truto: The 2026 Migration & Performance Playbook

If you are evaluating Merge.dev for your unified API strategy, the initial pitch sounds highly appealing: one API, hundreds of integrations, ship your product faster. But the architectural realities of scaling a B2B SaaS product tell a different story. Eventually, you do the math on managing thousands of linked accounts at $65 each. Your biggest enterprise customer demands support for a heavily customized Salesforce object. Your security team flags that Merge is storing a cached copy of every customer's HR data on their servers.

You are not the first engineering team to hit this wall. If you are evaluating how to move off Merge.dev without forcing your enterprise customers to re-authenticate, this playbook gives you the exact architectural pattern: extract OAuth tokens, mirror your existing response shapes through declarative mappings, and swap the underlying engine behind your existing endpoints. The migration itself is a structured engineering exercise—not a six-month re-platform.

This guide is a direct, highly technical Merge.dev migration playbook written for senior product managers and engineering leaders. We will break down exactly where Merge.dev falls short at scale, why Truto's programmable, zero-data-retention API is the superior alternative for enterprise SaaS integrations, and how to execute a zero-downtime migration without forcing your enterprise customers to click "Reconnect."

The Hidden Costs of Merge.dev at Scale

The unit economics of B2B SaaS rely on predictable infrastructure costs. Merge's pricing model actively penalizes growth by tying your middleware costs directly to your customers' adoption.

Early-stage decisions optimize for speed. You use a platform that owns the OAuth application, forces you into their predefined data models, and handles the sync logic. As your customer base grows, the operational reality changes drastically.

The 2026 SaaS statistics report from BetterCloud tracks the average number of SaaS apps per company at 106, a trend we analyzed in our 2026 unified API benchmark. Your customers expect your product to connect to their CRM, their HRIS, their ticketing system, and their accounting software. B2B vendors moving upmarket are expected to plug into a meaningful slice of that stack.

Merge's public pricing is unambiguous. Merge charges $650/month for up to 10 total production Linked Accounts, then $65 per Linked Account after that. A Linked Account is one customer connection to one third-party system. If a single customer connects three integrations (e.g., Salesforce, Jira, and BambooHR), you pay three times.

Let's run the math on a modestly successful mid-market SaaS company. If 100 customers each connect 2 integrations, you are looking at $13,000 per month—$156,000 annually—just in linked account fees. At 1,000 connections, the bill climbs to roughly $65,000/month, before any premium features or overage uplift. If your core SaaS product charges $500 per month, you are spending a massive percentage of your gross margin just to move data back and forth.

There are secondary cost traps worth pricing into your TCO model:

  • Annual capacity commits: Almost all Merge plans require an annual contract with capacity-based pricing, meaning you commit to a minimum number of linked accounts at the beginning of your contract, regardless of actual usage.
  • Forecasting drag: Most teams overestimate their linked account needs in the first 12 months by 2-3x, which compounds the overcommit problem.
  • Enterprise-gated features: Need to know when data was deleted in an external API? According to Merge, that makes you an Enterprise customer requiring a custom contract.

Stop being punished for growth by per-connection API pricing. The fix is not to negotiate harder on $65. It is to decouple your integration cost curve from your customer growth curve. Unified APIs should be priced based on compute and actual usage. Truto bills per integration category, entirely eliminating the per-linked-account tax that ruins your unit economics.

Sync-and-Store vs Zero Data Retention

A sync-and-store architecture creates massive compliance liabilities. A zero data retention API acts as a pure pass-through proxy, keeping your data out of third-party databases.

Merge.dev's core architecture relies on background data synchronization. When a third-party system updates, Merge pulls that data, normalizes it, and stores a copy in their own database. When your application queries Merge, you are actually querying Merge's cached version of the data, not the live third-party system. Data is encrypted and SOC 2 compliant, but remains stored until explicitly deleted.

This creates massive engineering and compliance headaches:

  1. Data Strikethrough and Latency: You are at the mercy of Merge's polling intervals. If a sales rep updates a deal in HubSpot, your application won't see that update until Merge decides to run its next sync job.
  2. Compliance Friction and DPAs: Storing a redundant copy of your customers' highly sensitive CRM, HRIS, and payroll data in a middleware vendor's database creates a massive attack surface. For AI agents that process HR data, financial records, or inbound emails, a cached copy of your customer's data sitting in a third-party system is a security review red flag. Passing enterprise security reviews becomes significantly harder.
  3. Webhooks are Delayed: Because events rely on polling diffs rather than real-time provider webhooks, event-driven architectures suffer from artificial latency.

Truto takes a radically different approach. Truto is a pure pass-through proxy layer. We do not store your payload data.

graph TD
    subgraph Merge Sync-and-Store
        A1[Third-Party API] -->|Polling| B1[(Merge Database)]
        B1 -->|Cached Read| C1[Your Application]
    end

    subgraph Truto Zero Data Retention
        A2[Third-Party API] <-->|Real-Time Proxy| B2{Truto Engine}
        B2 <-->|Normalized JSON| C2[Your Application]
        B2 -.->|No payload persisted| D2[(Token Store Only)]
    end

When you make a request to Truto, our generic execution engine dynamically translates your unified request into the provider-specific format, fetches the live data directly from the third-party API using the connected account's credentials, normalizes the response in memory using JSONata, and returns it to you immediately.

This zero data retention API architecture ensures you are always reading the live state of the third-party system. Your DPA gets shorter because there is no third-party processor holding customer records, regional data residency becomes a simple routing concern, and the blast radius of a vendor breach is limited to OAuth credentials, not the underlying customer payloads.

Trade-off worth naming: A pure pass-through model means you cannot ask Truto for "all contacts updated in the last 90 days" as a single warehouse query, because Truto did not retain them. If you need that pattern, you either run a sync into your own data store or keep a separate ETL pipeline.

Handling Custom Objects and Schema Rigidity

Unified APIs fail when they force enterprise data into rigid schemas. Truto solves this using a 3-level JSONata override hierarchy that requires zero code deployments.

The promise of a unified API is that you write code once against a single schema, and it works for 50 different CRMs. The reality is that no two enterprise Salesforce instances are exactly alike.

Merge forces you into their predefined Common Data Models. If your biggest Salesforce customer has a custom object called Opportunity_Phase__c with five fields you have never seen before, a rigid unified schema fails. You are forced to either (a) drop the data into a generic custom_fields blob, (b) upgrade to a contract-based enterprise plan to get custom field mapping, or (c) bypass the unified model entirely and write raw passthrough requests—defeating the entire purpose of using a unified API in the first place.

Truto's architecture contains zero integration-specific code. The entire platform handles integrations purely as declarative configuration.

Integration-specific behavior is defined entirely as JSONata expressions. JSONata is a functional, Turing-complete query and transformation language for JSON. Because these mappings are just strings stored in a database, Truto offers a 3-level override hierarchy that lets you modify mappings without touching source code.

Info

The 3-Level Override Hierarchy

  • Level 1 (Platform): The default mapping that ships with the connector and works for most standard use cases.
  • Level 2 (Environment): Your specific environment can override the base mapping. If you want to permanently map a specific custom field across all your customers, you do it here.
  • Level 3 (Account): Individual connected accounts can have their own overrides. If exactly one enterprise customer has a strange Salesforce configuration, you apply a JSONata override exclusively to their integrated_account record.

Here is a simplified example of how you can override a response mapping for a specific enterprise account to pull in a custom Salesforce field, without deploying a single line of backend code. Deep-merging happens at runtime:

# Account-level override merged on top of the platform default
response_mapping: >-
  $merge([
    $$.response_mapping,
    {
      "custom_fields": {
        "opportunity_phase": response.Opportunity_Phase__c,
        "contract_value": response.Contract_Value__c,
        "enterprise_score": response.Custom_Lead_Score__c
      }
    }
  ])

This architecture eliminates the SaaS integration maintenance cost associated with schema drift. You handle edge cases with configuration data, not by branching your codebase. The pay-off is that adding support for a custom object becomes a config change instead of a sprint, and your enterprise customers stop being blocked by your vendor's product roadmap.

Performance and API Rate Limit 429 Handling

Opaque rate limiting destroys background sync reliability. Developers need direct visibility into HTTP 429 errors and standardized IETF headers to manage their own backoff queues.

Merge.dev enforces strict API rate limits on its unified API, capping Launch plan users at 100 requests per minute per linked account. That is on top of whatever the underlying provider (Salesforce, HubSpot, Workday) is already enforcing. Two stacked rate limiters mean two places your AI agent or sync job can stall, with two different error formats.

Worse, many unified APIs attempt to "help" by silently swallowing HTTP 429 (Too Many Requests) errors and applying their own opaque exponential backoff. This breaks your internal queueing systems. A retry storm happens when a request fails, your code immediately retries, also fails, retries again, and suddenly three users are each retrying five times—15 requests where there were 3. Your retry logic creates more traffic, causing more 429s, causing more retries. It is a death spiral.

Truto believes in radical transparency. We do not retry, throttle, or absorb HTTP 429 responses from upstream APIs on your behalf. The upstream API's rate limit is the source of truth. When HubSpot or Salesforce returns a 429, Truto passes that error directly to the caller.

However, dealing with 50 different rate limit header formats is painful. Truto normalizes upstream rate limit information into standardized headers per the IETF RateLimit Header Fields specification:

  • ratelimit-limit: The maximum number of requests allowed in the current window.
  • ratelimit-remaining: The number of requests remaining in the current window.
  • ratelimit-reset: The time at which the rate limit window resets.

By passing these standardized headers back to your application, your engineering team retains complete control over your circuit breakers, retry queues, and backoff logic. You get the normalization of a unified API without losing the low-level architectural control required for high-throughput enterprise systems.

A minimal handler that uses the normalized headers looks like this:

async function callUnified(url: string, opts: RequestInit, attempt = 0): Promise<Response> {
  const res = await fetch(url, opts);
  if (res.status !== 429) return res;
 
  const reset = Number(res.headers.get('ratelimit-reset') ?? '1');
  const remaining = Number(res.headers.get('ratelimit-remaining') ?? '0');
  const jitter = Math.random() * 250;
  const waitMs = Math.min(reset * 1000, 30_000) + jitter;
 
  if (attempt >= 5) throw new Error(`429 after 5 attempts, remaining=${remaining}`);
  await new Promise(r => setTimeout(r, waitMs));
  return callUnified(url, opts, attempt + 1);
}

You own the backoff curve, the max attempt count, and the jitter. If you are running an AI agent that needs to fall back to a cached response on 429, that decision belongs in your code, not a vendor's black box.

The Zero-Downtime Merge.dev Migration Playbook

Migrating off a legacy unified API requires extracting your OAuth tokens, remapping your data models, and swapping the underlying infrastructure without your customers noticing.

If you are ready to escape the per-account pricing trap, you need a highly structured strategy. The single failure mode that kills these migrations is forcing customers to re-authorize. Every reconnect is a support ticket, a Slack message to an internal champion who has since left the company, and a measurable bump in integration churn.

Here is the exact architectural playbook to migrate from Merge.dev without re-authenticating customers.

Step 1: Audit OAuth App Ownership Per Provider

For each provider (Salesforce, HubSpot, Greenhouse, etc.), confirm whether the OAuth client_id belongs to you or to Merge. If you are using Merge's white-labeled OAuth apps, you will need to create your own OAuth applications in the developer portals of the respective providers. If Merge owns the app, you cannot transfer the refresh tokens—they were issued to a different OAuth client. In that case, you either negotiate a transfer with Merge (rare) or accept a controlled reconnect campaign for that specific provider only.

Step 2: Export Tokens and Account Metadata from Merge

Use Merge's account token endpoints (or request a secure export from support) to pull the access_token, refresh_token, expires_at, and any provider-specific context (Salesforce instance_url, Zendesk subdomain, HubSpot portal ID). Store the export encrypted at rest—these are bearer credentials to your customers' systems.

Format the payload into Truto's integrated_account schema:

{
  "integration_name": "salesforce",
  "environment_id": "env_12345",
  "context": {
    "oauth": {
      "access_token": "extracted_access_token",
      "refresh_token": "extracted_refresh_token",
      "expires_at": "2026-10-15T10:30:00Z",
      "instance_url": "https://your-instance.my.salesforce.com"
    }
  }
}

Step 3: Register Accounts on Truto (The App Swap)

Truto's integrated account model accepts pre-existing credentials. You POST the token bundle along with a customer identifier, and the platform stores it in a generic credentials context. You plug your new Client ID and Client Secret into Truto's integration configuration. Truto will use these credentials to refresh the tokens you imported. As long as the underlying permissions haven't changed, Truto successfully generates new access tokens using your new OAuth app credentials, and the account stays live.

Step 4: Mirror Your Old API Shape via JSONata

Your frontend and backend systems are currently expecting data shaped exactly like Merge's Common Data Model. If you simply swap the API endpoint, your code will break.

Instead of rewriting your entire application, use Truto's Level 2 (Environment) JSONata overrides to force Truto to output data that exactly mimics the legacy Merge schema. This translation layer acts as an anti-corruption layer. Your application code remains completely untouched.

A fragment that mirrors a Merge-style contact:

response_mapping: >-
  {
    "remote_id": response.id,
    "first_name": response.first_name,
    "last_name": response.last_name,
    "email_addresses": [{ "value": response.email, "email_address_type": "work" }],
    "phone_numbers": response.phones.{ "value": number, "phone_number_type": type },
    "remote_data": [{ "path": "/contact", "data": response }]
  }

Step 5: Webhook Normalization

Truto acts as a unified webhook receiver. Update the webhook URLs in your third-party provider dashboards to point to Truto's /integrated-account-webhook/:integratedAccountId endpoint. Truto verifies the webhook signatures, transforms the raw payload into a standardized event format using JSONata, enriches the data by fetching the full resource, and delivers it to your internal queue.

Step 6: Dual-Write and Diff for 7-14 Days

Route a copy of every read through both platforms. Hash and compare responses. Log any divergence to a structured table. Most diffs will be (a) timestamp formatting, (b) custom field handling, and (c) the order of array elements. Fix these in the JSONata mapping, not in your application code.

Tip

If you are migrating a write-heavy integration (e.g., creating Greenhouse candidates), do dual-writes only in a sandbox environment first. Diffing read responses is safe. Diffing writes by actually creating duplicate records is not.

Step 7: Cut Over Reads and Deprecate

Once the webhooks are re-routed and the JSONata translation layer is tested, you simply swap the base URL in your API client from api.merge.dev to api.truto.one via a feature flag, integration by integration. Keep the old contract live for a week as a fallback. Once error rates and diff counts are clean, stop paying for the old platform.

The migration is complete. Your customers experience zero downtime, they do not have to re-authenticate, and your infrastructure costs instantly decouple from your customer growth.

What to Evaluate Before Signing Anything

Vendor lock-in occurs when the engineering time required to rebuild on top of a vendor's proprietary model makes leaving too expensive. You are effectively held hostage by your own integration success.

Before committing to any replacement, get answers in writing to the questions your future self will care about:

  • Total cost at 200 and 1,000 linked accounts, including overage, enterprise feature uplift, and the engineering time needed to work around platform limitations.
  • Who owns the OAuth client_id for each integration. This is the migration tax that compounds with growth.
  • A live demo with a real Salesforce org that has custom objects. Generic demos hide the pain. Ask to see it live.
  • Data residency and retention policy, in plain language. Region of storage, retention window, deletion SLAs.
  • The programmable escape hatch when the unified schema does not cover a case. Pass-through proxy, raw API access, or declarative override.

By leveraging the SaaS integration migration playbook, you can break free from per-account pricing and rigid data models. Truto's zero-integration-specific-code architecture gives you the speed of a unified API with the architectural control of an in-house build.

Stop letting middleware dictate your margins and your product roadmap.

FAQ

How much does Merge.dev cost at scale compared to Truto?
Merge charges $650/month for up to 10 production Linked Accounts and $65 per additional Linked Account, which scales linearly with customer connections. At 1,000 connections, that is roughly $65,000/month in linked account fees alone. Truto bills per integration category rather than per connection, flattening the cost curve as you grow.
Can I migrate from Merge.dev without forcing customers to re-authenticate?
Yes, provided you own the OAuth client_id registered with each provider. You can export the access and refresh tokens from Merge, register them as integrated accounts on Truto, and the existing OAuth session continues without prompting users to reconnect.
What is a zero data retention API and why does it matter?
A zero data retention API acts as a real-time pass-through proxy. Unlike Merge's sync-and-store architecture, it dynamically fetches and normalizes data in memory without saving a permanent copy of your customers' sensitive payloads in a middleware database. This dramatically simplifies DPAs and enterprise security reviews.
How does Truto handle custom Salesforce objects that do not fit the unified schema?
Truto uses a three-level JSONata override hierarchy (platform, environment, account). You define a per-account mapping that adds custom fields or routes to a custom object endpoint, and the change is stored as configuration. No code deployment is required.
Does Truto automatically retry 429 rate limit errors like Merge does?
No. Merge strictly caps requests and applies opaque backoff, which can cause retry storms. Truto passes upstream HTTP 429 responses directly to your code along with normalized IETF headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset), giving your team full control over retry and circuit breaker logic.

More from our Blog