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
./vendor-world/schema/world.json so its entities match the records the vendor returns,
then write ./vendor-world/connector.toml:
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.gateway worlds data import instead; see Put data in a world.
3. Serve the vendor’s API
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:
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 sameconnector.toml
covers that:
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: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
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.