OAuth App Ownership Explained: Switch Unified API Providers Without Re-Authenticating Customers
Learn the BYO OAuth client pattern, token state machine implications, and webhook-driven reconnect strategies for migrating unified API providers without forcing customers to re-authenticate.
If you are evaluating a migration away from your current unified API provider—or picking your first one to handle your third-party integrations—the most dangerous thing you can overlook is OAuth app ownership. The most important architectural decision you will make has nothing to do with data models, schema normalization, pricing, or endpoint coverage. It comes down to a single security and infrastructure question: who controls the OAuth client ID, client secret, and the resulting authentication tokens?
When you hand your customers' OAuth tokens to a unified API vendor, you are handing them a kill switch over your entire integration layer. If you ever want to switch providers, you face the "migration cliff"—the moment when you have to email hundreds of enterprise customers and ask them to reconnect their Salesforce, Workday, and HubSpot accounts. Some will comply. Many will churn. All of them will question your engineering judgment.
This guide breaks down the technical mechanics of OAuth credential ownership, why most unified API architectures create invisible vendor lock-in through token control, and the exact step-by-step strategy to migrate to a new provider without sending a single re-authentication email to your customers.
The Hidden Cost of Unified APIs: The Migration Cliff
Unified APIs exist to solve a painful engineering problem. Instead of building and maintaining separate integrations for every CRM, HRIS, and ticketing system your enterprise customers use—each with their own API docs, inconsistent pagination, and undocumented edge cases—you write to one schema and the platform handles the rest.
The initial developer experience is legitimately great. You drop in a pre-built authorization component, your users connect their accounts, and data flows through a normalized interface. You avoid the headache of registering developer accounts with 50 different SaaS providers.
But here is what the sales deck does not cover: if the vendor's OAuth application is what your customers authorized, then the vendor holds the tokens. Every access token, every refresh token, every connection your customers made—lives in the vendor's infrastructure under the vendor's OAuth client registration. If you trade engineering velocity for total vendor lock-in, you are simply shifting technical debt from your codebase to your infrastructure bill.
This creates the migration cliff. The moment you decide to switch providers—because pricing scaled past your unit economics, or because the rigid schema cannot handle your enterprise customers' custom Salesforce objects—you face an ugly choice:
- Option A: Ask every connected customer to re-authenticate. Generate hundreds of support tickets. Watch enterprise accounts question your engineering judgment. Absorb the resulting churn.
- Option B: Stay with the vendor. Absorb the price hike. Accept the technical limitations.
As Kong HQ's engineering team notes in their analysis of API gateways: "Today's vendor lock-in is tomorrow's technical debt." Technical debt accumulates as systems become increasingly tailored to specific vendor platforms, creating inextricable dependencies. When companies rely too heavily on a single vendor, they lose leverage, face higher costs, and struggle to adapt to better alternatives.
Neither option is acceptable. Both are entirely avoidable—if you structure your integration layer correctly from day one.
What Is OAuth App Ownership?
OAuth app ownership determines which entity—your company or your integration vendor—registers and controls the OAuth 2.0 application (client ID and client secret) that your end-users authorize.
To understand why this matters, you have to look at the mechanics of a standard OAuth 2.0 Authorization Code flow. When a user connects a third-party software account to your application, the sequence involves three primary actors:
- Your application (or the middleware acting on your behalf) redirects the user to the third-party provider (e.g., Salesforce) with a specific
client_id. - The user logs in and grants permission to the requested scopes, and the provider issues a temporary authorization code.
- Salesforce redirects the user back to your application with that code.
- Your server exchanges that code, along with your
client_secret, for an access token and a refresh token.
If you use a unified API provider's managed authentication service, they hold the client_id and client_secret. They perform the token exchange. They store the refresh tokens in their database.
flowchart TD
A["Your Customer<br>clicks 'Connect Salesforce'"] --> B{"Who owns the<br>OAuth App?"}
B -->|Vendor owns it| C["Tokens bound to<br>vendor's client ID"]
B -->|You own it| D["Tokens bound to<br>YOUR client ID"]
C --> E["Migration = mass<br>re-authentication"]
D --> F["Migration = token<br>export + import"]
E --> G["Churn risk, support<br>tickets, lost trust"]
F --> H["Zero customer<br>disruption"]Because the OAuth application is registered under their developer account, the tokens belong to them. If you decide to leave their platform, you cannot simply take the tokens with you. Even if they export the raw token strings for you, those tokens are mathematically bound to their client_id. You cannot use them to make API calls or request new access tokens from your own infrastructure.
The moment you turn off their service, every single integration breaks. Your users must log in again through an OAuth application that you control. This is not a hypothetical edge case. It is the single most consequential architectural decision in your integration stack, and most teams do not realize it until they are already locked in.
Why Forcing Re-Authentication Causes SaaS Churn
Asking a user to click a button to re-authenticate sounds like a minor inconvenience. In an enterprise B2B context, it is a massive operational disruption.
The math on forced re-authentication is brutal. According to the 2025 Recurly Churn Report, the average churn rate for B2B SaaS companies is 3.5%, split between voluntary churn at 2.6% and involuntary churn at 0.8%. Similarly, LiveX data backs up these baseline metrics. That is your baseline—the churn you experience when everything is working normally.
Now imagine adding a migration event on top of that. You email every enterprise customer asking them to click a link, log into their Salesforce admin, re-authorize your app, and verify the connection works. Migration-driven disruptions can lead to a churn spike of up to 15% if mishandled, according to a 2024 Gartner study cited by Zigpoll.
Organizations used an average of 106 SaaS apps per company in 2024, according to BetterCloud. The integration mesh connecting these systems is highly complex and heavily relied upon for daily operations. When a connection drops, data stops syncing, automated workflows fail, and downstream teams are blocked. Your integration is one of 106 things competing for your customer's attention. Asking them to re-authenticate is asking them to do unpaid IT work on your behalf.
When you force an enterprise customer to re-authenticate a core system like their CRM or HRIS, you are forcing their IT administrator to:
- Notice the integration is broken.
- File a support ticket with your team.
- Track down the individual with the correct administrative permissions in the third-party system.
- Re-approve the OAuth scopes (which may require a security review and a change request ticket).
- Audit the connection to ensure data syncs have resumed.
Every time you force a customer through this loop simply because you changed infrastructure vendors, you burn political capital. Every day a connection is broken, your product is not delivering value. Every day it is not delivering value, the customer is evaluating alternatives.
How Unified API Vendors Trap You with Credential Control
Most unified API platforms use a combination of managed OAuth applications and per-connection pricing to maximize their revenue extraction. The lock-in mechanism is straightforward but worth spelling out, because it compounds with pricing models to create a double bind:
- You sign up for a unified API. The vendor provides a pre-built authorization component (an embeddable link or widget).
- Your customers connect their accounts through this widget. The OAuth flow runs against the vendor's OAuth application. The vendor stores the resulting tokens.
- You scale. 50 connected accounts become 500. Per-connection pricing kicks in. Your integration bill grows linearly with your customer base, and you are soon paying thousands of dollars a month just to maintain idle linked accounts.
- You want to leave. But you cannot export the OAuth tokens, because they are bound to the vendor's client ID. Switching requires every one of those 500 accounts to re-authenticate.
When you attempt to renegotiate, the vendor holds all the leverage. They know you cannot leave without causing massive customer disruption, so they can raise prices without fear of losing you. As seen in the broader enterprise software market—where VMware clients faced price increases of up to 10 times along with costly disruptions due to deeply integrated systems after the Broadcom acquisition—the same dynamic plays out in the unified API market, just at a smaller scale.
As APIwiz and i-Sprint Innovations have highlighted, relying on a single vendor's proprietary gateway or authentication infrastructure leads to high licensing fees and operational risk, stagnating innovation. The per-connection pricing model makes this worse. As your product grows, your integration costs grow at the same rate. But your ability to switch decreases with scale, because the migration cliff gets steeper with every new connected account. You are being punished for growth while simultaneously losing the ability to escape.
Ask this during every vendor evaluation: Can I register my own OAuth application (client ID + secret) and have the platform use it? Can I export all tokens and connection metadata at any time? If the answer to either is "no," you are signing up for lock-in.
The Zero-Lock-In Architecture: Bring Your Own OAuth App
To protect your unit economics and your customer experience, you must adopt a zero-lock-in architecture. As we've previously covered in our guide to avoiding vendor lock-in, the architectural solution is to separate the execution engine (the middleware that transforms and proxies API calls) from the credential ownership (who controls the OAuth apps and tokens).
Here is what that looks like:
You register the OAuth application with each third-party provider (Salesforce, HubSpot, etc.) under your own developer account. You own the client ID and client secret.
The unified API platform uses your credentials to run the OAuth flow. When your customer authorizes access, the resulting tokens are bound to your client registration, not the vendor's.
Tokens are exportable. You can extract every access token, refresh token, and connection context at any time. The platform acts as a highly scalable, generic execution engine—it takes a declarative configuration describing how to talk to a third-party API, normalizes responses, and handles token refresh—but the credentials belong to you.
This is how Truto's architecture works. You provide your own OAuth app credentials at the integration or environment level, and Truto uses them to execute the authorization flow. The credentials themselves are stored in a generic JSON context object tied to the integrated account.
Because Truto contains zero integration-specific runtime code, adding your own OAuth credentials is a pure data operation. The platform handles the hard operational work—proactively refreshing tokens before they expire, managing PKCE verification, handling concurrency so parallel requests do not trigger duplicate refreshes, and automatically detecting when a token needs re-authorization. But the credential ownership stays with you. If you ever leave Truto, you take your tokens with you. Your customers never know anything changed.
The BYO OAuth Client Pattern: How It Works and When It Helps
The Bring Your Own OAuth Client (BYO client) pattern is the single technical prerequisite for a zero-disruption migration. It deserves a closer look because the details matter when you are planning a provider switch.
How BYO client works
In a BYO client architecture, you register an OAuth application directly with each third-party provider (Salesforce, Google Workspace, HubSpot, etc.) under your own developer account. You then supply the resulting client_id and client_secret to your integration middleware. The middleware uses your credentials - not its own - when redirecting users through the OAuth consent screen and when exchanging authorization codes for tokens.
This means every token issued by the third-party provider is cryptographically bound to your client registration. When Salesforce issues a refresh token, it records the client_id it was issued to. Any future refresh request must present the same client_id and client_secret, or the provider rejects it. As RFC 6749 specifies, client authentication is used for "enforcing the binding of refresh tokens and authorization codes to the client they were issued to."
Because you own the client registration, you can present those same credentials from any middleware platform - your old vendor, your new vendor, or even your own infrastructure. The tokens do not care which server is making the request, only that the correct client credentials accompany it.
When BYO client helps
- Planned migrations: You export tokens from vendor A, import them into vendor B, and the tokens continue to work because both vendors use your
client_idto make API calls. - Multi-vendor architectures: You can run two providers in parallel against the same tokens. This is critical for validation during a migration window.
- In-house fallback: If you ever decide to build specific integrations yourself, you can use the same tokens without any customer-facing disruption.
When BYO client is not enough
BYO client does not help if you never set it up in the first place. If your current vendor used their own OAuth apps for all your customer connections, those tokens are bound to the vendor's client_id. No amount of export and import will make them work under a different client registration. In that scenario, you are looking at a partial or full re-authentication event.
Some providers also use refresh token rotation - issuing a new, single-use refresh token with every access token refresh. If there is any overlap period where both your old and new vendors attempt a refresh against the same token, the first one succeeds and the second gets an invalid_grant error because the old refresh token was already consumed. This is why a clean cutover (not a gradual migration of individual API calls) is safer for providers that rotate refresh tokens.
Start with BYO client on day one. Even if you have no plans to migrate, registering your own OAuth apps costs nothing and preserves your options permanently. Retroactively switching from vendor-owned to BYO client requires re-authentication for every affected account.
Step-by-Step: How to Switch Providers Without Re-Authenticating Customers
If you currently own your OAuth applications but are using an expensive unified API provider to handle the API calls and data normalization, you can migrate to a new provider—whether you are moving away from Merge.dev or expanding beyond Finch—without your end users ever knowing.
Here is the concrete technical playbook for migrating between unified API vendors without touching your end-users.
Step 1: Export Tokens and Connection Metadata from the Old Provider
Your first step is to extract the existing tokens from your current vendor. Because you own the OAuth application, these tokens are valid and portable. You need to export a dataset containing:
- The
access_token - The
refresh_token - The
token_typeandexpires_attimestamp - The third-party provider identifier (e.g.,
salesforce) - The tenant ID or customer identifier from your system
- Any connection-specific context (e.g., Salesforce instance URL, HubSpot portal ID, tenant subdomain)
Most providers offer an export API or require a support ticket to retrieve this bulk data. If the old provider does not allow token export because they own the OAuth app, you are in a harder position. You may need to coordinate with the old vendor's support team, or accept that some accounts will need re-authentication. This is exactly the scenario you want to avoid for your next provider choice.
Step 2: Register Your Own OAuth Apps (If You Have Not Already)
For each third-party platform you integrate with, register an OAuth application under your own developer account. You will need to:
- Configure redirect URIs to point to your new provider's callback endpoint.
- Request the exact same scopes your old integration used.
- For platforms that require app review (Salesforce, Google, Microsoft), submit the review process early, as this can take weeks.
Step 3: Import Tokens into the New Provider's Credential Context
Once you have the tokens, you need to map them into your new provider's infrastructure. In Truto, an integrated account is simply a database record that holds credentials in a generic JSON column called context.
You can programmatically create these integrated accounts via API, injecting the exported tokens directly into the OAuth structure Truto expects:
{
"integration_id": "salesforce",
"tenant_id": "customer_123",
"authentication_method": "oauth2",
"context": {
"oauth": {
"token": {
"access_token": "00Dxx0000001gER!AQEAQ...",
"refresh_token": "5Aep861...",
"expires_at": "2026-10-15T14:30:00Z",
"token_type": "Bearer"
}
},
"instance_url": "https://yourcompany.my.salesforce.com"
}
}Because the tokens are bound to your OAuth client ID, they work identically regardless of which middleware platform is executing the API calls. Once imported, Truto immediately takes over the token lifecycle management. If an access token is expired, Truto will automatically use the refresh token to obtain a new one before making the first API call.
sequenceDiagram
participant Client as Your App
participant Truto as Truto API
participant DB as Truto DB
participant Provider as Third-Party API
Client->>Truto: Create Integrated Account (Import Tokens)
Truto->>DB: Store tokens in generic context
Client->>Truto: GET /unified/crm/contacts
Truto->>DB: Fetch context.oauth.token
alt Token Expired
Truto->>Provider: Exchange refresh_token
Provider-->>Truto: New access_token
Truto->>DB: Update context
end
Truto->>Provider: Proxy API Request (Bearer token)
Provider-->>Truto: Native Response
Truto-->>Client: Unified ResponseStep 4: Mimic Legacy Responses Using Declarative Mappings
The trickiest part of migrating integration platforms is updating your frontend and business logic to handle a new data schema. If your old provider returned a first_name field and your new provider returns firstName, your application will break.
To achieve a zero-code migration on your end, you can use Truto's three-level override hierarchy and JSONata expressions to intercept responses and reshape them to exactly match your legacy provider's schema. Instead of rewriting your application code, you define an environment-level override in Truto:
/* Example JSONata mapping to mimic a legacy provider's response shape */
response.{
"id": $string(id),
"remote_id": remote_data.id,
"first_name": properties.firstname,
"last_name": properties.lastname,
"email_addresses": [
{
"email_address": properties.email,
"email_address_type": "WORK"
}
],
"phone_numbers": [
{
"phone_number": properties.phone,
"phone_number_type": "MOBILE"
}
],
"remote_was_deleted": false,
"modified_at": properties.hs_lastmodifieddate
}Because Truto evaluates these JSONata expressions at runtime, the data arrives at your application looking exactly like it did yesterday. Your frontend code does not need to change. This mapping lives in the platform's configuration layer, and you can adjust it on a per-environment or even per-account basis without touching your application code.
Step 5: Run Both Providers in Parallel, Then Cut Over
Do not do a big-bang migration. Run the old and new providers side-by-side for a week or two. Compare responses for a subset of accounts. Verify that pagination, token refresh, and error handling behave identically. Then switch your application's API endpoint from the old provider to the new one.
Your customers see zero downtime. They re-authenticate nothing. They might not even know it happened.
Migration-Specific Token State Machine Considerations
When you import hundreds of OAuth tokens into a new provider, you are not just moving data. You are bootstrapping a token lifecycle management system mid-flight, and the state each token lands in determines what happens next.
The integrated account state machine
Every integrated account in Truto exists in one of these states:
stateDiagram-v2
[*] --> connecting: Account created
connecting --> active: Post-install complete
connecting --> post_install_error: Post-install failed
connecting --> validation_error: Validation failed
active --> needs_reauth: Token refresh failed
needs_reauth --> active: Refresh succeeds or<br>user re-authenticates
post_install_error --> active: Retry succeeds
validation_error --> active: Retry succeedsDuring a migration, the critical transition is active → needs_reauth. When you import tokens, many will already be expired - access tokens typically live only 30 to 60 minutes, and your export-import cycle will almost certainly exceed that window. This is expected and not a problem, as long as the refresh tokens are still valid.
What happens to expired tokens after import
When Truto receives an API request for an integrated account whose access token has expired, it checks the token's expires_at timestamp with a 30-second buffer. If the token is expired, the platform automatically exchanges the refresh token for a new access token before proxying the request. This on-demand refresh fires transparently - your application just sees a slightly longer first request.
After that first successful refresh, Truto schedules a proactive refresh alarm 60 to 180 seconds before the new token's expiry, so subsequent requests never hit an expired token at all.
When imported tokens trigger needs_reauth
Some imported tokens will fail to refresh. Common causes during migration:
- Refresh token was already rotated by the old provider's last refresh cycle, and you exported a stale value.
- The third-party provider revoked the token because the user changed their password or an admin revoked app access.
- The refresh token expired. Some providers (Google, for example) expire refresh tokens after 6 months of inactivity, or if the token was issued to an app still in "testing" mode.
- Scope mismatch. If your new provider's OAuth app requests different scopes than the original, some providers reject the refresh.
When a refresh fails, Truto marks the account as needs_reauth and fires an integrated_account:authentication_error webhook. The account stays in this state until the user re-authenticates or a subsequent refresh attempt succeeds. This is idempotent - if the account is already in needs_reauth, duplicate failures do not generate duplicate webhooks.
Auto-reactivation during migration
There is a useful edge case to know about: if an account is in needs_reauth but a refresh attempt later succeeds (perhaps the provider's token endpoint had a temporary outage), Truto automatically transitions the account back to active and fires an integrated_account:reactivated webhook. Your application does not need to do anything special - the connection self-heals.
This matters during migration because transient failures are common. Network blips, rate limits on the provider's token endpoint, or brief clock skew issues can cause one-off refresh failures that resolve on the next attempt. Do not treat the first needs_reauth event as a permanent failure. Give each account a retry window before escalating to the customer.
Migration tip: After importing tokens, trigger a test API call for each integrated account (e.g., a lightweight GET /unified/crm/contacts?limit=1). This forces an on-demand token refresh and immediately surfaces which accounts need re-authentication, rather than discovering failures days later when a customer's sync breaks.
Webhook and UI Patterns to Minimize Reconnect Friction
Even with BYO client and clean token exports, some accounts will inevitably need re-authentication after migration. The difference between a smooth migration and a support nightmare is how you detect and handle those accounts.
Webhook-driven detection
Truto emits two webhook events that form the backbone of your migration monitoring:
integrated_account:authentication_error- Fires when a token refresh fails and the account moves toneeds_reauth. During migration, subscribe to this event and route it to a migration-specific handler.integrated_account:reactivated- Fires when a previously broken account self-heals through a successful refresh. Use this to update your dashboard and suppress any pending reconnect prompts for that account.
Your webhook handler should maintain a migration ledger - a simple table tracking each imported account's current auth state, the timestamp of the last status change, and whether a reconnect prompt has been sent to the customer.
Building a reconnect flow that does not feel broken
When an account does need re-authentication, you have a narrow window to resolve it before the customer notices broken data. Here is the pattern that minimizes friction:
1. In-app banner, not email. The first touchpoint should be a contextual banner inside your application, shown only to users who have accounts in needs_reauth state. Something like: "One of your connected integrations needs a quick refresh. Click here to reconnect." This is lower friction than email and catches users while they are already engaged with your product.
2. Deep-link to the specific reconnect. Do not send users to a generic settings page. Generate a reconnect link (using Truto's link token flow) that takes the user directly to the OAuth consent screen for the specific provider that needs re-authorization. One click, one consent screen, done.
3. Batch reconnect for power users. If a customer has multiple integrations that need re-authentication, show them a single dashboard listing all affected connections with a "Reconnect" button next to each, rather than sending separate notifications for each one.
4. Escalate to email only after 48 hours. If the in-app banner has not been acted on within two days, send a targeted email to the integration owner (not a blast to the whole company). Include the specific providers affected and a direct reconnect link.
5. Track resolution rate. Use the integrated_account:reactivated webhook to measure how quickly accounts return to active status. If your resolution rate is below 90% after one week, your reconnect UX needs work - not more emails.
flowchart LR
A["Token refresh fails"] --> B["Webhook fires:<br>authentication_error"]
B --> C["Update migration ledger"]
C --> D["Show in-app banner"]
D --> E{"Reconnected<br>within 48h?"}
E -->|Yes| F["Webhook fires:<br>reactivated"]
E -->|No| G["Send targeted email<br>with deep-link"]
F --> H["Suppress prompts,<br>update ledger"]
G --> I{"Reconnected<br>within 7 days?"}
I -->|Yes| F
I -->|No| J["Escalate to CSM<br>for manual outreach"]Monitoring your migration health
During the parallel-run phase, build a simple dashboard that shows:
- Total imported accounts vs. accounts in
activestate vs. accounts inneeds_reauth - Auto-reactivation rate - how many accounts self-healed without customer action
- Mean time to reconnect - for accounts that required manual re-authentication
- Accounts stuck in
needs_reauthfor >7 days - these are your escalation candidates
If your BYO client setup was correct, you should see 95%+ of accounts landing in active state after the initial token refresh wave. The remaining accounts are the ones with legitimately revoked or expired refresh tokens - a normal baseline that exists regardless of migration.
Security, Consent, and Audit Requirements for Token Handoff
Moving OAuth tokens between systems is a sensitive operation. Tokens are bearer credentials - anyone who holds a valid access token can make API calls as the user who authorized it. A sloppy migration can create security exposures that are worse than the vendor lock-in you are trying to escape.
Encryption in transit and at rest
OAuth best practices are clear on this point: tokens must be protected in both transit and storage. Google's OAuth best practices state that tokens must be "stored securely at rest and never transmitted in plain text." When exporting tokens from your old provider, ensure the export is delivered over an encrypted channel (TLS 1.2+, not emailed in a CSV). When importing into Truto, tokens are encrypted with AES-GCM before being written to the database. The encryption keys are managed separately from the application data, so a database compromise alone does not expose token values.
During the migration window, you will temporarily have tokens stored in two systems. Minimize this window. Once you have confirmed the import is successful and the new provider is refreshing tokens correctly, request deletion of the token data from your old vendor.
Audit logging during migration
If your organization operates under SOC 2, ISO 27001, or similar compliance frameworks, your auditors will want to see a clear trail of what happened to customer credentials during migration. SOC 2 Trust Services Criteria require that organizations capture and retain evidence of "who did what, when, and where" for security-relevant events.
For a token migration, your audit log should capture:
- Who initiated the export and import (specific engineer, service account, or automation)
- When each token was exported, transferred, and imported (timestamps for every operation)
- Which accounts were affected (integrated account IDs, tenant IDs, provider names)
- What the outcome was (successful import, refresh failure,
needs_reauthtransition) - When the old vendor's copies were confirmed deleted
Truto logs all credential operations - token refreshes, authentication failures, reactivations, and state transitions - with timestamps and account identifiers. During migration, supplement these platform logs with your own records of the export and transfer steps.
Consent considerations
OAuth tokens represent delegated authorization from your customer's users. When the user clicked "Allow" on the Salesforce consent screen, they granted access to the application identified by your client_id. As long as you continue using the same client_id, the consent grant remains valid. You do not need to re-obtain consent just because you changed which middleware server is making the API calls.
That said, review your terms of service and data processing agreements. If your DPA names a specific sub-processor (your old unified API vendor), you may need to update it to reflect the new vendor. This is a legal and contractual matter, not a technical one, but it is easy to overlook during a migration focused on engineering.
Token portability constraints
Not all tokens are equally portable. Be aware of these constraints:
- Refresh tokens are bound to the client they were issued to. RFC 6749 mandates that "the authorization server MUST maintain the binding between a refresh token and the client to whom it was issued." If you switch
client_idvalues, all existing refresh tokens become useless. - Some providers enforce token binding beyond
client_id. A small number of providers use IP allowlisting or mutual TLS for their token endpoints. If your new middleware runs from different IP ranges, you may need to update allowlists with those providers. - Client Credentials tokens are not portable at all. Client Credentials flow does not produce refresh tokens. These integrations simply acquire a new token using the client credentials each time, so there is nothing to export or import. The migration for these accounts is just configuring the same credentials on the new platform.
- API key and session-based integrations transfer trivially. These are static credentials that work from any server. Export the keys, import them, done.
Never log or expose raw token values in your migration tooling's standard output. Treat tokens like passwords - mask them in logs, transmit them only over encrypted channels, and delete temporary copies as soon as the import is confirmed. RFC 9700 (OAuth 2.0 Security Best Current Practice) recommends mechanisms for sender-constraining tokens specifically to prevent misuse of leaked credentials.
Handling Rate Limits and Retries Post-Migration
When migrating high-volume integration workloads, you must carefully plan for rate limit handling. Different upstream APIs (Salesforce, HubSpot, etc.) express rate limits in wildly different ways—custom headers, non-standard field names, or sometimes no headers at all.
Many legacy unified API providers attempt to abstract away rate limits by silently queueing requests, applying automatic backoff, or absorbing HTTP 429 (Too Many Requests) errors. While this sounds helpful, it creates massive observability black holes. Your application hangs waiting for a response, introducing unpredictable latency spikes and masking capacity problems you need to know about. You have no visibility into the underlying state of the third-party API.
Truto takes a radically transparent approach. Truto does not retry, throttle, or apply backoff on your behalf.
What the middleware should do is normalize the rate limit information into standardized headers so you get consistent data regardless of which upstream API you are hitting. The IETF RateLimit header specification defines three fields: RateLimit-Limit containing the requests quota in the time window, RateLimit-Remaining containing the remaining requests quota in the current window, and RateLimit-Reset containing the time remaining in the current window, specified in seconds.
Regardless of whether you are calling HubSpot, Salesforce, or Jira, the response headers you receive will always look like this:
HTTP/1.1 429 Too Many Requests
ratelimit-limit: 100
ratelimit-remaining: 0
ratelimit-reset: 3600This architecture puts the control back in your hands. Your application logic—or your AI agent's execution loop—reads these standardized headers and implements its own retry and exponential backoff logic. You know exactly how many requests you have left and exactly how many seconds to wait before trying again.
// Your retry logic reads normalized headers - works across all providers
const remaining = parseInt(response.headers.get('ratelimit-remaining') || '100');
const resetSeconds = parseInt(response.headers.get('ratelimit-reset') || '60');
if (response.status === 429) {
// Back off using the standardized reset window
await sleep(resetSeconds * 1000);
return retry(request);
}
// Proactive throttling: slow down before hitting the wall
if (remaining < 10) {
await sleep((resetSeconds / remaining) * 1000);
}This matters during migration because your rate limit budgets do not change when you switch middleware providers—the limits are set by the upstream APIs, not by your integration layer. But having consistent headers across all providers means your retry logic works the same way regardless of which API you are calling. For a deeper dive into rate limit handling patterns, see our guide on handling API rate limits across multiple third-party APIs.
Protect Your Unit Economics and Your Customers' Trust
The integration layer of a B2B SaaS product is infrastructure. Like any infrastructure decision, the choices you make early compound over time. Building B2B SaaS integrations is complex enough without fighting your own infrastructure provider. Handing over control of your OAuth applications to a vendor is a short-term convenience that creates a long-term strategic liability.
Owning your OAuth applications costs you nothing extra up front, but it gives you permanent optionality. You can switch middleware providers, renegotiate pricing from a position of strength, or even bring integrations in-house if your scale justifies it.
Here is the evaluation checklist that every engineering leader and PM should run through before signing with a unified API vendor:
- Can I register and use my own OAuth apps? If not, every customer connection creates lock-in.
- Can I export all tokens and connection metadata? If not, migration means mass re-authentication.
- Does the platform store my customers' data? Zero data retention simplifies compliance and reduces blast radius. Truto operates as a real-time proxy and transformation layer with no payload data retention.
- How are rate limits communicated? Transparent, standardized headers give you control. Silent absorption hides problems.
- Can I customize mappings per-customer without code deploys? Enterprise customers will have custom fields. A rigid unified schema that cannot accommodate them will block deals.
The companies that get this right treat their integration vendor as a replaceable execution engine—not as a credential custodian. Your OAuth apps, your tokens, your data models, your customer relationships. Everything else is middleware.
FAQ
- Can I migrate from Merge.dev to another unified API without re-authenticating customers?
- Yes, but only if you registered your own OAuth applications (BYO client) when setting up through Merge.dev. If the tokens are bound to your client_id, you can export them and import them into any other provider. If Merge.dev's OAuth app was used, the tokens are bound to their client_id and you will need customers to re-authenticate.
- What is the BYO OAuth client pattern?
- BYO (Bring Your Own) OAuth client means you register OAuth applications directly with third-party providers under your own developer account, then supply the client_id and client_secret to your integration middleware. This ensures all tokens are bound to your credentials, making them portable across providers.
- What happens to expired tokens when I import them into a new provider?
- Access tokens will likely be expired by the time you complete an export-import cycle, which is expected. The new provider automatically uses the refresh token to obtain a fresh access token on the first API call. If the refresh token is also invalid, the account enters a needs_reauth state and the user must reconnect.
- How do I handle accounts that need re-authentication during a migration?
- Use webhook events (like authentication_error) to detect which accounts failed token refresh. Show in-app reconnect banners with deep-links to the specific OAuth consent screen. Escalate to email only after 48 hours. Track resolution rates and escalate stuck accounts to your customer success team after 7 days.
- What security precautions should I take when exporting and importing OAuth tokens?
- Encrypt tokens in transit (TLS 1.2+) and at rest (AES-256 or equivalent). Log every export, transfer, and import operation with timestamps and operator identity for audit compliance. Minimize the window where tokens exist in two systems, and confirm deletion from the old provider once migration is complete.