Skip to main content
Hand a world to your own customers, one sealed copy each, hosted on your account rather than theirs. Provision a world per customer, open a session per end-user run, and grade the result. Every copy stays sealed from every other.

Pick the shape that matches what differs per customer

Both shapes can be combined: a world per tenant, and a task per end user inside it.
Start with one world and per-user tasks. Move to a world per tenant only when customers genuinely need different data or a different contract, because every world is a version history you then have to keep.

Shape one: a world per tenant

Create one world per customer

gateway worlds create cuts a world from a shipped connector template, from a connector your organization published, or from a directory holding a tree you authored.
The slug must be URL-safe and unique within the project, so prefix it with the tenant: acme-crm, globex-crm. The world is stored as its schema tree, and when the connector declares routes (connector.toml) the same session serves them beside the tools with no second step.

Provision many tenants in one call

--batch takes a JSONL file (or - for standard input) of one world per line, and prints one result line for each.
A line may also carry overlay and data (the seed rows, as {"rows": {"<entity>": [...]}}). The command exits 0 when every world was created, 2 when any line failed, and 1 on an error before the batch started.

Or call the API directly

POST /api/public/worlds is the same request, for provisioning from your own backend.
The response carries slug, containerId, kind, and the first version with its content hash. A slug already in use answers 409; a tree the contract refuses answers 400 and names the path that failed.

Load each tenant’s own records

Each import pushes a new version of that tenant’s world. Rows are checked against the world’s contract first, so one bad row writes nothing and the refusal names the field. See Put data in a world for the rows file and the append and replace modes.
Hash or drop personal fields before loading records from a real customer system. A world is sealed from the outside, but it is still readable by everyone in the project that owns it.

Shape two: one world, a task per end user

One published version serves any number of end users at once, each with their own goal, starting rows and grader. Supply the task when the session opens instead of publishing it in the bundle.
The same spec works from the CLI as --task-file, and over HTTP as the task field of POST /api/public/world-sessions.
Inline and stored tasks run on the World Host engine, which serves worlds stored as trees, with or without routes.

Tasks bundled in the world

A task can ship in the world instead of the request. Two formats exist, read by different engines: gateway worlds session open <world> -t <name> opens either kind by name. Details: the two bundled task formats.

Keep a task if the same user runs it again

Store a task your users return to in the project rather than in a request body. Stored tasks live outside any bundle, are versioned, and can be filtered by their metadata.
worlds task validate checks a task’s seed and grader entities against a world version without opening a session. worlds task update makes a new version; sessions already open keep the copy they started with.

The whole loop, per end-user run

1

Open a session for the run

2

Hand your user's agent the URL and the token

The session’s api.url and api.token are all an agent needs. Point it there instead of at the real vendor; nothing else in its code changes.
3

Grade the end state

4

Close it, and keep the ledger

What gets recorded, and to whom

Worlds and versions created with an API key are attributed to the key: the first version records apikey:<id> as its author and the world has no human owner. Everything lands in the project the key belongs to, so one project is one customer-facing integration. Runs record the version they faced, so a result stays interpretable after the tenant’s world moves on. Old versions stay addressable, and --version-id on session open pins a run to an exact one.

Verify

List what you provisioned. gateway worlds list prints each world’s id, slug and latest version; --slug narrows it to one. Check a tenant’s data landed. gateway worlds data show acme-crm prints the world’s kind and entity counts, and --entity accounts prints that entity’s rows. Prove the seal between tenants. Open a session on two tenants’ worlds and read the state of each; neither should contain the other’s records.
Watch them live. Each world’s Overview lists its live sessions with engine, state, task, API URL and age, plus Pin and Stop.
Slugs are unique per project, so a tenant name that collides answers 409 rather than overwriting anything. Decide the naming scheme before you provision the first customer — renaming a world later does not move the runs that recorded its old identity.

Where to go next