Documentation

Ship an API in one command

Everything below works without an account — the CLI, a curl call, or the Next.js sidecar. Try it live right here:

apiblaze — try it, no signup
$npxapiblaze create--target 

Edit the URL, then hit Run — your API goes live instantly. No signup.

Quickstart

Point APIblaze at any URL. No account, no setup — you get a live, key-protected proxy. Pick your tool:

npx apiblaze create — one command, no signup
$ npx apiblaze create --target https://pokeapi.co

  ✓ Live → https://bravotiger4821.tryabz.run/1.0.0/prod
    API key (prod):  sk_prod_…        # also minted for dev & test
    Portal:          https://quietfox5093.portal.apiblaze.com/1.0.0
    Claim URL:       https://dashboard.apiblaze.com/claim?claimCode=G7QN-…

# call it — the key is required by default
$ curl https://bravotiger4821.tryabz.run/1.0.0/prod/api/v2/pokemon/ditto \
    -H "X-API-Key: sk_prod_…"

All three paths create the same thing: a serverless proxy in front of your API, with keys and a hosted dev portal. Since you didn't log in, the proxy isn't attached to an account yet — it works right away, but it's temporary. That's what the claim_url in the response is for: keeping it. (The next.js path is covered in depth in the sidecar section.)

Want the complete endpoint list? Jump to the Full API reference.

Chat with your API

Point apichat at any OpenAPI spec and start asking questions — no login needed. Under the hood it creates a live proxy and an MCP server, then opens a chat that actually calls your API.

npx apiblaze apichat
$ npx apiblaze apichat --openapi https://apiblaze.com/pokeapi_openapi.yaml

  ✓ proxy live  → https://pokeapi4821.tryabz.run/1.0.0/prod
  ✓ MCP server  → pokeapi4821.mcp.apiblaze.com

you › what types is ditto?
agent › GET /api/v2/pokemon/ditto … → Ditto is a Normal-type Pokémon.
        (the exact curl for every call is printed above each answer)
  • --openapi <file|url> takes JSON or YAML, a local path or a URL (--openapispec is accepted as an alias). With only --target <url>, apichat tries to discover the spec.
  • If the upstream API needs a credential, you'll be prompted — or pass --target-auth-env MY_TOKEN to read it from an environment variable (CI-safe).
  • Every turn prints the equivalent curl, so the chat doubles as a live API explorer. Hide it with --no-verbose.
  • Optional: npx apiblaze llm set-key stores your own LLM key locally (OpenRouter / Anthropic / DeepSeek / OpenAI) — lifts model quality and bills your key instead.

Keep your proxy (claiming)

The quickstart worked without an account, so the proxy you just made isn't attached to anyone yet. It runs immediately, but it's temporary: it gets deleted after 72 hours if it never receives a request, or after 30 days without traffic. "Claiming" it makes it permanently yours — it takes about 30 seconds.

step 1 — the create response already gave you the link
$ curl -sX POST https://api.apiblaze.com/proxy \
    -H 'content-type: application/json' \
    -d '{ "target": "https://pokeapi.co" }'
{
  "project_id": "bravotiger4821",
  …
  "claim_url": "https://dashboard.apiblaze.com/claim?claimCode=G7QN-…"
}
  • 2. Open the claim_url in your browser and sign in with GitHub — that's also how you create an account if you don't have one.
  • 3. Pick a team, or accept the new one created for you. Done — bravotiger4821 is permanently yours, along with everything else you made while logged out (keys, settings, other proxies).

After claiming, your proxy also serves on the permanent domain abz.run instead of the try-it domain tryabz.run. Prefer the terminal? npx apiblaze login then npx apiblaze claim does the same thing — you don't even need the code if you're on the machine you created the proxy from.

“Get an API key” on your own site

Two files, and your signed-in users mint, rotate and revoke their own API keys — on your page, in your brand. The browser talks only to your backend; your backend holds the APIblaze credential. You write exactly three values — everything else is paste-as-is.

First, one environment variable

