Skip to content

How to Publish a Runnable Sample Repo for Headless vs iFrame Integrations

Cut Time to First Call (TTFC) from hours to minutes by shipping a runnable sample repo that demonstrates both headless and iFrame integration paths side-by-side.

Nachi Raman Nachi Raman · · 14 min read
How to Publish a Runnable Sample Repo for Headless vs iFrame Integrations

When engineering teams evaluate your B2B SaaS integration platform, they face an immediate architectural decision: do they drop in your pre-built embedded iFrame, or do they build a custom UI against your headless API? If they cannot quickly test both approaches in a local environment, your platform fails the technical evaluation. Providing a runnable tutorial and sample repo for headless vs iframe implementations is the highest-leverage asset your product team can ship to accelerate developer onboarding and close enterprise deals.

If you ship an integration platform, the fastest way to win developer trust is to publish a runnable tutorial and a sample repository that demonstrates both a headless API implementation and an embedded iFrame implementation side by side. One repo. Two branches. Same backend. The developer clones it, sets two environment variables, runs npm install && npm run dev, and sees a working OAuth connection to a real third-party API in under five minutes. That is the bar in 2026.

Developers do not read documentation top-to-bottom. They look for a GitHub repository, clone it, run npm install, and expect to see a working application on localhost:3000. If your integration documentation consists only of isolated curl snippets and abstract architecture diagrams, you are forcing evaluating engineers to write boilerplate authentication logic, reverse-engineer undocumented OAuth state parameters, and guess how your UI components interact with your backend.

This guide provides a concrete framework for DevRel leaders and senior product managers to compress evaluation cycles, cut support tickets, and stop losing technical buyers. We will cover how to structure the codebase, how to map out the exact differences between embedded integration UIs and custom UIs, and how to handle the painful realities of rate limits and unified webhooks directly in your code examples.

The Integration UX Dilemma: Headless API vs. Embedded iFrame

Every B2B SaaS product manager eventually hits the same crossroads when shipping customer-facing integrations. You need to connect your application to your users' third-party tools. You have two ways to expose this connection flow to your users.

Short answer: Embedded iFrames give you a working connect flow in an afternoon at the cost of UX control. Headless APIs give you total UX control at the cost of building auth, error states, scope screens, and field mapping yourself.

The first option is the embedded iFrame (commonly called a "Link UI" or "Connect SDK"). This is the fastest path to market. You drop a vendor-hosted JavaScript snippet into your frontend, and the integration provider renders a pre-built modal handling the entire OAuth flow, provider selection, scope selection, and basic field mapping. Your engineers wire up a session token, and you get a callback when the account is connected. The tradeoff is rigidity. iFrames operate in isolated browser contexts, frequently trigger third-party cookie blocking in modern browsers like Safari and Brave, and rarely match your host application's design system perfectly.

The second option is the headless API. You expose the underlying primitives—integrations list, OAuth initiation URL, callback handler, account status—as raw API endpoints. You build the entire integration catalog, connection buttons, and configuration forms natively in your own React or Vue application. This is what teams reach for when they want the connector UX to match their own design system, embed it inside an onboarding wizard, or surface custom field mapping screens that an iFrame cannot accommodate. The tradeoff is developer friction. Your engineering team must build and maintain the UI state, error handling, and callback routing.

Enterprise buyers demand the latter. Studies show that 64% of companies experience regret after choosing SaaS due to a lack of flexibility, driving the massive shift toward headless integration options. B2B SaaS customers require highly scalable, deeply embedded integration solutions due to tool sprawl. Analysis shows small firms use an average of 102 distinct SaaS applications, while mid-market companies use 137. A pre-built iFrame that cannot render your brand's typography, cannot show a help link to your own docs, and cannot inject a custom consent screen for a regulated industry creates exactly the kind of rigidity that drives buyer regret.

Evaluating architects know this. They usually start with the iFrame to prove the connection works, then immediately pivot to evaluating the headless API to see if they can build a native experience. If your documentation does not explicitly show them how to transition from the embedded UI to a custom headless UI, they will assume your platform is too rigid for their needs.

