Skip to content

OAuth App Ownership Explained: Switch Unified API Providers Without Re-Authenticating Customers

Own your OAuth apps to switch unified API vendors without re-auth. Covers BYO client, white-label + on-prem OAuth, consent testing, and secret rotation.

Nachi Raman Nachi Raman · · 45 min read
OAuth App Ownership Explained: Switch Unified API Providers Without Re-Authenticating Customers

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.

What Happens to Your Integrations When You Switch Unified API Providers?

The outcome depends on one variable: who owns the OAuth application your customers authorized during connection.

If your vendor owns the OAuth app, switching providers means every connected account breaks the moment you cut over. Every customer has to log into the third-party system (Salesforce, HubSpot, Workday, and so on), find the correct administrator, and re-authorize your app against the new vendor's OAuth client ID. The refresh tokens, access tokens, and provider-specific context (Salesforce instance URLs, HubSpot portal IDs, Workday tenant identifiers) that were issued to the old vendor's client_id are cryptographically useless anywhere else. Your integrations do not gradually degrade - they fail all at once, the instant you flip traffic to the new provider.

If you own the OAuth app, switching providers is a data migration. You export tokens from the old vendor, import them into the new vendor's credential store, and API calls resume immediately. Because the tokens were issued to your client_id, they work regardless of which middleware is executing the request. Your customers see zero disruption. No emails, no support tickets, no consent screens.

The rest of this guide explains the mechanics that make the zero-disruption path possible - and how to build it into your integration architecture from day one so you never end up on the wrong side of a migration cliff.

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 seemingly-magical middleware 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 and Why It Matters

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:

  1. 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.
  2. The user logs in and grants permission to the requested scopes, and the provider issues a temporary authorization code.
  3. Salesforce redirects the user back to your application with that code.
  4. 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:

  1. Notice the integration is broken.
  2. File a support ticket with your team.
  3. Track down the individual with the correct administrative permissions in the third-party system.
  4. Re-approve the OAuth scopes (which may require a security review and a change request ticket).
  5. 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:

  1. You sign up for a unified API. The vendor provides a pre-built authorization component (an embeddable link or widget).
  2. Your customers connect their accounts through this widget. The OAuth flow runs against the vendor's OAuth application. The vendor stores the resulting tokens.
  3. 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.
  4. 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.

Warning

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.

White-Label OAuth and On-Prem Deployment for Compliance

BYO OAuth is the baseline. Regulated industries (healthcare, financial services, defense, public-sector contractors) usually need two additional properties on top of it: the OAuth experience must be fully white-labeled, and the infrastructure that touches tokens must run inside a boundary the customer controls. If you are searching for a self-hosted OAuth server or white-label on-prem identity integration partner, these are the questions that separate real answers from marketing claims.

What white-label OAuth actually means.

  • The consent screen displays your company name, logo, and support URL - not the middleware vendor's - because the OAuth app is registered under your developer account.
  • The redirect URI is on a domain you control (e.g., oauth.yourproduct.com/callback), not the middleware vendor's domain. Any redirect through vendor-owned infrastructure is a white-label leak visible in the browser address bar.
  • Error screens, reconnect prompts, and disconnect flows all render in your product's UI. The user never sees the middleware's brand at any point in the authorization journey.
  • The link/connect UI is either embedded in your app or served from your domain via an iframe or subdomain proxy.

What "on-prem" or "single-tenant" actually means for OAuth middleware.

Enterprise buyers use these terms loosely. Pin the vendor down on which of these levels they support before signing:

  1. Shared multi-tenant SaaS. Fastest to deploy, cheapest to run, but customer tokens sit in a shared datastore alongside other tenants. Fine for most B2B use cases; not fine for regulated data.
  2. Dedicated single-tenant cloud. A separate deployment in the vendor's cloud with an isolated database, isolated compute, and isolated encryption keys. No shared blast radius, but the vendor still operates the plane.
  3. Customer-hosted VPC deployment. The middleware runs inside a virtual network the customer controls (typically an AWS VPC, Azure VNet, or GCP VPC). Tokens never traverse vendor infrastructure. The vendor provides deployment artifacts and remote support.
  4. True on-prem or air-gapped. The middleware runs on a Kubernetes cluster or bare metal inside the customer's data center. Updates are shipped as signed artifacts and applied by the customer. No outbound calls to the vendor's control plane are required for OAuth operations.

