Building Integrations Yourself vs. Using a Unified API: A Developer's Guide
March 9, 2026
Updated June 2026
Most teams don't choose between building integrations and using a unified API. They choose between shipping product and running an integration maintenance business.
That's the real trade-off. If you only need one or two shallow integrations, the maintenance load is bounded and building in-house can make sense. If your product needs to support multiple customer systems, move quickly, and avoid turning integrations into a permanent engineering tax, a unified API is usually the better choice.
This guide breaks down what building integrations yourself actually involves at the code level, where the hidden costs live, and how modern unified APIs change the architecture.
What "building your own integration" actually means
Building an integration yourself means your team owns every layer of the integration stack:
- Authentication and OAuth flows
- Token storage and refresh logic
- Rate-limit handling
- Retries and backoff
- Pagination
- Data mapping
- Webhook ingestion
- Schema changes
- Monitoring and debugging
- Customer-specific edge cases
That's true whether you're integrating with HubSpot, Salesforce, QuickBooks, Workday, or Slack. The first API call is usually the easy part. Production reliability is the hard part.
Here's what the minimum viable OAuth refresh wrapper looks like for a single integration — before you've handled rate limits, webhooks, schema mapping, or any of the other concerns above:
async function getValidAccessToken(connectionId: string): Promise<string> {
const connection = await db.connections.findById(connectionId);
if (connection.expiresAt > Date.now() + 5 * 60 * 1000) {
return connection.accessToken;
}
// Token is expired or about to expire — refresh it
const lock = await acquireLock(`refresh:${connectionId}`);
try {
// Re-check after acquiring lock (another worker may have refreshed)
const fresh = await db.connections.findById(connectionId);
if (fresh.expiresAt > Date.now() + 5 * 60 * 1000) {
return fresh.accessToken;
}
const response = await fetch(VENDOR_TOKEN_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: connection.refreshToken,
client_id: process.env.VENDOR_CLIENT_ID!,
client_secret: process.env.VENDOR_CLIENT_SECRET!,
}),
});
if (!response.ok) {
// Some vendors return JSON. Some return HTML on token expiry.
// Some rotate refresh tokens; some don't. Handle vendor-specific quirks here.
throw new Error(`Refresh failed: ${response.status}`);
}
const tokens = await response.json();
await db.connections.update(connectionId, {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token ?? connection.refreshToken,
expiresAt: Date.now() + tokens.expires_in * 1000,
});
return tokens.access_token;
} finally {
await releaseLock(lock);
}
}
This handles one vendor. Your product probably needs to connect to a dozen, and each one has different refresh behavior, error formats, and edge cases. The code above also assumes you already have lock infrastructure, an encrypted token store, and per-tenant scoping. None of that is free.
The mental shift this guide is really about:
Without a unified API:
- Build OAuth per vendor
- Handle refresh + race conditions
- Normalize schemas per vendor
- Debug vendor-specific error formats
- Repeat for every new integration
With a unified API:
- Create a connection
- Store the
connection_id - Call
/crm/contacts
That compression is what the rest of this guide explains.
Why OAuth is fragmented at scale
OAuth is not a standard at scale. It's a family of incompatible implementations.
Across the integrations Unified supports, we've found and handle more than 70 variations of the OAuth 2.0 "standard." Engineering time isn't spent implementing OAuth — it's spent normalizing edge cases forever:
- One vendor uses
contacts.read; another usesread_people; a third usesread_contacts - One vendor expires refresh tokens silently; another rotates them on every use
- One returns structured JSON errors; another returns an HTML page on an expired token
- Refresh strategies differ: refreshing the access token, refreshing the refresh token, time-based vs. activity-based expiries, re-consent windows
Of Unified's 460+ integrations, 176 use the OAuth2 authorization code flow and 29 use client credentials. Multiply 70+ variations across dozens of integrations and OAuth becomes a permanent operational cost, not a one-time engineering investment.
That's why teams building integrations directly often end up building an internal auth abstraction layer before they've shipped the actual feature.
The race condition problem most teams don't see until production
Here's a failure mode that doesn't show up in your test suite: concurrent token refresh.
Without protection, two API requests can arrive at the same time, both notice the token is expired, both fire refresh requests, and end up overwriting each other. For vendors that rotate refresh tokens on every use, the second request invalidates the first one's new refresh token. The connection breaks silently.
You only see this in production, at scale, and only intermittently. Debugging it from your application logs is painful because the symptom — "user disconnected, please reconnect" — happens hours after the actual failure.
The fix is locking:
- Single instance: in-memory locks (mutex, semaphore)
- Distributed systems: Redis-based locking (Redlock) or equivalent
Either pattern requires that only one refresh runs per connection at any given time, with all other requests waiting for completion. The OAuth boilerplate above shows this pattern, but most teams discover the race condition the hard way before they add the lock.
Multiply this across every integration. Every vendor whose tokens rotate. Every retry handler that might trigger a refresh. The surface area for race conditions grows linearly with integration count.
What debugging looks like when integrations break
When a request fails, you need to answer:
- Is the access token expired?
- Did the vendor change something on their side?
- Is this customer's connection configured differently from another's?
- Did the webhook fail?
- Is the schema mapping wrong?
- Is the API temporarily rate-limiting us?
Each vendor surfaces these differently:
- HubSpot: structured JSON errors but inconsistent status codes — 400 for failures that should be 422, similar failure modes mapped to different codes
- Salesforce: mixed XML/JSON depending on endpoint generation, expired-token responses vary by API version
- Workday: SOAP faults with vendor-specific error codes that map weakly to HTTP semantics
The pattern: every integration invents its own debugging vocabulary. Each new vendor adds another error format your team has to learn, another set of edge cases to map back to a coherent internal failure model.
The result: integrations start as roadmap items and end up as recurring operational work.
The mental model shift: one connection ID
Before getting to mechanics, here's the architectural shift that matters most.
With normalized OAuth, you stop tracking per-vendor tokens, scopes, and refresh cycles in your own code. You hold a single connection ID.
That connection ID is the only artifact your application needs. It works for every API call, for webhook creation, and for MCP access — for the life of the connection. Token exchange, refresh, expiry, and vendor-specific quirks happen behind it. Your application's job is reduced from "manage OAuth for each integration" to "hold an ID and make calls."
This is the right way to think about authentication at scale: the integration layer owns the auth complexity, and your code owns a reference to it.
For deeper detail on how this is implemented, see how we normalize OAuth across 460+ APIs and the OAuth2 authorization flows guide.
Where traditional unified APIs still fall short
Not all unified APIs are equal. Many older or first-generation unified APIs rely on:
- Batch syncs on a schedule (hourly, daily)
- Cached data served from the unified API's own database
- Shallow common models that don't surface vendor-specific fields
That architecture has concrete consequences for the buyer:
- Your AI agent acts on stale state. Reads return what was synced hours ago, not what's in the source system now. An agent reasoning on outdated data writes back to a reality that no longer exists.
- Your workflows are delayed by hours. Actions take effect on the next sync interval, not immediately. Real-time use cases break.
- You inherit data storage liability you didn't need. Every cached customer record is data your unified API vendor stores at rest — expanding your SOC 2, GDPR, HIPAA, CCPA, and PIPEDA compliance scope.
So the real choice for most modern SaaS products isn't "build vs. unified API." It's "build it yourself vs. use a real-time, pass-through unified API architecture."
The architectural difference matters more than the abstraction itself. Pass-through vs. sync-based unified APIs covers the trade-offs in depth.
Why Unified.to handles this differently
Unified.to is built as a real-time integration infrastructure layer, not a convenience wrapper. The architectural choices map to three outcomes the others miss.
Real-time architecture for AI and automation workloads
Every request hits the source API live. No cached snapshots, no sync lag, no stale data. For products that rely on current customer data — AI agents, automation, analytics — this is a structural advantage over sync-and-store models.
22,566 callable MCP tools across 460+ integrations, accessible to Anthropic, OpenAI, Google Gemini, and other MCP-compatible clients. AI agents that read, reason, and write back operate on consistent, current source-system state — not cached snapshots that may have changed since the read.
Reduced compliance scope by architecture, not after-the-fact controls
Unified doesn't store end-customer data. That reduces compliance scope, data-at-rest risk, and audit complexity by design. The five-cert compliance footprint — SOC 2 Type II, GDPR, CCPA, HIPAA, and PIPEDA — comes from the architecture, not from compensating controls added later.
For teams in regulated industries, this is the difference between a yes/no procurement question and a multi-month security review.
Operational reliability without building internal tooling
The infrastructure most teams end up building around their integrations — observability, health monitoring, error standardization, log retention — comes built in:
- Connections are pre-tested before handoff. When a connection is created, Unified tests it against the requested permissions. If it doesn't have the access you asked for, a 403 is returned and the connection is not created. Your application never receives a broken connection.
- Connection health surfaces as events, not polling. Set a notifications webhook URL in your workspace, and your backend receives a POST on
WEBHOOK_UNHEALTHYevents covering auth failures, failed token refreshes, and failed reads. - Standardized error surfacing. Every OAuth failure surfaces as a consistent error object: 401 for expired or revoked tokens, 403 for missing scope, 400 for invalid configuration. One known set of error responses across all integrations.
- Centralized log infrastructure. API call logs retained 60 days, accessible in the dashboard and programmatically via the Admin API. Customers needing longer retention can stream logs to Datadog, Grafana, or ClickHouse.
- Read and write across categories. Create records, update statuses, trigger flows, push data back into customer systems — not just read-only sync.
Head-to-head: building yourself vs. using a unified API
| Category | Build in-house | Use a real-time unified API |
|---|---|---|
| Initial build time | Slow, integration-by-integration | Faster, build once per category |
| OAuth and token lifecycle | You own all 70+ variations | Handled centrally |
| Schema mapping | You write and maintain it | Normalized for you |
| Race conditions | You discover them in production | Handled internally |
| Maintenance | Ongoing and cumulative | Reduced significantly |
| Real-time access | Possible, but you build the architecture | Pass-through by default |
| Debugging | Fragmented across vendor error formats | Centralized, standardized |
| Scaling to many integrations | Each new vendor multiplies complexity | Adding new vendors is config-only |
| Compliance scope | Larger if you store or sync data | Smaller with no-storage architecture |
| Customization | Maximum control | Depends on vendor depth |
| Best fit | Few deep integrations | Broad, scalable integration support |
When you should still build in-house
Build in-house when all three are true:
- You only need a few integrations
- Those integrations are unusually deep or custom — you need vendor-specific features the unified abstraction won't surface
- You're willing to own the maintenance long-term, including OAuth refresh quirks, schema drift, and the race conditions you'll find in production
If even one of those stops being true, the economics usually shift.
When a unified API is the better choice
Use a unified API when:
- Integrations are important, but not your core moat
- Your customers use many different systems in the same category
- You need to move fast
- Your engineering team should stay focused on product differentiation, not OAuth refresh wrappers
- You want predictable scaling across categories
- You care about real-time access and lower compliance overhead
That's the reality for most B2B SaaS products.
What this looks like in practice
Timothy S., Senior Engineer at Humi, on Unified's connection observability: "I am very impressed with Unified. Their team is highly responsive, and their support is top-notch. Their dashboard is powerful and allows us to easily observe the health of our connections."
Mike K., Co-founder at Mycroft: "Our team has been able to build and integrate with 15 products on an Alpha-stage product and we expect to be launching with over 150 integrations for our public launch. This is a truly unified API platform, not a pretty UI passthrough where you'd still end up building the same integrations yourself."
The pattern across customer teams: integration work that used to take weeks per vendor compresses into the time it takes to add a connection.
The practical decision framework
Cut through the abstract trade-offs with these directional rules:
- More than 5 integrations needed in 12 months? Strongly consider a unified API. Building one by one becomes a drag on product velocity past this threshold.
- Real-time reads or writes required? Eliminate sync-based vendors immediately. The architecture won't change to fit your use case.
- AI agents reading and writing across customer systems? Real-time pass-through is structural, not a feature comparison. Stale state breaks agentic workflows.
- Integrations are a supporting feature, not the core product? Buy. Don't let them consume the roadmap.
- Customers in regulated industries (HIPAA, GDPR, PIPEDA)? No-storage architecture cuts compliance scope substantially. This is a procurement-grade requirement, not a nice-to-have.
If most of these point the same direction, the decision is usually clear. If your situation puts you on the "build in-house" side of all five, you probably already know.
Common concerns
A sophisticated reader will push on three objections. Brief honest answers:
Cost at scale. Usage-based pricing scales with demand, but at high volumes the question becomes whether the platform fee exceeds the fully-loaded engineering cost of building and maintaining the same surface in-house. Industry benchmarks put production-grade integrations at $50K–$150K in year one and $25K–$70K ongoing per integration. At 20+ integrations, the math usually still favors buying. Run your own numbers against your actual integration count.
Vendor lock-in. Lock-in is real but bounded. With Unified specifically, you can export connection credentials and migrate at any time, and integration logic lives in your codebase rather than in a proprietary workflow DSL. The lock-in is the time invested in mapping your domain model to the unified schema — not the data or credentials themselves.
Abstraction limits. Unified data models cover the common case across vendors. When you need vendor-specific fields or endpoints, custom-field passthrough via the raw. prefix and the metadata API let you access the underlying data without abandoning the abstraction. Evaluate this against your specific use cases before committing.
Final thoughts
Building your own integrations gives you control. It also gives you every problem that comes with that control — the 70+ OAuth variations, the race conditions in production, the fragmented error formats, the debugging vocabulary you have to learn per vendor.
For a small number of highly specific integrations, that can still be the right trade-off.
For most SaaS products, the better move isn't to keep rebuilding integration infrastructure internally. It's to use a real-time unified API that lets your team launch faster, scale broader, and spend less time on third-party API plumbing.
The alternative to building integrations in-house: one real-time unified API across the tools your customers already use.
Start your 30-day free trial →
Related reading:
- Building Integrations In-House: When It's Worth It, When It Isn't — the buyer-audience build-vs-buy guide
- What Is a Unified API? — the architectural pillar guide
- Best Unified API Platforms of 2026 — category comparison