Info

The right answer is rarely "one or the other." Most mature B2B SaaS products ship the iFrame as the default for fast-moving customers and expose the headless API for enterprise tenants that need a bespoke flow. Your sample repo should reflect that reality.

Why Time to First Call (TTFC) Dictates Integration Success

Time to First Call (TTFC) is the developer experience metric that measures the elapsed time from a developer signing up for your platform to executing their first successful, authenticated API request that returns a non-error response. A long TTFC kills API adoption faster than missing features or pricing concerns.

When a staff engineer is tasked with evaluating a unified API or integration platform, they usually timebox the spike to a single afternoon. For integration platforms, the "first call" usually means listing contacts, employees, or tickets from a connected third-party account. They want to authenticate with a provider they already use, pull a list of records, and verify the data schema. If they spend that afternoon debugging invalid_grant OAuth errors, fighting CORS policies, or trying to understand how your platform handles pagination, the evaluation ends in failure.

TTFC is the most important metric you'll need for a public API, and the data on runnable examples is unambiguous. Developers spend significantly less time making their first API request when they fork a collection—they make a successful API call 1.7 to 56 times faster when using a forked collection or sample repo. In one case study, PayPal reduced their time to first call from 60 minutes to one minute and shortened testing time from hours to minutes with runnable collections. A runnable sample repo bypasses the friction of environment configuration. It provides a working baseline where the developer only has to swap out placeholder API keys for their own.

The lesson is not "publish a Postman collection." As we've detailed in our guide to developer API recipes, the lesson is that executable artifacts beat prose. A senior engineer evaluating your platform will spend the first ninety seconds scanning your docs for something they can run. If they find it, they run it. If they have to copy-paste five curl commands, edit headers, and read three OAuth pages first, they bounce.

There is a subtler trap to avoid. Be careful of artificially hacking a TTFC, perhaps by hiding away the tricky parts or ignoring the gotchas, as you may be shifting the friction to the implementation stage. A sample repo that mocks OAuth, hardcodes a fake bearer token, or papers over rate limiting will produce a beautiful first call and a painful week two. The whole point of the runnable repo is to show developers what production actually looks like.

This is why one repo with two implementations beats two separate quickstarts. The headless branch shows the developer what they would build. The iFrame branch shows them the shortcut. They get to compare both with a single auth setup and a single dataset.

Bridging the Gap: The Power of a Runnable Sample Repo

A high-converting sample repo does not force the developer to choose between the iFrame and the headless API. It demonstrates both, side-by-side, in a single application. This proves the flexibility of your architecture and collapses the headless-versus-iFrame decision from a multi-day spike into a fifteen-minute evaluation.

flowchart LR
    A[Developer clones repo] --> B[Sets API key<br>and OAuth client]
    B --> C{Picks UI Toggle}
    C -->|Embedded iFrame| D[Runs Next.js demo<br>with Truto Link SDK]
    C -->|Headless API| E[Runs Next.js demo<br>with custom connect UI]
    D --> F[Truto Unified API Engine]
    E --> F
    F --> G[Connects sandbox<br>HubSpot/Salesforce]
    G --> H[Lists contacts<br>via unified API]
    H --> I[Receives webhook<br>on contact update]

The ideal sample repo is a monorepo containing a minimal backend (Node.js/Express or Python/FastAPI) and a modern frontend (Next.js or React). The repo should ship with:

  • A single backend that handles the OAuth callback, exchanges the code for tokens, and exposes a thin /api/contacts proxy. Both UI branches hit the same backend.
  • Two UI implementations in parallel directories (/apps/iframe-demo and /apps/headless-demo) or a simple toggle switch in the same Next.js app: "View Embedded UI" vs "View Custom Native UI". Developers see that switching is a UI decision, not an architecture decision.
  • Pre-seeded .env.example with sandbox credentials for one or two providers. CRMs work well because every evaluator understands contacts.
  • A make demo or pnpm demo script that runs the entire flow end to end, including a webhook receiver bound to a public tunnel.
  • Honest error states. When the upstream returns a 429 or a 401, the UI should show the real error, not a smoothed-over toast.