Evaluation questions for any "white-label OAuth" or "on-prem identity platform" partner:

  • Can the platform be deployed in a single-tenant VPC or on-prem Kubernetes cluster? What is the artifact, secret, and update model?
  • Does the OAuth authorization flow use your domain or the vendor's? Inspect the network trace during a test flow. Any hop through vendor-controlled DNS is a red flag.
  • Where do tokens live at rest? A "self-hosted" claim is meaningless if tokens still round-trip through the vendor's cloud for refresh.
  • What is the KMS integration story? On-prem deployments usually require BYO KMS (AWS KMS, HashiCorp Vault, or an HSM-backed key) rather than vendor-managed keys.
  • Can policy decisions (rate limits, retry rules, allowed integrations) be configured without an outbound call to the vendor's control plane?
  • What is the update cadence, and is there a stable LTS track? Air-gapped customers cannot pull updates on the vendor's schedule.
  • Is there a signed audit log the customer controls? External auditors will not accept "the vendor has the logs."

Truto supports BYO OAuth apps by default for the multi-tenant SaaS deployment, and dedicated single-tenant deployments for customers with data residency or compliance requirements that cannot be met by a shared platform. In single-tenant deployments, the unified API execution engine, token store, and refresh workers all run inside the deployment boundary, so no customer OAuth token leaves your infrastructure. The consent screen, callback URL, and link UI are white-labeled to your brand end-to-end.

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.

Benefits of BYO OAuth beyond migration

Migration portability is the headline benefit, but it is not the only one. BYO client also gives you:

  • Consent screen branding. Your customer sees your name on the authorization dialog, which materially reduces drop-off during the connect flow. Vendor-owned OAuth apps show the vendor's brand or a generic name that erodes trust.
  • Direct control over scope requests. You can add or remove scopes on your own OAuth app without a middleware vendor's change request queue. This matters when a customer's security review flags an over-broad scope.
  • Rate limit isolation. Most providers apply rate limits per OAuth client. If you share a client with the vendor's other customers, one noisy neighbor can starve your production traffic. Your own client ID gets your own bucket.
  • Direct access to provider security channels. When Salesforce or Google emails you about a policy change, deprecation, or suspicious activity on your client, you receive it directly - not through a support ticket to the middleware vendor.
  • Independent revocation and rotation authority. You can rotate the client secret or revoke a compromised token without coordinating with the middleware vendor.
  • Audit and compliance evidence. Your SOC 2 or ISO 27001 auditor can see that OAuth applications used to access customer data are registered under your organization, not a sub-processor's.

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_id to 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.

Tip

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.

How to Register Your Own OAuth Apps: Provider-by-Provider Workbook

The BYO client pattern is only useful if you actually do the setup work. Below is a concrete workbook for the four providers most enterprise SaaS teams hit first: Salesforce, HubSpot, Google, and Workday. The exact field names, task names, and gotchas differ across providers, so treat this as a checklist rather than a generic template.

Salesforce: Connected App (or External Client App)

Salesforce authenticates external applications through Connected Apps (or the newer External Client Apps, which Salesforce is moving toward). The mechanics are identical; only the setup navigation differs.

  1. Sign into Salesforce and open Setup. In the Quick Find box, search App Manager.
  2. Click New Connected App. Enter a name. Enter your contact email. If you want the newer path, click New External Client App instead.
  3. In the API (Enable OAuth Settings) section, select Enable OAuth Settings.
  4. Set Callback URL to your new unified API vendor's OAuth callback endpoint. Truto exposes an environment-specific callback URL that you paste in here.
  5. Select the OAuth Scopes your integration needs. For most CRM use cases, include Manage user data via APIs (api) and Perform requests at any time (refresh_token, offline_access). The offline access scope allows generation of refresh tokens and prevents repeated authentication.
  6. Save. Once the connected app is created in Salesforce, the Consumer Key (Client ID) and Consumer Secret (Client Secret) can be obtained: Within the app, click on Manage Consumer Details in the app overview to get the keys.
  7. Paste the Consumer Key and Consumer Secret into your unified API vendor's credential configuration for the Salesforce integration.

Heads-up on the platform change: Connected apps creation is restricted as of Spring '26. You can continue to use existing connected apps during and after Spring '26. However, we recommend using external client apps instead. If you are setting this up fresh in 2026 or later, plan for External Client Apps by default.

