Multi-tenant authentication for your API

How to make your backend API multi-tenant?

Work out which customer every call belongs to, from their login or API key, and pass that to your code so it only ever touches that customer’s data.

Filtering by customer is easy. Knowing for sure which customer sent each call is the hard part. Here’s why, and the easy way to solve it.

Skip to the one-command way
1 Why it’s hard

Filtering by customer is easy. Knowing the customer isn’t.

Every query your code runs has one blank in it: which customer is this call from? Filling it in safely takes all of this, on every request:

// the easy part: one line in every query

SELECT * FROM bookingsWHERE customer =?

The hard part: what goes in that blank, on every call?

  1. Their login

    Each customer may sign in with their own Google, Okta or Auth0. Every one of those tokens has to be checked.

    checked

  2. Their API key

    Tie every key to one customer, and look it up on every call, fast, wherever your API runs.

    looked up

  3. An ID the caller sends

    A header, the URL or a field in the body. Whoever is calling can type anything there.

    never trust it

And around it, on every call

  • Before any of your code runs

    And from something the caller can’t change.

  • Handed to your code safely

    So it can trust the customer without checking the token again.

  • Each customer runs their own people

    Their users, admins and keys, instead of emailing you.

So most teams let the caller say who they are, and trust it

A tenant ID in a header or the URL is quick to build. But the caller controls it, so any customer can ask for any other customer’s data.

A backend that trusts the caller

One edited word. Another customer’s data.

  1. 1Gino and Nino both run restaurants on your API
  2. 2Your backend reads the customer from a header the caller sends
  3. 3Gino changes “gino” to “nino” in that header
  4. 4Back come all of Nino’s bookings
  5. 5Nothing in your logs looks wrong

Every customer is one edit away from every other customer.

Gino’s app calls your backend

1The call Gino’s app normally makes

GET /bookings

X-Tenant-Id: gino

200 OK · Gino’s own 2 bookings

[ "Table for 2", "Table for 6" ]

2The same call, one word changed

GET /bookings

X-Tenant-Id: gino nino

edited by Gino

200 OK · Nino’s 42 bookings, sent to Gino

[ "Chiara, table for 4", "Marco, table for 2",
  … 40 more of Nino’s bookings ]

Why it worked: your backend never checks that “nino” is who is really calling. It just looks up whichever customer the header names.

There’s a shorter way.
2 The shortcut

Make your API multi-tenant with one command

APIblaze fills in that blank on every call, and does everything around it. Point it at your API, then add your customers as tenants:

Put APIblaze in front of your API

$ npx apiblaze@latest create --target https://ninopizzas.com/openapi.yaml

Paste it in a terminal. No sign-up, no servers. The link is a sample restaurant API; swap in your own.

How can it know the customer when your backend can’t?
3 Why it works

APIblaze is an API proxy: every request passes through it first

It sits between your customers and your backend. Every call goes to APIblaze, which checks the login or key and only then passes the call on, with the customer attached.

Callers

  • Chiara, with Nino’s loginasks to list bookings
  • Gino’s app, claiming Nino’sasks to list bookings
  • A caller with no login or keyasks to list bookings

APIblaze

The API proxy

Reads the login or key on every call, works out the customer, and tells your backend in a header nobody can fake.

No customer: refused

Your API

Only sees the calls APIblaze lets through.

Never reached your code.

That’s why it’s the right place to decide the customer. It verifies the credential itself, so the answer comes from the login or key, never from anything the caller typed. Here it is for two restaurants:

Chiara

signs in with Nino’s login

client_id: nino-web

Token issued for Nino’s

tenant: nino

GET /bookings

X-Abz-Tenant-Id: nino

Gino’s app

sends a key made on ginopizzas.com

X-API-Key: sk_prod_…

Key belongs to Gino’s

tenant: gino

GET /bookings

X-Abz-Tenant-Id: gino

Gino’s appfake

claims to be Nino’s

X-API-Key: sk_prod_…

X-Abz-Tenant-Id: ninodropped

Fake header dropped, key belongs to Gino’s

tenant: gino

GET /bookings

