API collections

voltro build api --target postman | bruno | insomnia | hoppscotch — a collection from the same pipeline as the SDKs, with environments, auth, examples, saved error responses and tests; and publicApi.artifacts, so voltro dev keeps it written beside rpcGroup.generated.ts.

Before anyone programs against /v1, somebody clicks through it — in Postman, Bruno, Insomnia or Hoppscotch. All four import OpenAPI, and what the import cannot bring is exactly what every consumer then adds by hand: the environments, the auth on the collection, the example bodies, the saved error responses, the tests. voltro build api --target <tool> writes those from the same pipeline that generates the TypeScript client and the native SDKs: bindings in, a consumer artifact out.

Generate

voltro build api --target postman    --out ./api-collections --name awb
voltro build api --target bruno      --out ./api-collections --name awb
voltro build api --target insomnia   --out ./api-collections --name awb
voltro build api --target hoppscotch --out ./api-collections --name awb
Target Writes
postman <name>.postman_collection.json (v2.1) and <name>.<env>.postman_environment.json per server.
bruno <name>.bruno/bruno.json, collection.bru, a folder per tag with one .bru per request, environments/<env>.bru. The variant that diffs in git.
insomnia <name>.insomnia.yaml — the v5 export with the collection, environments and a cookie jar.
hoppscotch <name>.hoppscotch.json and <name>.<env>.hoppscotch_environment.json per server.

What every collection carries

One model, four serializers — the content is the same in all four:

  • A folder per tag (teams), with the tag's description and externalDocs link; a request per projection, named by summary, documented by description as Markdown, with the API version in the docs.
  • Path parameters as :teamId / {{ _.teamId }} / <<teamId>> with the example value; query parameters from the input, optional ones disabled by default.
  • The body from publicApi.example.request — or the derived one, marked in the docs as derived, so nobody takes a valid shape for the author's word.
  • A saved response for the 200 and one per errorStatus entry, application/problem+json with the real urn:voltro:error:<Tag>.
  • Auth on the collection: bearer {{apiKey}}; every request inherits it. A security: 'optional' projection still inherits, and its docs say it works without a token; security: false sends none.
  • Idempotency-Key (a generated UUID per attempt) on every mutation whose descriptor says idempotent: true.
  • One environment per servers entrybaseUrl, a secret apiKey, apiVersion — so switching to v2 or to staging is one variable.
  • A test per request that asserts what the spec cannot say: the request succeeded (a generated request is a happy path built from the example, so a non-2xx is a failure of the API — without this a collection stays green against a server answering 500 to everything), a 2xx decodes against the response schema (Postman validates the JSON Schema; the others check the required fields), an error is application/problem+json whose type starts with urn:voltro:error: and whose status matches, a 429 carries Retry-After and RateLimit-Reset, a GET under an ETag profile carries an ETag.

That makes the collection the smoke test against a running instance:

newman run awb.postman_collection.json -e awb.staging.postman_environment.json --env-var apiKey=$API_KEY
bru run --env staging --env-var apiKey=$API_KEY awb.bruno

Written by voltro dev

// app.config.ts
export default defineApi({
  publicApi: { profile: 'standard', artifacts: ['openapi', 'postman', 'bruno'] },
})

With artifacts, every voltro dev boot writes openapi.generated.json and the named collections beside rpcGroup.generated.ts — and, with 'typescript' in the list, the generated TypeScript client package as the app's publicApi.client declares it — <app>.generated.postman_collection.json, <app>.generated.bruno/, … — touching only files whose content changed. The .generated. segment is the watcher's rule for codegen output, so a rewrite never restarts the boot that wrote it. Commit them: a review diff then shows the surface change as a change to the collection, Bruno opens the folder from the repository, Postman watches the file. A collection somebody imported once drifts; one the dev loop writes cannot. voltro serve writes nothing.

The way without a build

Postman keeps an API definition in sync from a URL, and Hoppscotch and Insomnia import a spec URL — pointed at /openapi.json of a dev instance, that stays current on its own, without environments, auth or tests. Both ways read the same document; the build is the one that brings the rest.