Skip to content
v0.3

Surfaces

A surface is a way to reach the port. Forge ships four, and none of them is written by hand: each is generated from the command and read registries, so they cannot fall out of step with the kernel or with each other.

Surface What it is for
API HTTP. The one every other surface is built on, and the one your ERP will use.
CLI A terminal. Operations, scripts, and looking at things quickly.
MCP An agent. The registry exposed as tools, with the kernel’s own scope check behind it.
SDK TypeScript. Typed per command name, results included.

Writes go to /v1/commands/<name> with a tenant credential. Platform commands — the ones marked System: yes on their reference page — go to /v1/control/commands/<name> and need a platform credential instead. They are a different actor reaching a different gate, not the same call with more permission.

⚠️ Public commands are on neither. Building a cart, placing an order, a shopper logging in, starting a payment — those are anonymous, and they are served by faces of their own: /v1/cart, /v1/checkout, /v1/customer, /v1/operator, /v1/payment. No credential; the store travels in the x-forge-store header, and possession of the relevant id is the capability. A handful of public commands are deliberately on no external face at all — the ones that move money are reached only by the kernel itself, on a verified provider verdict.

Reads have three faces, and the difference between them is who is allowed to ask:

  • GET /v1/read/<name>public. No credential. The store resolves the tenant. This is what a storefront calls, including from a browser.
  • GET /v1/read/internal/<name>internal. A tenant credential is required and the tenant comes from the caller’s identity, never from a parameter. Gated by scope.
  • GET /v1/control/read/<name>control. The platform face.

⚠️ A public read is PII-safe by decision, even for the party holding the identifier. A guest reading their own checkout gets j***@e***.com, not the address they typed. That is deliberate: possession of a cart id is not proof of identity, and the front-end that typed the value is the one that should still have it.

This is the part worth internalising, because it shapes how you build on Forge.

The read port is not “the API the storefront happens to use”. It is the contract, designed to be consumed by anything: your storefront, a native app, a partner’s marketplace integration, an agent doing research. It answers in stable shapes with stable names, and it does not assume a browser.

The storefront and admin that ship with Forge are consumers of that contract, not layers of the kernel. They are a reference implementation — complete and maintained by us — and you can replace them piece by piece or entirely. Whatever you build talks to the same kernel through the same door, with the same guarantees.

That is why “own your storefront” is a supported outcome rather than an escape hatch. See the guide.

What is generated, and what that means for you

Section titled “What is generated, and what that means for you”

The reference pages, the OpenAPI document, the CLI dispatch table, the MCP tool list and the SDK’s types all come out of the same registries in one pass. CI regenerates them and fails the build if the committed bytes differ.

For you, the practical consequence is: the reference cannot lie about what exists. If a command is on this site, it is in the kernel with that exact input schema. If it is not, it does not exist.

Every authenticated response carries RateLimit-* headers — on success, not only on a 429. The state of your budget is part of the response contract, so you can design a backoff before you need one.

The subject of the limit is the credential, per tenant, never the account. Two keys belonging to the same customer have independent budgets, so one runaway integration cannot take the others down.