Skip to main content
A world can stand in for any third-party service your agent calls — a breach-data provider, a CRM, an intelligence feed. Describe the connection once, capture what the real service returns, seed the world from those captures through its schema, and serve the vendor’s own routes from the world. Your agent’s client points at the mock without a code change. Everything below runs with the gateway CLI from the @withgateway/sdk npm package. Nothing is provisioned on your side; publishing and hosting use the platform.

1. Start a world and describe the connection

Edit ./vendor-world/schema/world.json so its entities match the records the vendor returns, then write ./vendor-world/connector.toml:
List only the operations your agent actually calls. gateway worlds connector validate ./vendor-world checks the file and lists them. The files a world is made of documents every table and default in connector.toml and schema/world.json. The [redact] block decides what a captured value becomes before it reaches disk. hash and drop are shown above; preserve and round are the other two modes, and the salt is [connector] slug. See Keep real data out.

2. Capture and seed

capture follows the vendor’s cursor, writes every page under captures/ with a manifest, records the request without the secret, redacts the body per [redact], and keeps error responses, because the vendor’s error bodies are part of what the mock must reproduce. ingest turns the successful captures into the world’s starting state, checking the rows against the schema in a scratch copy before anything is written. Re-running ingest with no new captures writes nothing. A local capture calls the vendor from your machine and needs python3 >= 3.12 on the PATH. Without one, capture with --remote below.
Captures hold real records even after redaction. Keep captures/ out of anything you share; the world’s schema and connector.toml are what travel.
Rows you already have — a fixture file, a sanitized export, a previous run — go in with gateway worlds data import instead; see Put data in a world.

3. Serve the vendor’s API

The world answers the routes in connector.toml from its store, in the vendor’s envelope, with the vendor’s auth scheme and error bodies. Locally it accepts the credential --api-key pins, conform when you pass none. Point your agent’s client at it locally, or publish it:
A hosted session accepts its own api token, as a bearer or in the vendor’s own header; session open prints it beside api.url.

4. Writes and workflows

Lookup vendors are served from the store alone. Workflow vendors need more: the client creates a search, sets criteria, executes it, polls a status and reads results. The same connector.toml covers that:
Writes persist through the world’s contract: a body the vendor would reject gets the vendor’s 400, and the next read sees the write. A handler is plain Python with the store and a clock:
ctx reads and writes (rows, get, find, insert, update, delete), keeps time (now, elapsed, ladder; set GATEWAY_WORLD_CLOCK to an epoch to freeze it), mints ids (uuid) and shapes answers (envelope, not_found, invalid). A handler may answer (status, body, headers), and [api] before = "gate" names a function that runs ahead of every route to apply the vendor’s per-key rules (quotas, allow-lists) to plain reads and writes as well. Handlers ship inside the pushed world, so the hosted container runs them as-is. Vendors that authenticate with OAuth2 client credentials declare scheme = "oauth2" with token_url, client_id_env and scope: capture exchanges the credentials for a bearer before its first request, and the hosted world mints bearers on the same token path, so a client that performs the grant first works unchanged. Pagination follows the vendor too: [api] page is none, offset or cursor with the vendor’s own parameter and envelope key names, and a per-route single = true answers the object itself rather than a list. The files a world is made of lists every key: the [api] envelope, paging and error family, every [operations.api] key with its default, [capture], [operations.ingest], and [ui] for shipping a dashboard with the world.

4b. Capture from the platform’s IP

Some vendors allow-list the caller’s address. Store the credential on the environment once and let the platform make the calls:
The pages come back redacted into captures/ exactly as a local capture would write them, so connector ingest is unchanged. Give the vendor the platform’s egress address when they ask for one; the credential is held encrypted on the project and never leaves the platform.

5. Prove it against the vendor’s OpenAPI

Every declared operation is called on the served world with arguments taken from its own seed, and each answer is checked against the vendor’s document: the documented status and a body the response schema accepts. The exit code is non-zero on a mismatch, so it runs in CI. Scenarios (scenarios/<name>/ with seed overlays and an instruction) let a session start the world in a given state — gateway worlds serve --scenario quota-exhausted locally, or any of them hosted. Work through the failures with gateway worlds schema reconcile ./vendor-world. It compares schema/world.json against what the vendor really returned in captures/, then relaxes required fields the captures omit, adds undocumented keys as optional properties, and drops formats the captured values do not meet. Changes are printed and nothing is written until --write; --from names a source other than captures.

6. Save it as a workspace connector

publish saves the template — the contract, the connection, the handlers, scenarios and reports, never captures/ or data/ — on the platform as a workspace connector: versioned, owned by your organization, with its own page. The platform compiles it first and refuses a template that does not make a world, telling you why. Publish again after a change and it becomes a new version. Anyone in your organization starts a fresh world from it with --connector workspace:<slug>, and gateway worlds connector pull <slug> writes the files back out. Connectors another organization published show up in list --hub; gateway worlds connector clone <slug> copies one into your organization with its origin recorded. The publisher’s later changes do not reach the copy unless you clone again. For an agent doing the work, gateway worlds connector skill --install . puts the authoring checklist and the connector-template-author agent into the repository’s .claude/, so Claude Code or Cursor builds the world with the steps above and finishes with publish.

When the vendor’s shape does not fit the schema

  • A vendor that inlines a collection on a row (users carrying channels[]) when your schema keeps it as its own entity: add [operations.ingest] drop = ["channels"] and an [[operations.ingest.explode]] (field, entity, carry, take) so each element becomes a child row.
  • Credentials in several headers: [auth] headers = { "X-App-Token" = "VENDOR_APP_TOKEN" }.
  • Lookups driven by query parameters rather than the path: name the parameter in [operations.api] filter; several operations may share one path and are told apart by the parameters sent.
  • A vendor that answers 403 rather than 401: [api] unauthorized_status = 403.