Common gotcha: Salesforce takes up to 10 minutes to propagate a new Connected App. If your first OAuth attempt fails immediately after creation, wait 10 minutes and retry before assuming misconfiguration.

HubSpot: Public App from the Developer Dashboard

  1. Sign into your HubSpot developer account at developers.hubspot.com. This is a separate account from your production HubSpot portal.
  2. Open the HubSpot developer dashboard. From the dashboard you can click the Create an app button to create your OAuth app.
  3. On the "App info" tab, you will be prompted to fill out some basic information about your app. This includes name, description, logo, etc. These fields appear on the consent screen your customers see, so use production-quality copy.
  4. Lastly, in the "Auth" tab, you will be provided with a Client ID & Client secret. Copy both.
  5. In the same Auth tab, set the Redirect URL to your unified API vendor's OAuth callback. For production environments, this URL must use https, but for local development or testing, http://localhost is acceptable.
  6. Configure Scopes carefully. HubSpot enforces strict scope matching: An insufficient scopes error happens when there is a mismatch of scopes in your application's HubSpot install URL (OAuth). For example, let's say your HubSpot application only has the crm.objects.contacts.write scope selected. If your scopes in Apideck have different/additional scopes, such as crm.objects.contacts.read or files, you will receive the "Insufficient scopes were provided. The same is true regardless of which unified API platform you use. Common CRM scopes: crm.objects.contacts.read, crm.objects.contacts.write, crm.objects.companies.read, crm.objects.companies.write, crm.objects.deals.read, crm.objects.deals.write.

Token behavior to know: A HubSpot access token typically lasts 30 minutes (expires_in: 1800). Refresh tokens can be revoked if scopes change or the app is uninstalled. Otherwise, they are long-lived. This means HubSpot tokens migrate cleanly between middleware providers as long as no scope changes happen during the migration window.

Google: OAuth Client in Google Cloud Console

Google's OAuth setup is more involved than Salesforce or HubSpot because most useful scopes are classified as "sensitive" or "restricted" and require formal verification before production use.

  1. Sign into the Google Cloud Console and create (or select) a project dedicated to this integration. Multiple OAuth clients can live under one project.
  2. Enable the specific APIs your integration needs (Gmail API, Calendar API, Drive API, Admin SDK, etc.) under APIs & Services > Library.
  3. Open APIs & Services > OAuth consent screen. Configure the app name, support email, developer contact email, authorized domains, and app logo. These fields render on the consent screen your customers see, so use production copy and a verified domain.
  4. Choose External user type unless you are only integrating within a single Google Workspace domain.
  5. Add the exact scopes your integration needs. When you add scopes to your project, scope categories (non-sensitive, sensitive, or restricted) are indicated automatically in the Google Cloud Console.
  6. Under Credentials, click Create Credentials > OAuth client ID. Choose Web application. Add your unified API vendor's callback URL as an authorized redirect URI.
  7. Copy the Client ID and Client Secret into your unified API vendor's configuration.
  8. Submit for verification before going live if you request sensitive or restricted scopes. Apps that request access to scopes categorized as sensitive or restricted must complete Google's OAuth app verification before being granted access.

Verification tiers to plan for:

  • Non-sensitive scopes (openid, email, profile): no third-party audit required.
  • Sensitive scopes (e.g., calendar.events): manual review by Google's OAuth team, including a demo video showing scope usage.
  • Restricted scopes (e.g., gmail.readonly, drive.readonly): Google requires a CASA (Cloud Application Security Assessment) audit by an approved third-party lab. Apps must be reverified for compliance and complete a security assessment at least every 12 months after the assessor's Letter of Assessment approval date. Budget 4-8 weeks and a four-figure fee for the assessment, and plan your migration timeline accordingly.

Token behavior to know: Google refresh tokens for verified apps in production are long-lived, but for apps still in "testing" status they expire after 7 days. If your app starts to use the new sensitive or restricted scopes before they are approved, users will experience the unverified app screen and the app will be subject to the 100-user cap. Do not attempt a production migration while your app is still in testing.

Common gotcha: Google enforces a limit on the number of refresh tokens per user per OAuth client. If a single user re-authorizes many times, older refresh tokens are silently invalidated. During migration, avoid re-authorization loops on the same user account.

Workday: Register API Client for Integrations

