Make (formerly Integromat) is great at stitching APIs together. The trouble starts when you try to involve a browser.

A lot of developers assume this flow will work:

  • frontend app calls API directly
  • Make scenario orchestrates some backend steps
  • browser reads the response and moves on

Then CORS shows up and ruins the afternoon.

The core problem: Make runs server-to-server just fine, but browsers enforce CORS and Make does not magically bypass that for your frontend. If your app calls an API from the browser, the API still needs the right CORS headers. If your app calls a Make webhook from the browser, that webhook also needs to behave in a browser-friendly way.

Here’s the practical comparison guide I wish more teams read before building a Make-based integration.

The 4 common integration patterns

1. Browser → Third-party API directly

This is the cleanest setup when it works.

Your frontend sends fetch() requests straight to the vendor API. No Make in the request path. Make might still run separately for background automation.

Pros

  • Lowest latency
  • Simple architecture
  • No extra relay layer to maintain
  • Browser gets the response directly

Cons

  • Vendor API must support CORS
  • Browser-safe auth only; no secret API keys
  • Preflight requests can break things
  • Rate limits hit from end users directly

This works well for public APIs or OAuth-based APIs designed for SPAs.

GitHub is a good real-world example of a browser-friendly API. Its response includes:

access-control-allow-origin: *
access-control-expose-headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning

That access-control-allow-origin: * means browsers can read the response from any origin, assuming credentials aren’t involved. The exposed headers are also a nice touch. If you want your frontend to read Link for pagination or X-RateLimit-Remaining, those headers must be explicitly exposed.

Example:

const res = await fetch('https://api.github.com/repos/octocat/Hello-World');
const data = await res.json();

console.log(res.headers.get('ETag'));
console.log(res.headers.get('X-RateLimit-Remaining'));
console.log(data.full_name);

Without access-control-expose-headers, some of those headers would be invisible to browser JavaScript even though they exist on the network response.

2. Browser → Make webhook → Third-party API

This is the pattern people reach for when the target API has bad CORS or needs secret credentials.

The idea is simple:

  • browser sends request to Make webhook
  • Make scenario calls the third-party API server-side
  • Make returns the result

This can work, but there are tradeoffs.

Pros

  • Hides API secrets from the browser
  • Avoids third-party CORS limitations
  • Lets you normalize ugly APIs
  • Easy to add logging, branching, retries

Cons

  • Added latency
  • More moving parts
  • Webhook responses are not a full replacement for a custom backend
  • OPTIONS/preflight handling may be awkward depending on the webhook setup
  • Debugging browser CORS against middleware is annoying

The biggest gotcha: your browser is no longer talking to the vendor API. It’s talking to Make. So the Make endpoint must satisfy browser CORS rules.

If your frontend sends JSON, custom headers, or credentials, the browser may send a preflight:

OPTIONS /webhook/abc123
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, authorization

If the Make webhook doesn’t answer that preflight correctly, the browser blocks the real request before your scenario even runs.

Typical browser code:

await fetch('https://hook.make.com/your-webhook-id', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    email: '[email protected]',
    event: 'signup'
  })
});

That innocent Content-Type: application/json is enough to trigger preflight in many cases.

My opinion: this pattern is fine for low-volume internal tools, prototypes, and lightweight public forms. I don’t love it for core product flows where you need tight control over headers, auth, and response behavior.

3. Browser → Your backend → Make and other APIs

This is the boring architecture, which is why it’s usually the right one.

Your backend becomes the browser-facing API. It handles auth, CORS, validation, caching, and policy. It can trigger Make when automation is useful, but Make is not your public API surface.

Pros

  • Full control over CORS
  • Better security model
  • Easier auth and session handling
  • More predictable response contracts
  • Better observability and error handling

Cons

  • You have to build and maintain a backend
  • More engineering work up front
  • Another deployable component

If I’m building something customer-facing, this is the route I trust most.

Minimal Express example:

import express from 'express';
import cors from 'cors';
import fetch from 'node-fetch';

const app = express();

app.use(express.json());
app.use(cors({
  origin: 'https://app.example.com',
  methods: ['GET', 'POST', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  exposedHeaders: ['ETag', 'Link'],
  credentials: true
}));

app.post('/api/enrich', async (req, res) => {
  const makeRes = await fetch('https://hook.make.com/your-webhook-id', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(req.body)
  });

  const text = await makeRes.text();
  res.set('Cache-Control', 'no-store');
  res.send(text);
});

app.listen(3000);

Now you own the CORS policy instead of hoping every downstream service behaves nicely.

4. Make as background automation only

This is the least painful option.

Your app talks to your backend or directly to browser-friendly APIs. Make handles:

  • syncing CRM records
  • sending Slack alerts
  • enriching data after the fact
  • moving files between systems
  • scheduled jobs

Pros

  • No browser CORS dependency on Make
  • Cleaner separation of concerns
  • Better reliability for user-facing flows

Cons

  • Not suitable if the browser needs immediate direct response from Make
  • Requires async thinking and event-driven design

Honestly, this is where Make shines. I like Make far more as an automation engine than as a browser-facing middleware layer.

CORS-specific comparison

Direct API calls

Best when the API already supports browser use.

Look for:

  • access-control-allow-origin
  • access-control-allow-methods
  • access-control-allow-headers
  • access-control-expose-headers

And if cookies or auth sessions are involved:

  • no wildcard origin with credentials
  • access-control-allow-credentials: true

Make webhook relay

Best when you need secret handling and light orchestration, but only if the webhook plays nicely with browser requests.

Look for:

  • preflight support for OPTIONS
  • consistent response headers
  • no assumptions about opaque redirects or non-JSON responses

Custom backend

Best when the frontend matters and you need predictable behavior.

You can also combine this with broader header hardening. If you’re cleaning up your edge layer anyway, it’s worth reviewing CSP and related headers too. csp-guide.com is useful for that side of the stack.

Common failures with Make integrations

“It works in Postman”

Of course it does. Postman doesn’t enforce browser CORS.

“The webhook returns 200 but fetch still fails”

That usually means the browser blocked access to the response because the CORS headers were missing or wrong.

“I can see the header in DevTools but JS can’t read it”

Then it probably wasn’t listed in Access-Control-Expose-Headers.

“We added * and it still fails”

Wildcard origin does not work with credentialed requests. If you send cookies or credentials: 'include', you need a specific origin.

How I test this stuff

I test three things separately:

  1. The actual browser request
  2. The preflight OPTIONS
  3. Which response headers are readable in JavaScript

A simple curl preflight check:

curl -i -X OPTIONS 'https://example.com/api' \
  -H 'Origin: https://app.example.com' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: content-type, authorization'

If you want a quicker sanity check on header behavior, I’d use HeaderTest. It’s handy when you want to verify what a response is actually returning before blaming your frontend code.

My recommendation

If you’re choosing between these patterns for Make integrations:

  • Use direct browser calls when the API is intentionally browser-friendly.
  • Use Make as a relay only for simple cases where a little extra latency and CORS friction are acceptable.
  • Use your own backend for serious product flows, authenticated apps, and anything customer-facing.
  • Use Make in the background whenever possible. That’s where it earns its keep.

The mistake I see most often is treating Make like a drop-in replacement for an application backend. It isn’t. It’s excellent at automation. It’s mediocre as your public browser API boundary.

If your integration starts in the browser, design for browser rules first. CORS doesn’t care how elegant your Make scenario looks.