APIblaze Dashboard → Developers → create a Widget key (not a full admin key), then put it in .env:

APIBLAZE_CP_KEY=sk_prod_…

The same key powers both widgets — you never need a second one.

How the pieces fit — two files

  1. 1The component <ApiKeyWidget/> renders the keys UI. When it needs data it makes one fetch() to a URL on your site (default /api/apiblaze/keys) — it never sees your APIblaze key and never calls APIblaze directly.
  2. 2The server route answers that fetch. createApiblazeKeys(...) returns { handler } — a normal request handler you export as GET/POST. It holds your secret cpKey and calls the one function you write — getUser — to learn who is asking, then talks to APIblaze server-to-server on their behalf (listing / minting / revoking only that user's keys).

The widget finds your route by its URL, not by any variable name — so const apiblazeKeys = … below is just your local name (call it whatever you like). Moving the route? Set the widget's endpoint prop to the new path. getUser is the only code you actually write.

Step 1 — the server route. Pick whatever you already use to log people in:

app/api/apiblaze/keys/route.ts — NextAuth / Auth.js v5
import { createApiblazeKeys } from 'apiblaze/server';
import { auth } from '@/auth';                 // your NextAuth config file

const apiblazeKeys = createApiblazeKeys({
  cpKey: process.env.APIBLAZE_CP_KEY!,
  getUser: async () => {
    const session = await auth();
    if (!session?.user) return null;           // not signed in → widget asks them to

    return {
      tenant: session.user.companyId,          // ① your org/team id — or session.user.id
      userId: session.user.id,                 // ②
      email:  session.user.email ?? undefined, // ③
    };
  },
});

export const GET = apiblazeKeys.handler;
export const POST = apiblazeKeys.handler;

Step 2 — the page. Identical for everyone, nothing to fill in:

app/keys/page.tsx — paste as-is
import { ApiKeyWidget } from 'apiblaze/react';

export default function Page() {
  return <ApiKeyWidget theme={{ accent: '#7C3AED' }} />;
}

The only three values you supply

① tenantWhich of your customers this user belongs to. It is the wall between customers — two tenants never see each other's keys, users or groups.
Use your company / team / workspace / organisation id. No such concept in your app? Pass the user's own id — then every user simply gets their own private space.
② userIdWhich person is signed in. They own the keys they create.
Your user's primary key. Must be stable, and never reused for a different human.
③ emailOptional, but recommended — it puts a real identity on this user. Sent once (never on every call) to link userId ↔ email, so the same person is recognised across surfaces — e.g. when they later sign into your API's dev portal with that email, or when an admin adds them by email.
It's also the hook for admin rights: an email on the tenant's admin allowlist becomes a tenant admin in the Users & Groups widget. Omit it and the user still works — they're just identified only by userId.

Where those come from, whatever you use to log people in

Your logintenantuserId
NextAuthsession.user.organizationIdsession.user.id
ClerkorgId (from auth())userId
Auth0 / WorkOSorg_id claimsub
Your own databasethe account / company row idthe user row id
No teams at allthe same value as userIdyour user id

“Organisation”, “org”, “workspace”, “team”, “account” and “company” all mean the same thing here: the customer you bill. That is your tenant.

  • Eligibility is decided on your server, from your session — never in the browser. Return keyTypes: false from getUser to deny a user API access entirely; a list of two or more types shows a picker.
  • Keys are durable by default (no silent expiry); set keyExpiresInSeconds for expiring, re-revealable keys. Durable secrets are shown once, masked afterwards.
  • Everything is white-label through the theme prop: accent, radius, fonts.

Users & groups on your own site

Literally the same thing again with one word changed: same widget key, same getUser, one more route. Your customers' admins then manage their own users and (nested) groups from your page — and group membership drives authorization at the proxy.

It's your keys route with one word changed. Same two files (route + page), same getUser, same APIBLAZE_CP_KEY — only createApiblazeKeyscreateApiblazeGroups. Compare the tab below to your keys route: identical but the one word. (Running both widgets and don't want to write getUser twice? Move it to a shared file and import it in both — optional.)
app/api/apiblaze/groups/route.ts — your keys route, one word changed
import { createApiblazeGroups } from 'apiblaze/server';   // ← the only change vs. the keys route
import { auth } from '@/auth';