This is where the underlying architecture of your integration platform becomes highly visible. Most embedded iPaaS platforms struggle with the headless approach because their internal systems are heavily coupled to their visual workflow builders. They maintain separate code paths for every integration.

Truto operates differently. Truto's runtime is a generic execution engine that takes a declarative configuration describing how to talk to a third-party API, and executes it without any awareness of which integration it is running. The entire platform contains zero integration-specific code.

For the developer building the headless UI, this is a massive advantage. Your sample repo can demonstrate how a single, generic frontend component can handle authentication and data fetching for 100+ integrations. The developer does not need to write if (provider === 'hubspot') logic in their custom UI. They write one code path that dynamically renders connection fields based on the generic metadata returned by the Truto API.

This structure also lets you produce a follow-on tutorial for Post-Connection Configuration UI Patterns for SaaS Integrations (2026 Guide) without rewriting the foundation. The same backend can power the field mapping screen, the sync schedule picker, and the disconnect flow.

The payoff compounds. Paddle's API-first strategy reduced TTFC by 50%, with the improved developer experience leading to a shorter sales cycle and faster onboarding—now onboarding takes only hours. That is the order of magnitude PMs should target when scoping this work.

Step-by-Step: Structuring Your Developer Tutorial

The accompanying tutorial—the markdown file that lives in the repo's README and on your docs site—should follow a five-section structure. Each section should have a runnable artifact next to it. Assume the developer has a locked-down corporate laptop, strict firewall rules, and zero patience for complex build steps.

1. Environment Setup (Target: 60 Seconds)

The root of your sample repo must contain a docker-compose.yml file or a simple package manager script that boots the entire stack. List exact versions (e.g., Node 20, pnpm 9). Provide a .env.example file that clearly documents exactly where the developer needs to paste their API keys.

Tip

Never ask a developer to globally install databases or caching layers just to run your tutorial. Use Docker to containerize any required state, or use in-memory SQLite for the sample backend. Avoid the "you will also need a sandbox account at Provider X, ask their sales team for access" trap. If sandbox provisioning is gated, document a workaround or ship with a mock provider in dev mode.

2. Demonstrating the Authentication Harness

Authentication is the most complex part of any integration. Your sample repo must clearly separate the frontend trigger from the backend token exchange. Show how to obtain a connection. With a unified API platform, this is a session token exchange:

// server.ts
const { token } = await trutoClient.linkTokens.create({
  end_user: { id: 'user_123', email: 'evaluator@example.com' },
  integrations: ['hubspot', 'salesforce', 'pipedrive'],
});
return Response.json({ token });

The iFrame branch uses that token to render a connect modal. The headless branch uses it to call the OAuth initiation endpoint directly, capture the state parameter, handle the callback route, and exchange the authorization code itself. Because Truto handles the automated token refreshes proactively behind the scenes, your sample repo code remains incredibly clean. The developer only needs to store the Truto integrated_account_id in their local database, mapped to their internal user_id.

3. The First Call

This is where TTFC is won or lost. The example should be one function:

const contacts = await trutoClient.unified.crm.contacts.list({
  integrated_account_id: accountId,
  page_size: 50,
});
console.log(`Pulled ${contacts.data.length} contacts`);

No polymorphic field discovery. No manual pagination cursor handling on the first call. Get the developer to a 200 OK and a list of records. Then layer in complexity.

4. Writing Data Back

Show a POST next. Reads are easy. Writes expose every quirk of the upstream API—required fields, validation rules, custom objects. A sample repo that only reads is a sample repo that lies. Include a create contact example that handles the 422 response when a required field is missing.

5. Subscribing to Changes

Wire up one webhook receiver. Show the unified event shape. Demonstrate signature verification with a real HMAC check, not a TODO comment. If you skip this section, every developer who clones your repo will reinvent it badly.

