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:
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 --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 --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 (--openapispecis 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_TOKENto 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-keystores 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.
$ 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_urlin 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 —
bravotiger4821is 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
- 1The component
<ApiKeyWidget/>renders the keys UI. When it needs data it makes onefetch()to a URL on your site (default/api/apiblaze/keys) — it never sees your APIblaze key and never calls APIblaze directly. - 2The server route answers that fetch.
createApiblazeKeys(...)returns{ handler }— a normal request handler you export asGET/POST. It holds your secretcpKeyand 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:
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:
import { ApiKeyWidget } from 'apiblaze/react';
export default function Page() {
return <ApiKeyWidget theme={{ accent: '#7C3AED' }} />;
}The only three values you supply
| ① tenant | Which 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. |
| ② userId | Which 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. |
| ③ email | Optional, 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 login | tenant | userId |
|---|---|---|
| NextAuth | session.user.organizationId | session.user.id |
| Clerk | orgId (from auth()) | userId |
| Auth0 / WorkOS | org_id claim | sub |
| Your own database | the account / company row id | the user row id |
| No teams at all | the same value as userId | your 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: falsefromgetUserto 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
keyExpiresInSecondsfor expiring, re-revealable keys. Durable secrets are shown once, masked afterwards. - • Everything is white-label through the
themeprop: 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.
getUser, same APIBLAZE_CP_KEY — only createApiblazeKeys → createApiblazeGroups. 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.)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 ⊂ adminpasses anadmincheck. - • 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.groupson 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 # 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)- •
initalso generates a dev-only/abz-inspectorpage (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 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 exampleacme-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/listandtools/callover 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.
$ 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, /publishFor 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(ortarget_url),openapi, orgithub. - • Names auto-generate if omitted (two words + 4 digits). Set
name,tenant,product_slugto 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 domainabz.runwith standard keys.
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 -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 -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 -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 -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 "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.
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 }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"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. AllPOST. - • 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 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 makesuserRateLimit 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 -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 -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 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 myapiEach 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 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 oneImport a spec
Instead of a bare target, provision from an OpenAPI document — routes, schemas and docs arrive pre-populated.
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.
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 settingWhat 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.
# 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 tokensPrefer 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 / github | The upstream source (exactly one) |
| name / subdomain / display_name / tenant / product_slug | Naming — all optional, auto-generated |
| auth_type | Shortcut: none / api_key / oauth |
| requests_auth.mode | passthrough or authenticate |
| requests_auth.methods | jwt / opaque / api_key |
| requests_auth.identified_traffic_only | Require a resolvable end-user identity |
| requests_auth.preapproved_users_only | Restrict to pre-approved portal users |
| requests_auth.owner_fallback | Attribute keyless usage to the key owner |
| requests_auth.jwt.allowed_pairs | Accepted third-party (iss, aud) pairs |
| requests_auth.opaque | Introspection endpoint / method / params / body |
| requests_auth.key_types | Custom API-key types with scopes |
| auth_config.who_can_register | anyone / authorized_only (portal login) |
| login.providers[] | Dev-portal OAuth providers (managed or BYO) |
| login.providers[].token_type | Client-side token: apiblaze / thirdParty |
| login.providers[].target_server_token | Forwarded token: apiblaze / third_party_* / none |
| environments | Per-environment upstream targets |
| throttling | userRateLimit + 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.
Prefer the raw spec? It's served as OpenAPI at api.apiblaze.com/openapi.json — import it into Postman, Insomnia or your own tooling.