const apiblazeGroups = createApiblazeGroups({
  cpKey: process.env.APIBLAZE_CP_KEY!,                   // the SAME widget key
  getUser: async () => {                                 // the SAME getUser
    const session = await auth();
    if (!session?.user) return null;
    return {
      tenant: session.user.companyId,                    // ① which CUSTOMER
      userId: session.user.id,                           // ② which PERSON
      email:  session.user.email ?? undefined,           // ③ links identity (dev-portal, admin rights)
    };
  },
});

export const GET = apiblazeGroups.handler;
export const POST = apiblazeGroups.handler;
  • Groups nest, and rules walk the whole tree (OpenFGA underneath) — maria ∈ reservationists ⊂ admin passes an admin check.
  • Identities seen in real traffic surface in the widget so admins can pull them into groups.
  • Resolved membership is forwarded to your backend as abz.groups on every request.
  • To enforce access from group membership, add a rule — see Authorization rules.

The Next.js sidecar

Install once, and every external call your app makes shows up in your dev console. Approve an origin and its traffic routes through APIblaze — auth, rate limits, observability — with zero code changes. What you don't approve is never touched.

npx apiblaze init — observe, then approve
$ npx apiblaze init          # in your Next.js app; wires the sidecar + dev inspector
$ npm run dev

[apiblaze/sidecar] active — approved origins route through APIblaze; the rest go direct.
[apiblaze/sidecar] direct (not approved) → https://api.stripe.com

$ npx apiblaze sidecar approve api.stripe.com
  ✓ proxy created — api.stripe.com now routes through APIblaze

$ npx apiblaze sidecar deny analytics.example.com    # stop suggesting it
$ npx apiblaze sidecar remove api.stripe.com         # go direct again (deletes the proxy)
  • init also generates a dev-only /abz-inspector page (skip with --no-inspector).
  • No login needed — proxies created this way are temporary until you keep them, exactly like the quickstart one.

Publish an MCP server

Any proxy can serve as a Model Context Protocol server, so Claude, Cursor and other agents call your API as typed tools. Build the catalogue in a short chat, then publish.

npx apiblaze agent mcp
$ npx apiblaze agent mcp acme
you › expose search + order lookup as tools
agent › search_products(q, limit)
        get_order(order_id)
/publish
  ✓ Published MCP server — acme-acmecorp.mcp.apiblaze.com/1.0.0/prod
  • The server address is {project}-{tenant}.mcp.apiblaze.com/{version}/{environment} — for example acme-acmecorp.mcp.apiblaze.com/1.0.0/prod. (A tenant is a named workspace for a group of your users, like one customer company. If your proxy doesn't have one, the address is just {project}.mcp.apiblaze.com.)
  • Tools speak real MCP: tools/list and tools/call over JSON-RPC 2.0, with path/query/body marshalling and streaming.
  • Access is OAuth-gated — agents authenticate like any other consumer of your API.
  • apichat publishes an MCP server automatically as part of its one-command setup.

Builder agents

Chat-driven builders that write configuration for you — each proposes, you review, then /publish. Billed per turn.

the four builders
$ npx apiblaze agent                      # general: describe what you want, it makes the calls
$ npx apiblaze agent openapi acme         # drafts your OpenAPI spec from real captured traffic,
                                          #   then /publish — or opens a GitHub PR to your repo
$ npx apiblaze agent authz acme           # drafts access rules, tests them on real
                                          #   traffic watch-only, /enable to enforce
$ npx apiblaze agent mcp acme             # picks routes to become MCP tools, /publish

For a single rule without a conversation, use npx apiblaze rule "users see only their own rows" acme — one shot, watch-only by default, --enforce to turn it on immediately.

The create request

POST https://api.apiblaze.com/proxy — no auth header. Provide exactly one source; everything else is optional and defaulted. Every option below also works as a --config file.json passed to npx apiblaze create.

  • One source, required: target (or target_url), openapi, or github.
  • Names auto-generate if omitted (two words + 4 digits). Set name, tenant, product_slug to choose them.
  • Created without an account, the proxy serves on the try-it domain tryabz.run. Once you keep it, it serves on the permanent domain abz.run with standard keys.
curl — choose the names
curl -sX POST https://api.apiblaze.com/proxy \
  -H 'content-type: application/json' \
  -d '{
  "target": "https://pokeapi.co",
  "name": "pokeproxy"
}'