For a deeper treatment of how to structure the tutorial document itself, see our guide on How to Publish End-to-End Developer Tutorials with Runnable API Examples.

A detail that matters more than it sounds: pick one provider for the headline path, but make sure the same code works against a second provider with only an integration ID change. This is the proof point for any unified API. If your sample only ever talks to HubSpot, evaluators will assume the abstraction leaks.

Handling the Hard Parts: Rate Limits, Webhooks, and GraphQL

A sample repo that only shows the "happy path" is entirely useless for an enterprise evaluation. Senior engineers want to know what happens when the system fails. This is the section every other tutorial skips. Do not skip it. Your code must explicitly demonstrate how to handle rate limits and asynchronous webhooks.

Architecting the Retry Interceptor

Many integration platforms obscure rate limit errors, resulting in silent failures or unpredictable latency spikes when background workers retry requests endlessly. Radical honesty wins technical evaluations.

A serious sample repo must demonstrate retry logic. Truto does not magically absorb rate limit errors. When an upstream API returns an HTTP 429 Too Many Requests, Truto passes that error directly back to the caller. That design is intentional: the caller knows whether the request is idempotent, whether the user is waiting, and whether to fail fast or queue.

However, Truto normalizes the chaotic upstream rate limit information into standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset) following the IETF specification. Your sample repo must include a global HTTP interceptor (using Axios or Fetch) that catches 429 errors, reads the ratelimit-reset header, and applies an exponential backoff with jitter before retrying.

async function withRetry<T>(fn: () => Promise<Response>, max = 5): Promise<T> {
  for (let attempt = 0; attempt < max; attempt++) {
    const res = await fn();
    if (res.status !== 429) return res.json();
    const reset = Number(res.headers.get('ratelimit-reset') ?? 1);
    const jitter = Math.random() * 500;
    await new Promise(r => setTimeout(r, reset * 1000 + jitter));
  }
  throw new Error('Rate limited after retries');
}

Because the headers are normalized, the same interceptor works whether the underlying provider is HubSpot, Salesforce, or NetSuite. Showing this interceptor in your sample repo proves that you understand production realities. It gives the developer copy-pasteable code for handling one of the hardest parts of API integration. For a deeper understanding of this pattern, review Best Practices for Handling API Rate Limits and Retries Across Multiple Third-Party APIs.

Unified Webhook Ingestion

Webhooks from third-party providers are notoriously inconsistent. Some send nested JSON (Salesforce), others send flat form data or arrays (HubSpot). Some sign payloads with HMAC-SHA256, others use asymmetric RSA keys, and some send flat objects with snake_case keys (Pipedrive).

If a developer is building a custom headless integration, they dread having to write 50 different webhook handlers. Your sample repo must demonstrate how Unified Webhooks solve this. Truto uses JSONata-based configuration for provider-specific event normalization to expose a single contract so your sample repo can ship one handler:

app.post('/webhooks/truto', async (req, res) => {
  if (!verifySignature(req.headers['x-truto-signature'], req.rawBody)) {
    return res.status(401).end();
  }
  const { event_type, integrated_account_id, data } = req.body;
  switch (event_type) {
    case 'record:created':
    case 'record:updated':
      await upsertContact(integrated_account_id, data);
      break;
  }
  res.status(200).end();
});

In your sample backend, expose this single POST /api/webhooks/truto route. Show how this single endpoint receives a normalized, standardized event payload, regardless of whether the original event came from Salesforce, BambooHR, or Jira. Demonstrate how to verify the single X-Truto-Signature header to secure the endpoint. Show idempotency by storing the event ID. Be honest about delivery guarantees—at-least-once means handlers must be safe to run twice. This "aha" moment—realizing they only need to write one webhook handler for their entire application—is often the exact moment an architect decides to buy your platform.

Normalizing GraphQL and REST

One of the most frustrating aspects of building a custom headless UI is dealing with the differing architectures of third-party APIs. Some providers (like Zendesk) use REST. Others (like Linear, Monday, Shopify) use complex GraphQL schemas.