Workday's model differs from Salesforce, HubSpot, and Google because each Workday tenant is per-customer. Your customer's Workday admin has to register your API client inside their own tenant - you cannot pre-register a single app that all customers use.

  1. Have your customer's Workday admin sign into their tenant. Search for Register API Client for Integrations in Workday's search field. Select the Register API client for Integrations task.
  2. Enter a Client Name the customer will recognize later (e.g., "Acme HRIS Sync").
  3. Select the Non-Expiring Refresh Tokens option. This prevents refresh token rotation on a fixed schedule.
  4. Under Scope, include the functional areas your integration needs. Choose all of the permission scopes for the use-case. Minimum should be 'Staffing' (for employee/worker access), 'Leave of Absence' (for timeoff). Always include 'Tenant Non-Configurable' and 'Integration' as well.
  5. Save. Workday displays the Client ID and Client Secret on the View API Client page. The Client Secret is shown only once. If it's lost, you must regenerate it. Capture it immediately and store it in a secrets manager.
  6. Create an Integration System User (ISU) if one does not already exist. Type Create Integration System User into Workday's search bar and select the task. Enter a username and set a password. Set Session Timeout Minutes to 0 prevent the ISU from timing out. Select the Do Not Allow UI Sessions checkbox to restrict UI logins.
  7. Add the ISU to an Integration System Security Group with the domain permissions your integration requires.
  8. From the View API Client page, click the ellipsis (...) next to the client name and choose API Client > Manage Refresh Tokens for Integrations for token management. Select the ISU and generate a refresh token. Capture the refresh token value immediately.
  9. From the View API Clients report, save the URLs listed in the Token Endpoint and Authorization Endpoint fields. These URLs are required for the OAuth 2.0 connection. Both are tenant-specific and must be provided per customer.

Because Workday integrations use one refresh token per tenant (rather than one per end user), migrating Workday connections between unified API vendors is often the simplest of the three: it is a per-tenant record swap with no user-facing consent flow.

Before you route real customers through your OAuth flow, run this consent-screen test for every provider you registered an app with. It catches the most common misconfigurations before they surface as support tickets.

Prerequisites:

  • OAuth app registered under your developer account with the Client ID and Client Secret configured in your unified API vendor's staging environment.
  • Redirect URI configured on the OAuth app to point at the staging environment's callback endpoint.
  • A test third-party account (a Salesforce Developer Edition org, a personal HubSpot test portal, a dedicated Google Workspace test user, or a Workday sandbox tenant). Never test against production data.
  • Browser dev tools open so you can inspect the network trace.

