API: your first call
Everything below was run against a live Forge instance before it was published. The ids are real; substitute your own host and they work.
We use https://your-instance.example for the host. If you are following along on a demo instance, use its
hostname.
1. Read something — no credential at all
Section titled “1. Read something — no credential at all”The public read face answers anyone. Start by resolving the store from the hostname:
curl "https://your-instance.example/v1/read/store.by_host?host=your-instance.example"{ "store_id": "sto_01ABCDEFGHJKMNPQRSTVWXYZ01" }That id is the store parameter for every other public read — yours, from your instance. Every id on
this page has the right shape and is not a real one; store.by_host above is how you get the one that
answers for you.
Now list some products:
curl "https://your-instance.example/v1/read/products?store=sto_…&limit=2&projection=feed"2. Change something — still no credential
Section titled “2. Change something — still no credential”Building a cart is the shopper face: anonymous, scoped to a store, and the cart id is the capability. Exactly what a browser does.
curl -X POST "https://your-instance.example/v1/cart/commands/cart.create" \ -H "content-type: application/json" \ -H "x-forge-store: sto_…" \ -d '{}'{ "cart_id": "cart_01ABCDEFGHJKMNPQRSTVWXYZ01" }curl -X POST "https://your-instance.example/v1/cart/commands/cart.add_line" \ -H "content-type: application/json" \ -H "x-forge-store: sto_…" \ -d '{"cart_id": "cart_…", "sku_id": "sku_…", "qty": 1}'{ "line_id": "cl_01ABCDEFGHJKMNPQRSTVWXYZ01", "qty": 1 }3. Read it back
Section titled “3. Read it back”curl "https://your-instance.example/v1/read/cart?store=sto_…&cart_id=cart_…"{ "cart_id": "cart_01ABCDEFGHJKMNPQRSTVWXYZ01", "currency": "BRL", "subtotal": 20000, "total": 20000, "lines": [ { "line_id": "cl_…", "sku_id": "sku_…", "qty": 1, "unit_amount": 20000, "line_total": 20000, "available": 95 } ]}4. Now with a key — the operator face
Section titled “4. Now with a key — the operator face”Everything so far was anonymous. To read what an operator reads, you need an API key. Issue one in the admin under Team → API keys, with only the scopes you need, and export it:
export FORGE_TOKEN='fopk_…'curl -i -H "Authorization: Bearer $FORGE_TOKEN" \ "https://your-instance.example/v1/read/internal/whoami"HTTP/2 200ratelimit-limit: 6000ratelimit-remaining: 5999ratelimit-reset: 28{ "actor_id": "act_…", "tenant_id": "…", "scopes": ["catalog.admin.read", "logistics.read", "order.read"]}Two things to notice:
The RateLimit-* headers came back on a success, not on a 429. The state of your budget is part of
every authenticated response, so you can design a backoff before you need one. The subject of the limit is
the credential, per tenant — two keys of the same customer have independent budgets.
whoami tells you what you hold. It is the fastest way to check that a key carries what you think it
carries.
5. The refusal
Section titled “5. The refusal”Ask for something the key does not carry:
curl -i -H "Authorization: Bearer $FORGE_TOKEN" \ "https://your-instance.example/v1/read/internal/api_keys"{ "error": { "kind": "forbidden", "message": "read requires the admin.users.write scope" } }403, and the message names the scope. Not a 404, not an empty list, not a silence you have to
diagnose. That is the kernel’s own check — the same one every other surface reaches, so a scope withheld is
withheld everywhere at once.
What you just used
Section titled “What you just used”| Face | Credential | For |
|---|---|---|
GET /v1/read/<name> |
none | Anything a shopper’s browser may see |
POST /v1/cart/commands/<name> |
none | The cart journey; the cart id is the capability. There are sibling anonymous faces — a command’s reference page names its exact route |
POST /v1/commands/<name> |
tenant | Operator and integration writes |
GET /v1/read/internal/<name> |
tenant | Operator reads, gated by scope |
The platform face (/v1/control/…) exists too, for whoever runs the fleet.
- CLI: forge login — the same commands from a terminal.
- MCP: connect an agent — the same registry as agent tools.
- SDK: npm install — the same names, typed.
- Reference — every command and read, generated from the registry.