Authentication

Control how callers authenticate with auth_type (none | api_key | oauth) or the full requests_auth object (which wins when present). Every example below is the complete, runnable call — the highlighted part is the setting that turns the feature on. The same keys can be set later with npx apiblaze config.

No auth (passthrough)

curl — create a proxy with no caller auth
curl -sX POST https://api.apiblaze.com/proxy \
  -H 'content-type: application/json' \
  -d '{
  "target": "https://api.example.com",
  "requests_auth": { "mode": "passthrough" }
}'

API keys — every caller must present a key

curl — create a key-protected proxy
curl -sX POST https://api.apiblaze.com/proxy \
  -H 'content-type: application/json' \
  -d '{
  "target": "https://api.example.com",
  "requests_auth": {
    "mode": "authenticate",
    "methods": ["api_key"],
    "identified_traffic_only": true,
    "owner_fallback": true,
    "key_types": [
      { "name": "read-only", "scopes": ["read"], "description": "Read-only consumer key" }
    ]
  }
}'

Accept third-party JWTs (your own issuer / audience)

curl — create a proxy that trusts your issuer
curl -sX POST https://api.apiblaze.com/proxy \
  -H 'content-type: application/json' \
  -d '{
  "target": "https://api.example.com",
  "requests_auth": {
    "mode": "authenticate",
    "methods": ["jwt"],
    "jwt": {
      "allowed_pairs": [{
        "iss": "https://login.acme.com/",
        "aud": "acme-api",
        "jwks_url": "https://login.acme.com/.well-known/jwks.json"
      }]
    }
  }
}'

That's the whole thing — iss, aud, jwks_url. The end-user identity defaults to the token's sub claim. Only if your user id lives in a different claim do you add "endUserIdSource": "claim", "endUserIdClaimName": "email" (or map external subs to your users with "endUserIdTransformation": "map").

Opaque tokens — introspect at your endpoint

curl — create a proxy that introspects tokens
curl -sX POST https://api.apiblaze.com/proxy \
  -H 'content-type: application/json' \
  -d '{
  "target": "https://api.example.com",
  "requests_auth": {
    "mode": "authenticate",
    "methods": ["opaque"],
    "opaque": {
      "endpoint": "https://auth.example.com/introspect",
      "method": "POST",
      "params": "?access_token={token}",
      "body": "token={token}"
    }
  }
}'

Authorization rules

Rules like "users can only see their own reservations", enforced at the proxy — your backend doesn't change. New rules start in watch-only mode (called "shadow"): they report what they would have blocked on real traffic, without blocking anything, until you turn enforcement on.

npx apiblaze rule — plain English, one shot
$ npx apiblaze rule "users see only their own reservations; \
    children of admin see all" reserv

  ✓ rule created — watch-only for now:
    reporting what it WOULD block, blocking nothing

# happy with the report? turn it on:
$ npx apiblaze rule "…" reserv --enforce

GET /reservations/42   # john  · owner                     → 200
GET /reservations/42   # alice · nobody                    → 403
GET /reservations/42   # maria ∈ reservationists ⊂ admin   → 200
  • Ownership is captured at the proxy when objects are created — no backend changes.
  • For an interactive design session over many routes, use npx apiblaze agent authz <project>.
  • Enforcement fails closed: if the policy engine is unreachable, requests are denied, not leaked.

Authorization from your backend

