VendoVendo Docs
Build a toolvendo.yaml

vendo.yaml

The manifest at the root of your repo that tells Vendo what your tool needs to run.

vendo.yaml is a deploy-time manifest. It lives at the root of your repo and the Vendo CLI validates it before you ship a release. It is the contract between your tool and the platform: what providers you talk to, what env vars your code requires, and how Vendo decides whether your container is healthy.

The SDK does not read vendo.yaml. The runtime contract — env vars, proxy URLs, the vendo_sk_* key — is set up by the platform based on what the manifest declares, and your code reads it from process.env / os.environ like any other configuration.

vendo.yaml and the template manifest are two different files. vendo.yaml lives in your tool's repo and declares the tool's intent at the schema below. The release pipeline transforms it into a template manifest (templates/<slug>/<version>.json in vendo-templates), which carries richer fields like services, databases, secrets, userInputs, and wizardLayout — those are documented in Deploy & publish → Manifest. The deploy worker reads the template manifest from R2 at deploy time, not your vendo.yaml.

What it controls

vendo.yaml has exactly these fields. The canonical schema is vendo-cli/src/lib/vendo.schema.json — every undeclared key fails validation.

name: my-tool          # catalog slug, immutable once set
version: 1             # manifest schema version (always 1 today)
runtime: python        # optional hint: python | node | go | rust | ruby | java | other
integrations:          # optional: providers your tool calls through Vendo's proxy
  - openrouter
  - telegram
env:                   # optional: env contract
  required: [DATABASE_URL]
  optional: [LOG_LEVEL]
health:                # optional: how Vendo decides your container is up
  path: /healthz
  port: 8787
restart_on:            # optional: integration slugs whose connection-state changes restart the app
  - openrouter

Only name and version are required. Each concern below gets its own page.

When it's read

vendo.yaml is consumed at two points and nowhere else:

  1. Release time. When you push a new version of your tool, the release pipeline reads vendo.yaml, validates it against the schema, and uses it (alongside the tool's template manifest in vendo-templates) to write the resolved release record in tool_releases. Existing deployments stay pinned to the version they were deployed on; new deployments pick up the new manifest.
  2. Deploy time. When a tenant deploys your tool, the deploy worker reads the template manifest from R2 (templates/<slug>/<version>.json), resolves placeholders against the deployment context, materializes env vars, and starts your container. It does not re-read vendo.yaml.

After that, the manifest plays no role. Your code never reads it; the SDK never reads it. Everything is plain env vars from your code's point of view.

Versioning the manifest

version: 1 refers to the manifest schema, not your tool's release. It stays 1 indefinitely — new fields are added as optional, and breaking changes get a new major version that the platform supports in parallel for at least one release cycle.

Your tool's own version is separate (e.g. 1.0.7, 2.0.0), bumped whenever you ship a meaningfully different build. Each version is a frozen snapshot of vendo.yaml plus the container image SHA — existing tenants on the old version aren't affected until they opt into an upgrade.

Validation

You can validate locally before pushing:

npx @vendodev/cli validate ./vendo.yaml

The release pipeline runs the same check and fails loudly on drift. The CLI prints the offending field path, the expected shape, and a one-line fix hint.

Restart on connection change

restart_on: lists integration slugs whose connection-state changes should trigger a process restart. If a tenant rotates their OpenAI key, restarts an OAuth grant, or swaps a Telegram bot token, the deployment is restarted so your code picks up the new value at boot. Example:

integrations:
  - openrouter
  - telegram
restart_on:
  - openrouter

Connection rebinds always update deployment_env_vars whether the slug is in restart_on or not — what restart_on adds is the process restart so a long-lived client doesn't hold a stale token.

On this page