Test procedure and expected results:

  1. Trigger the flow. From your staging application, click the "Connect [Provider]" button. Expected: the browser navigates to the third-party provider's /oauth/authorize (or equivalent) URL.
  2. Inspect the authorization URL. Expected: the client_id query parameter matches the value you configured (not the middleware vendor's default). The redirect_uri matches what you configured on the OAuth app. The scope parameter lists exactly the scopes you expect, in the order you configured them. The state parameter is present and non-empty (CSRF protection). For providers that support PKCE, a code_challenge is present.
  3. Complete the consent screen. Sign in with the test account. Expected: the consent dialog displays your app name, your company logo, and your support URL. The listed scopes on the consent screen exactly match what you configured. If any of these fields show the middleware vendor's brand, the OAuth app was registered under the vendor's account and you need to redo the registration.
  4. Approve the connection. Expected: the browser redirects to your callback URL with a code query parameter and the state parameter echoed back exactly. If state does not match, your middleware should reject the callback.
  5. Confirm token exchange. Expected: in your unified API vendor's dashboard, the integrated account is now in active state. The record includes an access_token, refresh_token, token_type: Bearer, an expires_at timestamp in the future, and any provider-specific context (Salesforce instance_url, HubSpot portal ID, etc.).
  6. Trigger a test API call. Hit a lightweight endpoint like GET /unified/crm/contacts?limit=1. Expected: HTTP 200 with a normalized response body. If you get 401 or 403, the scope set is insufficient. If you get 400 with a schema error, the response mapping needs adjustment.
  7. Force a refresh. Wait for the access token to expire (or manually invalidate it if the vendor supports that). Trigger another API call. Expected: the middleware transparently exchanges the refresh token for a new access token and completes the request. Your application sees only a slightly longer first response.
  8. Test revocation. In the third-party provider's admin UI, revoke access for your OAuth app. Trigger another API call. Expected: the middleware transitions the account to needs_reauth, fires the authentication_error webhook, and returns a 401 (or provider-specific error) to your application.
  9. Test reconnection. Generate a reconnect link and complete the flow again with the same test user. Expected: the middleware updates the existing integrated account record (not create a duplicate), transitions the state back to active, and fires the reactivated webhook.
Step Expected result Common failure mode
2 client_id matches yours Middleware is using its own default OAuth app
3 Consent shows your branding OAuth app registered under vendor's account
3 Scopes match config exactly Extra vendor-default scopes being requested
5 Tokens stored, state active Client secret mismatch between provider and middleware config
6 HTTP 200 with data Requested scopes insufficient for the endpoint
7 Silent refresh Refresh token not persisted, or secret mismatch on refresh call
8 needs_reauth + webhook Webhook not subscribed, or state machine misconfigured
9 Same account updated Duplicate integrated account created (identifier collision)

Any failure here is a configuration bug that will cascade into production. Fix it in staging before onboarding real customers.

Token Portability: Mechanics and Export/Import Considerations

Not every OAuth token behaves the same during a migration. Some providers rotate refresh tokens on every use. Some bind tokens to IP ranges. Some do not issue refresh tokens at all. Before you plan the actual cutover, map every provider you integrate with against these behaviors.

What to include in the export dataset

For each connected account, your export should include:

  • access_token and refresh_token values
  • expires_at timestamp and token_type
  • The scope string the token was issued for
  • Provider-specific context: Salesforce instance_url, HubSpot hub_id / portal ID, Workday tenant plus token and authorization endpoints, Xero tenant_id, and so on
  • Your internal tenant/customer identifier so the imported account maps back to the correct customer record on the new platform
  • The client_id under which the token was issued (useful for auditing and confirming portability)

Provider-specific portability behavior

  • Refresh token rotation. Some providers issue a new refresh token every time the previous one is used; others return the same value. Google and Xero rotate refresh tokens. HubSpot and Salesforce do not - each HubSpot account generates a unique refresh token, but the same refresh token can be reused across refresh cycles. If your old vendor recently refreshed a rotating token, your export may capture a stale value.
  • Refresh token lifetime. HubSpot refresh tokens can be revoked if scopes change or the app is uninstalled. Otherwise, they are long-lived. Workday refresh tokens can be marked non-expiring at registration. Google refresh tokens for apps in "testing" status expire after 7 days.
  • IP allowlists and mutual TLS. A subset of enterprise providers (some banking APIs, certain healthcare providers) restrict token endpoints to allowlisted IP ranges or require mTLS at the token endpoint. If your new middleware runs from different IPs, coordinate the allowlist change with each affected provider before cutover.
  • Client Credentials tokens. The client credentials flow doesn't support refresh tokens. There is nothing to export - just configure the same client_id/client_secret on the new platform and let it acquire fresh tokens on demand.
  • API key and Basic auth integrations. Trivial to migrate. Export the keys, import them, done. No client_id binding to worry about.

Token migration and portability best practices

  • Freeze refreshes on the source system during export. If both the old vendor and your export tool refresh the same rotating token simultaneously, one of them will get a stale value that fails at import. Coordinate a short freeze window (5-15 minutes) where the old vendor pauses refresh workers while you snapshot the tokens.
  • Export via encrypted channel, never plain files. Use a signed URL over TLS, an SFTP transfer with per-run credentials, or a direct API-to-API pull. Never email a CSV of tokens; never drop the export in a shared drive.
  • Import in small waves. Split the imported accounts into batches of 50-200 and validate each wave with a test read before proceeding. Big-bang imports of thousands of accounts make it hard to diagnose systemic issues (e.g., wrong secret configured on the new middleware).
  • Preserve the original expires_at timestamps. Do not zero them out on import. The new middleware uses expires_at to decide whether to refresh on the next call; overwriting it will cause a refresh storm the moment traffic switches over.
  • Store the source client_id alongside the token. During validation, having the client ID recorded lets you verify the token was issued under your client (portable) and not under the vendor's default client (not portable).
  • Delete tokens from the source system on a defined schedule. Once the new middleware has successfully refreshed each imported token at least once, request deletion from the old vendor within 30 days. Get written confirmation for your audit trail.

Verifying portability before cutover

For each imported account, trigger a lightweight read call (e.g., GET /crm/v1/contacts?limit=1) to force an on-demand token refresh through the new platform. Sort the results into three buckets:

  1. Success. The account is fully portable and ready for cutover.
  2. needs_reauth after refresh. The refresh token was rejected. These accounts require customer re-authentication.
  3. Other errors (400s, provider 5xx). Investigate individually. Common causes: scope mismatch, stale Salesforce instance_url, expired Workday ISU password.

If bucket 1 is 95%+ of your accounts, proceed with cutover. If it is below 90%, pause and investigate before touching production traffic.

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_type and expires_at timestamp
  • 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 Response

Step 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 succeeds

During 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 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.

Info

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 to needs_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 active state vs. accounts in needs_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_reauth for >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.

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_reauth transition)
  • 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.

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_id values, 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.
Warning

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.