The same permission engine that guards your proxy is also a plain HTTPS API. Call it straight from your own backend to ask "is this user allowed?", or to record who owns what — even for requests that never go through your proxy. You define the model once in the dashboard (or with npx apiblaze rule); here you just read and write facts against it.

Ask: is John allowed to see reservation 42?
curl -sX POST https://policies.apiblaze.com/1.0.0/prod/v1/check \
  -H 'x-api-key: sk_prod_…' \
  -H 'content-type: application/json' \
  -d '{ "tuples": [ { "user": "user:john", "relation": "owner", "object": "reservation:42" } ] }'

# → { "results": [ { "allowed": true } ], "all_allowed": true }
Record a fact: John owns reservation 42
curl -sX POST https://policies.apiblaze.com/1.0.0/prod/v1/write \
  -H 'x-api-key: sk_prod_…' \
  -H 'content-type: application/json' \
  -d '{ "writes": [ { "user": "user:john", "relation": "owner", "object": "reservation:42" } ] }'

# to remove it later, send the same shape under "deletes"
List everything John can access of one type
curl -sX POST https://policies.apiblaze.com/1.0.0/prod/v1/list-objects \
  -H 'x-api-key: sk_prod_…' \
  -H 'content-type: application/json' \
  -d '{ "user": "user:john", "relation": "owner", "object_type": "reservation" }'

# → { "objects": ["42", "77"], "count": 2 }
  • Four endpoints: /v1/check, /v1/write, /v1/list-objects, /v1/read. All POST.
  • Which tenant you're acting on comes from your API key — never from the URL, so one key can only ever touch its own data.
  • Each call is one ordinary request on your bill, at the same band price as a proxied call.
  • The model (what relations exist, how they nest) is authored in the dashboard or via npx apiblaze rule; this API reads and writes facts, it doesn't change the model.

Throttling

Set a per-user rate limit and a proxy-wide quota — at creation or any time after.

npx apiblaze throttle
$ npx apiblaze throttle myapi --rate 10 --quota 10000 --period daily
  ✓ userRateLimit=10 r/s · proxyQuota=10000/daily

# add --verbose to any command to see the exact API calls it makes

userRateLimit is requests/second per user; proxyQuota with quotaPeriod (daily / weekly / monthly) caps total volume. Callers over the limit get a clean 429.

Dev-portal login

Every proxy gets a hosted developer portal. Omit login for a default APIblaze-managed GitHub sign-in, or bring your own providers with per-provider token types.

Managed GitHub (the default, made explicit)

curl — portal login via managed GitHub
curl -sX POST https://api.apiblaze.com/proxy \
  -H 'content-type: application/json' \
  -d '{
  "target": "https://api.example.com",
  "login": { "providers": [ { "type": "github", "managed": true } ] }
}'

Bring your own Google — choose client & target-server token types

curl — portal login via your own Google client
curl -sX POST https://api.apiblaze.com/proxy \
  -H 'content-type: application/json' \
  -d '{
  "target": "https://api.example.com",
  "auth_config": { "who_can_register": "authorized_only" },
  "login": {
    "scopes": ["openid", "email", "profile", "offline_access"],
    "authorized_callback_urls": [
      "https://myteam.portal.apiblaze.com/1.0.0"
    ],
    "providers": [{
      "type": "google",
      "client_id": "xxxx.apps.googleusercontent.com",
      "client_secret": "GOCSPX-xxxx",
      "token_type": "thirdParty",
      "target_server_token": "third_party_access_token",
      "scopes": ["openid", "email", "profile"]
    }]
  }
}'

Providers: github · google · microsoft · facebook · auth0. token_type is what the browser receives; target_server_token is what gets forwarded to your API.

Versions & environments

Run old and new API versions side by side, point dev/staging/prod at different upstreams, and choose what your main URL serves.

add a version · retarget an environment
# add a 2.0.0 alongside the existing 1.0.0 (serves at /2.0.0/…)
$ npx apiblaze create --name myapi --target https://api-v2.example.com --apiversion 2.0.0