X-Abz-Tenant-Id: gino
Here is what setting it up looks like.
4 How to do it

Your API, multi-tenant, step by step

  1. 1

    Add each customer as a tenant

    Every step is one command, so your signup code can add a new customer the moment they pay.

    Create their tenant. Gino’s gets a tenant, and a portal at gino.portal.apiblaze.com.

    $ npx apiblaze@latest tenant create --name gino

    Let them use your API. They get a sign-in on their portal straight away. Swap in their own login provider any time.

    $ npx apiblaze@latest tenant attach myapi --tenant gino

    Make their manager the admin. From then on, Gino adds his own staff and makes his own keys.

    $ npx apiblaze@latest admins add gino@ginopizzas.com --tenant gino

    Or send a first key yourself. Every call made with it reaches you as X-Abz-Tenant-Id: gino.

    $ npx apiblaze@latest apikeys mint --tenant gino
  2. 2

    Each customer gets their own way in

    Everything that decides who they are, and who works there, is theirs alone.

    Their own sign-in

    Their Google, Microsoft, Okta, Auth0 or any OAuth client. Signing in there means signing in as them.

    Their own API keys

    Made on their site with the API-key widget, or by you. Revoke one without touching anyone else.

    Their own users and groups

    Managers, waiters, guests. Your rules check them on every call.

    Their own admins

    Their manager adds people and makes keys, so you aren’t the help desk.

    Their own developer portal

    Docs and keys at nino.portal.apiblaze.com, or on their own domain.

    Their own login page

    Under their own name, like login.ninopizzas.com.

  3. 3

    Your backend reads one header

    No token checks or key lookups in your code. Read the header and filter by it.

    What arrives at your backendGET /bookings

    • X-Abz-Tenant-Idnino

      Which customer. Taken from the login or key.

    • X-End-User-Idchiara@ninopizzas.com

      Which person at that customer.

    • X-Abz-IdentityeyJhbGciOi…

      Both of the above, signed, if you want proof.

    server.js
    app.get('/bookings', (req, res) => {
      // set by APIblaze from the login or key
      const tenant = req.get('x-abz-tenant-id');
      res.json(db.bookings.where({ tenant }));
    });

    A caller can’t fake the customer. APIblaze removes any X-Abz header a caller sends and adds its own.

  4. 4

    Every customer calls the same address

    The login or key already names the customer, so nothing in the address says who is calling, and there is nothing in it to change or forge.

    https://myapi.abz.run

    Chiara, signed in with Nino’s login→ X-Abz-Tenant-Id: nino

    Gino’s app, with its key→ X-Abz-Tenant-Id: gino

Common questions

What is a multi-tenant backend?

One backend that serves many customers, each kept to their own users, keys and data. Each customer is a tenant.

What is an API proxy?

A service that sits in front of your API. Every request goes through it first, so it can check the caller’s login or key and work out which customer they belong to before your code runs. APIblaze is an API proxy.

How does APIblaze know which customer is calling?

From the credential. A login token was issued for one customer, and an API key belongs to one customer, so every call names its own tenant. APIblaze sends it to your backend as X-Abz-Tenant-Id.

Do I need the customer in the URL?

No. Every customer calls the same address, like myapi.abz.run. The tenant comes from the login or key, never from the address.

Can a caller pretend to be another customer?

No. APIblaze removes any X-Abz-Tenant-Id a caller sends and sets it from the verified login or key.

Can each customer use their own login provider?

Yes. Each tenant has its own sign-in: their Google, Microsoft, Okta, Auth0 or any OAuth client. Tokens from it are tagged with that tenant.

Can one big customer get their own backend?

Yes. Give them a project of their own, pointed at their server, with its own limits and spend cap.

How am I billed?

Per request, from one wallet for your team, with no subscription.

Prices are on the pricing page.

Make your backend API multi-tenant today

One command, no sign-up. Every call tells your backend which customer it is for.

$ npx apiblaze@latest create --target https://ninopizzas.com/openapi.yaml
Using an AI coding assistant? Paste this into Claude Code or Codex:Make my backend API multi-tenant using npx apiblaze@latest skills