Secrets Handling and Rotation Patterns

OAuth client secrets are long-lived credentials that authorize every token exchange under your client ID. A leaked client secret plus a leaked refresh token gives an attacker persistent access to a customer's third-party account. Treat client secrets with the same rigor as production database passwords.

Where secrets should live

  • Never in Git. Not in .env files committed to a repo, not in Helm values files checked into source control, not in Terraform state stored without encryption. Scan your repos with a secrets detection tool before any migration.
  • In a dedicated secrets manager. AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, or Azure Key Vault. Access is audit-logged, versioned, and gated by IAM policies.
  • Not in application config files distributed to developer laptops. Fetch secrets at process start via IAM roles or short-lived credentials.
  • Encrypted at rest by the middleware that consumes them. If your unified API vendor holds the secret to run OAuth on your behalf, verify it is encrypted with a KMS-backed key and that access is audit-logged. For single-tenant or on-prem deployments, verify the vendor supports BYO KMS so the encryption keys never leave your control.

Rotation cadence

  • Rotate at least annually. SOC 2 and ISO 27001 do not prescribe an exact cadence, but auditors expect a documented rotation policy that is actually followed.
  • Rotate immediately after: an employee with secret access leaves, a suspected exposure event, a provider security advisory, or any incident that could plausibly have exposed the secret.
  • Rotate on major provider security changes. If the provider publishes a CVE affecting OAuth or announces a policy change (e.g., mandatory PKCE), treat it as a trigger to rotate.

Zero-downtime rotation pattern

Most major providers (Salesforce, HubSpot, Google) support an overlap window where two client secrets are simultaneously valid, so you can rotate without breaking active traffic. The pattern:

  1. Generate a new client secret in the provider's developer console. The old secret remains valid.
  2. Store the new secret in your secrets manager under a new version. Do not overwrite the old version yet.
  3. Roll out the new secret to your middleware. For single-tenant deployments, this is a config update and restart. For SaaS middleware, use the vendor's API to update the secret on the integration configuration.
  4. Verify a fresh consent-screen test succeeds with the new secret (rerun the test procedure from earlier in this guide).
  5. Verify a refresh succeeds on the new secret against an existing refresh token. Refresh tokens are bound to the client ID, not the secret, so they should continue to work with the rotated secret.
  6. Revoke the old secret in the provider console once you have confirmed the new secret is working for both fresh authorizations and existing refresh flows.
  7. Log the rotation event in your audit trail: who rotated, when, on which provider, and confirmation that the old secret was revoked.

What does not need to change during rotation

Refresh tokens issued under your client_id do not need to be reissued when the secret rotates. The refresh token is bound to the client ID, not the secret. Rotating the secret invalidates any attacker who copied the old secret, but existing refresh tokens continue to work under the new secret. Your customers see nothing.

Provider-specific rotation notes

  • Salesforce: In App Manager, open the Connected App and choose Manage Consumer Details > Rotate Consumer Secret. Salesforce provides a configurable grace period where both secrets are valid; use it.
  • HubSpot: Client secrets can be rotated via the Auth tab of the app settings. Rotation is immediate with no overlap window, so schedule during a low-traffic period and pre-stage the new secret in your secrets manager.
  • Google: Client secrets can be rotated from the Credentials page of the Google Cloud Console. Multiple client secrets can be active concurrently, giving you a clean overlap window for zero-downtime rotation.
  • Workday: Client secrets are per-tenant. Rotation requires coordinating with each customer's Workday admin, which makes automation harder. Batch rotations by customer, provide a runbook the admin can follow, and communicate the rotation window in advance.