# point one environment somewhere else
$ npx apiblaze target myapi --env dev --url https://dev.example.com

# choose which version/environment the bare domain serves
$ npx apiblaze domain set-base myapi

Each environment gets its own API keys, so a leaked dev key never touches prod.

Custom domains

Keep the free subdomain, or serve your API from your own domain — APIblaze provisions the hostname and TLS.

npx apiblaze domain
$ npx apiblaze domain add myapi
  → set this DNS record at your provider:
    CNAME  api.yourcompany.com  →  <shown-target>.apiblaze.com

$ npx apiblaze domain status myapi
  api.yourcompany.com   ✓ TLS issued · ✓ active

$ npx apiblaze domain list myapi          # all custom domains for a proxy
$ npx apiblaze domain rm myapi            # remove one

Import a spec

Instead of a bare target, provision from an OpenAPI document — routes, schemas and docs arrive pre-populated.

curl — create from an inline OpenAPI document
curl -sX POST https://api.apiblaze.com/proxy \
  -H 'content-type: application/json' \
  -d '{
  "openapi": "<full OpenAPI YAML or JSON document as a string>"
}'

Importing straight from a GitHub repo (and keeping it in sync) is a one-click flow in the dashboard — it connects your GitHub once, then pulls the spec for you.

Already live without a spec? npx apiblaze agent openapi <project> drafts one from your real traffic — see Builder agents.

The CLI

Everything on this page, from the terminal. npx apiblaze (Node 18+) — no install required. Add --verbose to any command to print the exact API calls it makes.

bash
npx apiblaze apichat --openapi <url>          # chat with any API, no login
npx apiblaze create --target https://pokeapi.co   # go live, no signup
npx apiblaze init                                 # sidecar into a Next.js app
npx apiblaze dev 3000                             # tunnel localhost, stream traffic
npx apiblaze login && npx apiblaze claim          # claim it to your account
npx apiblaze config myapi                         # browse & change EVERY setting

Full CLI reference

What your API's users get

The people who call your API (your "consumers") get a hosted portal — login, self-serve keys, live "try it" — plus their own CLI commands under apiblaze consumer.

what your consumers use
# the hosted portal — login, self-serve keys, try endpoints live
https://{project}.portal.apiblaze.com/{version}

# or from the terminal:
$ npx apiblaze consumer login       # device-flow login to a tenant's portal
$ npx apiblaze consumer apikeys     # list keys (reveals expiring ones), offer to create
$ npx apiblaze consumer tokens      # show the access / refresh / id tokens

Prefer keys on your own page instead of the portal? That's the API-key widget.

Field reference

The create option set at a glance. The authoritative source for types, enums and defaults is the live OpenAPI spec at api.apiblaze.com/openapi.json — browsable in the API portal.

target / target_url / openapi / githubThe upstream source (exactly one)
name / subdomain / display_name / tenant / product_slugNaming — all optional, auto-generated
auth_typeShortcut: none / api_key / oauth
requests_auth.modepassthrough or authenticate
requests_auth.methodsjwt / opaque / api_key
requests_auth.identified_traffic_onlyRequire a resolvable end-user identity
requests_auth.preapproved_users_onlyRestrict to pre-approved portal users
requests_auth.owner_fallbackAttribute keyless usage to the key owner
requests_auth.jwt.allowed_pairsAccepted third-party (iss, aud) pairs
requests_auth.opaqueIntrospection endpoint / method / params / body
requests_auth.key_typesCustom API-key types with scopes
auth_config.who_can_registeranyone / authorized_only (portal login)
login.providers[]Dev-portal OAuth providers (managed or BYO)
login.providers[].token_typeClient-side token: apiblaze / thirdParty
login.providers[].target_server_tokenForwarded token: apiblaze / third_party_* / none
environmentsPer-environment upstream targets
throttlinguserRateLimit + proxyQuota + quotaPeriod

Full API reference

Every endpoint of the APIblaze control-plane API — creating and configuring proxies, keys, tenants, users, groups and authorization — with request/response schemas, enums and defaults.