Developers do not want to learn three query languages to evaluate your platform. Your sample repo should demonstrate how to fetch data from both types of providers using a single pattern. Truto's GraphQL to REST proxy API allows developers to treat complex GraphQL APIs as simple CRUD REST resources. In your sample code, you can show a single GET /crm/contacts request that successfully pulls data from a REST provider, and an identical request that pulls data from a GraphQL provider, with the proxy layer handling the translation automatically. This proves to the evaluating architect that your headless API genuinely abstracts away provider-level complexity. Read more about this mechanism in our deep dive on Converting GraphQL to REST APIs: A Deep Dive into Truto's Proxy Architecture.

The Tradeoffs: Maintenance and Provider Drift

The honest read on the headless-versus-iFrame question is that you should ship both, document both, and let the evaluator pick. A sample repository with two parallel implementations against a shared backend is the highest-leverage artifact a DevRel team can publish, because it answers the buyer's question without requiring a sales call.

However, you must keep your eyes open about the trade-offs:

  • Maintenance cost. Two UI implementations means two surfaces to keep current when the underlying API evolves. Budget for it.
  • Provider drift. Sandbox accounts at HubSpot, Salesforce, and Pipedrive expire, throttle, and change auth flows. CI against live sandboxes is non-negotiable. Treat the repo as production code, not a one-time marketing asset.
  • The abstraction's limits. Unified APIs are a productivity multiplier, not a magic wand. The sample repo should be honest about where the unified model ends and passthrough begins. A short "escape hatch" example—making a raw upstream call through the proxy layer for a field the unified schema does not cover—earns more trust than pretending the abstraction is complete.

Stop Forcing Developers to Guess

Publishing a reference page and hoping developers figure out the implementation details is a failing strategy. The technical evaluation is won or lost in the first five minutes of interaction.

By providing a runnable sample repo that explicitly contrasts embedded iFrame UIs with custom headless implementations, you respect the architect's time. You acknowledge the real-world complexities of rate limits, pagination, and token management, and you provide working code that solves them.

The ROI on this work is direct. Faster TTFC means more activated accounts, fewer support tickets, and shorter enterprise sales cycles. The cost is two engineers for two weeks. The competitive landscape—Postman, Paragon, Appmixer, and every other DX-forward platform—has set the expectation. Match it or watch your evaluators churn.

A runnable tutorial turns a theoretical API reference into a practical, high-converting developer onboarding tool. It moves the conversation away from "Can this platform do what we need?" to "How quickly can we push this to production?"

FAQ

What is Time to First Call (TTFC) and why does it matter for integration platforms?
TTFC is the elapsed time between a developer signing up for your service and executing their first successful authenticated API call. For integration platforms, it predicts adoption: developers who hit a 200 OK within minutes are dramatically more likely to integrate than those who spend hours wiring up OAuth from scratch. Postman's research shows that runnable collections cut TTFC by 1.7x to 56x compared to prose docs.
Why should a sample repo include both headless and iFrame options?
It caters to different enterprise buyer needs, allowing evaluating architects to test the speed of an embedded UI against the total control of a custom headless implementation. A single sample repo with both implementations sharing one backend lets evaluators pick without a sales call.
How do you handle third-party API rate limits in a sample repo?
Demonstrate retry logic explicitly. Provide a generic HTTP interceptor that reads standardized rate limit headers (like ratelimit-reset) and applies exponential backoff with jitter, rather than hiding the 429 errors. Hiding rate limiting in the demo just shifts the pain to week two of implementation.
What should go in a developer tutorial alongside the sample repo?
A five-section structure works well: environment setup, authentication (session token plus OAuth), the first read call, a write call that exposes upstream validation quirks, and a webhook handler with real signature verification. Each section should have a runnable artifact, not just prose.
How do you keep a multi-provider sample repo from breaking when upstream APIs change?
Run CI against live sandbox accounts on a daily schedule and treat sandbox provisioning as part of your release pipeline. A unified API layer that abstracts pagination, auth, and rate limit headers reduces the surface area that changes, but does not eliminate it.

More from our Blog