Automating rotation

For providers that expose a secret-rotation API (Google, Salesforce via Metadata API), wrap the rotation in a scheduled job that runs quarterly. The job:

  1. Reads the current secret version from the secrets manager.
  2. Generates a new secret via the provider API.
  3. Stores the new secret as a new version.
  4. Updates the middleware to consume the new version.
  5. Runs a canary consent-screen test.
  6. Revokes the old secret only after the canary passes.
  7. Emits an audit event.

For providers without a rotation API (Workday, HubSpot at time of writing), maintain a documented manual runbook and put the rotation on your team's quarterly compliance calendar.

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: 3600

This 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.

Enterprise Operational Checklist to Avoid the Migration Cliff

Use this as your operational playbook before, during, and after switching unified API providers. Every item maps to a real failure mode we have seen in production migrations.

Before signing the new contract

  • Confirm the new vendor supports BYO OAuth apps at both the integration level (all customers share one app) and the environment level (different apps for staging vs. production).
  • Confirm the current vendor supports full token export. Get the process in writing before signing anything with the new vendor.
  • Enumerate every third-party provider you integrate with. For each, note the refresh token rotation behavior and whether the current OAuth app is yours or the vendor's.
  • Verify your DPA and any sub-processor notifications can be updated to reflect the new vendor without customer-facing consent flows.
  • Have security review the new vendor's token storage (encryption at rest, key management, access logging, incident response).
  • If white-label OAuth or on-prem deployment is a compliance requirement, get written confirmation of the deployment model, the KMS integration options, and whether any token traffic transits the vendor's cloud.

Migration prep phase (2 to 4 weeks before cutover)

  • Register OAuth apps under your own developer accounts for every provider that does not already have one. Submit for verification or review where required - Salesforce, Google, Microsoft, and Slack often take 1 to 3 weeks. Google CASA for restricted scopes takes longer.
  • Register the new vendor's callback URLs on each OAuth app.
  • Run the consent-screen test for each provider in staging before touching production tokens.
  • Export tokens and connection metadata from the old vendor. Verify the export includes refresh tokens, scopes, and provider-specific context (instance URLs, portal IDs, tenant identifiers).
  • Set up the new vendor in a staging environment. Import a subset of test accounts and verify token refresh, unified API calls, and webhook delivery work end-to-end.
  • Build response-shape compatibility mappings (JSONata or your new vendor's transform layer) so your application code does not have to change.
  • Subscribe to the new vendor's authentication_error and reactivated webhooks. Wire them to your migration ledger.

During cutover

  • Freeze writes to the old vendor for the cutover window.
  • Bulk-import tokens and metadata into the new vendor.
  • Trigger a lightweight test read for every imported account to force on-demand refresh. Bucket results into success, needs_reauth, and other errors.
  • Flip your DNS or feature flag routing to send API traffic to the new vendor.
  • Monitor error rates, refresh success rates, and webhook delivery for at least 24 hours before declaring cutover complete.

Post-cutover reconciliation

  • Reconcile the migration ledger. Confirm the total count of active accounts matches pre-migration baseline (minus any expected re-auth cases).
  • For accounts stuck in needs_reauth, launch the in-app reconnect banner deep-linked to the specific provider.
  • Track resolution rate over 7 days. Escalate the long tail via CSM outreach.
  • Request deletion of token data from the old vendor. Get written confirmation.
  • Archive the migration audit log (who did what, when, on which accounts) for compliance evidence.

Ongoing post-migration hygiene

  • Never let a vendor register OAuth apps on your behalf, even for a new provider you add next quarter. Register the app under your account first.
  • Rotate client secrets on a schedule your compliance framework requires. Both Salesforce (Manage Consumer Details > Rotate) and HubSpot support secret rotation without invalidating existing refresh tokens, so this is low-risk to automate.
  • Monitor authentication_error webhook volume continuously. Sustained spikes are usually the first sign a provider changed their auth behavior (scope changes, rotation policy changes, or endpoint deprecations).

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.
  • Is the OAuth flow fully white-labeled? Consent screen, callback domain, and reconnect UI should all display your brand end-to-end.
  • Can the platform deploy in a customer-controlled boundary (dedicated tenant, VPC, or on-prem)? Required for regulated data and enterprise deals with strict residency requirements.
  • 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.

More from our Blog