Skip to content

Workspace format

A workspace is a plain folder of YAML files, meant to be committed to Git and reviewed in pull requests. This page describes every file Zorvik reads and writes there. For how to create and open workspaces, see Workspaces.

my-api/
zorvik.yaml # workspace: name, id, variables, default auth, headers, scripts
environments/
Local.yaml # an environment and its variables
Staging.yaml
requests/
Get status.yaml # a request
Users/ # a folder
_folder.yaml # optional: the folder's order, auth, headers, scripts, docs
List users.yaml
Admin/
Delete user.yaml
servers/
Payments mock.yaml # a mock API or another server
loadtests/
Checkout smoke.yaml # a load test
specs/
Payments API.yaml # an OpenAPI document kept by an import
data/users.csv # any other files you add (data files, bodies, .proto files…)
  • YAML, camelCase keys. Every file is YAML 1.2 with camelCase field names. Zorvik writes files in a stable order and leaves out fields at their default value, so files stay small and diffs stay clean. You can write them by hand; missing fields take their defaults.
  • File names come from names. The display name is inside the file (name:). The file or folder name is derived from it: < > : " / \ | ? * and control characters become -, leading dots and trailing dots and spaces are dropped, names are cut to 80 characters, Windows device names (CON, NUL, COM1 …) get a _, and _folder becomes untitled. When the name is taken (ignoring case), Zorvik adds 2, 3 … Renaming an item in Zorvik renames its file.
  • Names can’t be empty and are at most 200 characters.
  • Order. seq is the position among siblings in the sidebar. Items with the same seq sort folders first, then by name.
  • Hidden and linked files. Files and folders whose name starts with . are ignored. Symbolic links are not followed anywhere in the workspace (they could point outside it).
  • Limits. YAML files over 50 MB are not read. The request tree is read at most 32 folder levels deep.
  • Broken files. A file that doesn’t parse shows in the sidebar with a warning (and the parse error) instead of breaking the workspace.
  • What is not in the workspace. Secret values, values set by scripts, cookies, OAuth tokens, history, load test results and the active environment live in the app’s data folder on each computer. See Data locations.

{{variables}} can be used in almost every text field of requests, folders and the workspace (URLs, headers, bodies, auth fields). See Variables.

The workspace file. A folder is a workspace when it has one.

zorvik.yaml
version: 1
id: 5b7f0c1e-2d44-4b8a-9d3a-1f0e6c2a9b71
name: Shop API
variables:
- { key: apiVersion, value: v2 }
- { key: apiKey, value: "", secret: true }
auth:
type: bearer
token: "{{accessToken}}"
headers:
- { key: X-Client, value: zorvik }
scripts:
postResponse: |
pm.test("No server error", () => pm.expect(pm.response.code).to.be.below(500));
docs: |
# Shop API
Local setup: `make run`, then use the Local environment.
FieldTypeDefaultMeaning
versionnumberFormat version, 1. A workspace with a newer version is refused: “This workspace was created by a newer Zorvik (format vN); please update the app”.
idstringgeneratedStable id: up to 64 letters, digits, - or _. Written on first open when missing. Together with the folder’s path, it keys this computer’s secrets, cookies and tokens for the workspace, so a copied id doesn’t unlock another workspace’s secrets.
namestringDisplay name.
variablesvariables[]Workspace (collection) variables.
authauthnoneDefault auth for requests and folders set to inherit.
headerskey/values[]Headers added to every request (a folder’s or the request’s header of the same name replaces it).
scriptsscriptsRun before and after every request of the workspace, before the folders’ and the request’s own.
docsstringMarkdown notes.

Edit it in the app with Workspace settings (name, default auth, default headers, scripts) and Environments (workspace variables).

Each request is one .yaml file (any case of the extension) under requests/. Folders are folders. A request’s path is its file path relative to requests/, with /: Users/List users.yaml. Load tests, agents and the command line refer to requests by this path.

requests/Users/Create user.yaml
name: Create user
seq: 2
method: POST
url: "{{baseUrl}}/users?notify=true"
paramDescriptions:
- { key: notify, description: Send a welcome email }
disabledParams:
- { key: dryRun, value: "true", description: Validate only }
headers:
- key: X-Request-ID
value: "{{$uuid}}"
body:
type: json
text: |
{ "name": "{{name}}", "email": "{{email}}" }
auth:
type: bearer
token: "{{token}}"
settings:
timeoutMs: 10000
scripts:
postResponse: |
pm.test("Status is 201", () => pm.response.to.have.status(201));
pm.environment.set("userId", pm.response.json().id);
docs: Handler in `src/users/create.ts`.
FieldTypeDefaultMeaning
namestringDisplay name (also the file name).
kindstringhttphttp, websocket, socketio, sse, grpc, tcp, udp, dns or mqtt.
seqnumber0Position among siblings.
methodstringGETHTTP method. gRPC: package.Service/Method. DNS: the record type (A, AAAA, MX …).
urlstring""The URL as typed, including the enabled query parameters. :name path segments take their values from pathParams.
disabledParamskey/values[]Query parameters switched off (not in url), with their descriptions.
pathParamskey/values[]Values of :name path segments.
paramDescriptionskey/values[]Descriptions of the enabled query parameters, by key (the url has no room for them).
headerskey/values[]Request headers.
bodybodynoneThe request body.
authauthinheritAuth. inherit takes the nearest folder’s, then the workspace’s.
settingssettingsPer-request overrides of the app’s request settings.
scriptsscriptsPre-request and post-response JavaScript.
socketsocketTCP and UDP options.
dnsdnsDNS options.
mqttmqttMQTT options.
grpcgrpcgRPC options.
socketiosocketioSocket.IO options.
docsstringMarkdown notes.
openapiobjectSet by an OpenAPI import: operation ("GET /pets/{petId}") and removed: true when the operation is no longer in the document.

Headers, query parameters, form fields and path parameters are lists of:

FieldDefaultMeaning
key""Name.
value""Value ({{variables}} allowed).
enabledtruefalse keeps the row but doesn’t send it.
description""Notes.

Data for every body type is kept, so switching the type in the app never loses what was typed; only type decides what is sent.

FieldMeaning
typenone (default), json, text, xml, formUrlencoded, multipart, binary or graphql.
textThe JSON, text or XML body. For WebSocket requests, the message draft; for Socket.IO, the arguments to emit; for gRPC, the JSON message; for MQTT, the message to publish.
contentTypeContent-Type of text bodies (default text/plain).
formKey/values of a formUrlencoded body.
multipartParts: key, value (text, or a file path when file: true), file, contentType, enabled.
fileThe file of a binary body: absolute, or relative to the workspace folder.
graphqlquery, variables (JSON text, may contain {{variables}}), operationName. Sent as JSON {"query", "variables", "operationName"}. For subscriptions also transport (websocket, websocketLegacy or sse), subscriptionUrl and connectionParams (JSON text); see GraphQL subscriptions.

Body files (binary bodies and multipart files) must be inside the workspace folder unless Files outside the workspace is on in Settings.

auth is an object with a type:

typeFields
inheritnone. Use the nearest folder’s auth, then the workspace’s. The default for requests and folders.
nonenone. No auth, even if a folder has some. The default for zorvik.yaml.
basicusername, password.
bearertoken; prefix (default Bearer).
apiKeykey (header or parameter name), value, location: header (default) or query.
oauth2See below.

OAuth 2.0 fields:

FieldDefaultMeaning
grantTypeclientCredentialsclientCredentials, password or authorizationCode.
tokenUrlToken endpoint.
authUrlAuthorization endpoint (authorization code).
redirectUrihttp://127.0.0.1:53682/callbackLoopback redirect registered with the provider (authorization code).
clientId, clientSecretClient credentials.
scope, audienceOptional.
username, passwordPassword grant.
clientAuthbasicHeaderbasicHeader (client id and secret in a Basic header) or body (form fields).
pkcetruePKCE (S256) for the authorization code grant.
headerPrefixBearerAuthorization header prefix; empty sends the bare token.

Tokens are cached in the app’s data folder, never in the workspace.

Each field overrides the app setting for this request only (see Settings).

FieldMeaning
timeoutMsWhole-request timeout; 0 = no limit.
followRedirectsFollow redirects.
maxRedirectsMost redirects followed.
verifyTlsVerify the server’s certificate.
httpVersionauto, http1, http2 or http3.
decompressDecode gzip, deflate, br and zstd responses.
repeatCollection runs: send again until a condition holds. condition (JavaScript, e.g. pm.response.json().status === "done"; empty = until the request’s tests pass, or without tests until the status is below 400), intervalMs (default 1000), timeoutMs (default 30000; then the request fails).
streamSSE requests in collection runs and for agents: when to stop reading. event (stop after the first event with this name; message for unnamed events), maxEvents (default 100; 0 = only the time limit), timeoutMs (default 10000).

Load tests don’t use a request’s own settings: they use the load test’s options and the app settings.

FieldMeaning
preRequestJavaScript run before sending.
postResponseJavaScript run after the response: tests with pm.test.

Workspace scripts run first, then the folders’ (outer to inner), then the request’s. See Scripts.

url is tcp://host:port (tls://host:port for TLS) or udp://host:port.

FieldDefaultMeaning
framingrawHow incoming bytes become messages: raw (as they arrive; one message per UDP datagram), line (one per line) or lengthPrefixed.
lengthBytes2Size of the big-endian length prefix: 1, 2 or 4.
lineEndingnoneAdded to each text message sent: none, lf or crLf.
broadcastfalseUDP: allow sending to broadcast addresses.

url is the name to look up and method the record type.

FieldDefaultMeaning
server""The resolver. Empty: the system’s. Otherwise 1.1.1.1, 8.8.8.8:53, tcp://1.1.1.1, tls://1.1.1.1 (DNS over TLS) or https://…/dns-query (DNS over HTTPS).
recursiontrueAsk for recursive resolution.

url is mqtt://host:1883 or mqtts://host:8883. The username and password come from basic auth; the message to publish is body.text.

FieldDefaultMeaning
clientId""Empty: a random id per connection.
versionv311v311 or v5.
cleanSessiontrue
keepAliveSecs30
subscriptions[]Topics subscribed after connecting: topic, qos, enabled.
topic""Topic the composer publishes to.
qos0QoS of published messages.
retainfalseRetain published messages.

url is grpc://host:port (plaintext HTTP/2) or grpcs://host:port (TLS), method is package.Service/Method, and body.text is the JSON message.

FieldMeaning
protoFiles.proto files, relative to the workspace folder or absolute. Empty: server reflection.
importPathsFolders searched for imports (the proto files’ folders are always searched).

url is the server and the namespace (http://localhost:3000/chat); body.text holds the arguments the composer emits.

FieldDefaultMeaning
path/socket.io/The server’s Socket.IO path.
transportautoauto (WebSocket, else long-polling), websocket or polling.
auth""The connection’s auth payload as JSON text (may contain {{variables}}).
event""The event the composer emits.
ackfalseThe composer asks for an acknowledgement.

url is the MCP server’s URL (http://localhost:3004/mcp) or the command that starts it (npx -y @modelcontextprotocol/server-everything). See MCP client.

FieldDefaultMeaning
transportautoauto (Streamable HTTP for http(s)://, falling back to HTTP+SSE; a program otherwise), streamableHttp, sse or stdio.
calltooltool, resource or prompt.
name""The tool or prompt name, or the resource URI (a template’s {parts} come from the arguments).
arguments""The arguments as JSON text (an object; may contain {{variables}}).
cwd""Programs: the folder they start in, relative to the workspace folder (empty: the workspace folder).
env[]Programs: environment variables (key, value, enabled).

A workspace can only start a program once you allowed that exact command, folder and environment on your computer; zorvik run needs --allow-programs.

WebSocket requests use ws:// or wss:// URLs and keep the message draft in body.text. SSE requests use http:// or https:// URLs; settings.stream says when runs stop reading.

A folder under requests/ is a folder in the collection. An optional _folder.yaml inside it holds its settings; requests inside inherit its auth, headers and scripts.

requests/Users/_folder.yaml
name: Users
seq: 1
auth:
type: apiKey
key: X-Api-Key
value: "{{apiKey}}"
headers:
- { key: Accept, value: application/json }
docs: Everything under /users.
FieldDefaultMeaning
namethe folder nameDisplay name.
seq0Position among siblings.
authinheritAuth for requests inside set to inherit.
headers[]Headers for requests inside (inner folders’ and the request’s headers of the same name win).
scriptsRun for every request inside, after the workspace’s and outer folders’ scripts.
docsMarkdown notes.
openapiSet by an OpenAPI import: spec (the kept document, e.g. specs/Payments API.yaml), source (the URL or file it came from, for Update from API spec), validate (default true: check responses against the document).

The collection root (requests/ itself) has no _folder.yaml: its settings are in zorvik.yaml.

One file per environment. The file name (without .yaml) is the environment’s id; renaming the environment renames the file.

environments/Staging.yaml
name: Staging
variables:
- { key: baseUrl, value: "https://staging.example.com" }
- { key: token, value: "", secret: true }
- { key: debug, value: "true", enabled: false }
FieldMeaning
nameDisplay name.
variablesThe environment’s variables.
FieldDefaultMeaning
keyName, used as {{key}}. Case-sensitive.
value""Value. Always empty in the file for secret variables.
enabledtruefalse keeps it but doesn’t define it.
secretfalseThe real value is kept on this computer only (the app’s secret store), never in the file.

Which environment is active is saved per computer, not in the workspace. Values set by scripts (pm.environment.set, pm.collectionVariables.set) are also kept on the computer and win over the file’s value. See Secret variables.

One file per mock API or server. The file name (without .yaml) is the server’s id. Only the section of the server’s kind is used; the others are kept, so switching the kind in the app never loses what was set up. See Mock APIs and Running servers.

servers/Payments mock.yaml
name: Payments mock
seq: 0
host: 127.0.0.1
port: 4010
http:
routes:
- name: Create payment
method: POST
path: /payments
status: 201
headers:
- { key: Content-Type, value: application/json }
body: '{"id": "{{$uuid}}", "status": "created"}'
delayMs: 120
- method: GET
path: /payments/:id
body: '{"id": "{{request.params.id}}", "status": "paid"}'
- method: POST
path: /refunds
fault: error
faultPercent: 20
fallback: proxy
proxyUrl: http://localhost:8080
cors: true
FieldDefaultMeaning
nameDisplay name.
kindhttphttp (mock API), mcp, websocket, socketio, sse, tcp, udp, dns or tcpProxy (a TCP relay that shows both directions).
seq0Position in the sidebar.
host127.0.0.1Address to listen on: 127.0.0.1 (this computer only) or 0.0.0.0 (other devices too).
port0Port; 0 = any free port. New servers made in the app get 3000 (HTTP), 3001 (WebSocket), 3002 (SSE), 3003 (Socket.IO), 3004 (MCP), 9000 (TCP), 9001 (UDP), 1053 (DNS) or 9100 (relay).
tlsoffenabled, certPath, keyPath (PEM). Without paths, a self-signed certificate for localhost is generated. For http, mcp, websocket, socketio, sse and tcp.
autoStartfalseStart with the workspace. Only configurations this computer has started or saved before start by themselves; one that is new or changed outside Zorvik (e.g. by a Git pull) must be started once by hand.
httpMock API: routes and fallback (below).
websocketWebSocket server: mode, greeting, rules.
socketioSocket.IO server (below).
mcpMCP server (below).
sseSSE server: events, intervalMs, repeat.
socketTCP and UDP servers: mode, greeting, rules, encoding, framing, lengthBytes, lineEnding.
dnsDNS server: records, upstream.
proxyTCP relay: target, upstreamTls.
docsMarkdown notes.
FieldDefaultMeaning
routes[]Tried in order: the first enabled route that matches answers.
fallbacknotFoundRequests no route matches: notFound (404 with a short explanation) or proxy (forward to proxyUrl).
proxyUrlBase URL of the real backend for fallback: proxy.
corsfalseAnswer CORS preflights and add Access-Control-Allow-* headers.

Route fields:

FieldDefaultMeaning
nameShown in the traffic log.
method*HTTP method, or * for any.
pathStarts with /. :name matches one segment, a trailing * the rest.
status200Status code.
headers[]Response headers.
body""Response body.
delayMs0Wait before answering.
matchQuery[]Only match requests with these query parameters (empty value = any value).
matchHeaders[]Only match requests with these headers (empty value = any value).
matchBody""Only match requests whose body contains this text.
faultnoneInstead of the answer: error (500), reset (close the connection) or hang (never answer).
faultPercent100How often the fault happens, in percent.
enabledtrue

Status, headers and body are templates: {{request.params.id}}, {{request.query.name}}, {{request.headers.name}}, {{request.body}}, {{request.method}}, {{request.path}}, dynamic values such as {{$uuid}}, and the active environment’s and workspace variables. Secret variables are never filled in, and text a client sends is never expanded.

FieldDefaultMeaning
modeechoecho (send every message back), rules (reply with the first matching rule), manual (only what you send from the app) or discard.
greeting""Sent to each client right after it connects (WebSocket, TCP).
rules[]Replies for mode: rules: match (any, contains (default), exact, regex), pattern, reply, delayMs, enabled.
encodingtextTCP and UDP: text or hex (e.g. 48 65 6c 6c 6f) for greeting, patterns and replies.
framingrawTCP: raw, line or lengthPrefixed.
lengthBytes2TCP, lengthPrefixed: 1, 2 or 4.
lineEndingnoneAppended to text replies: none, lf, crLf.
FieldDefaultMeaning
modeechoecho (emit every event back, acknowledge with its arguments), rules, manual or discard.
greetingEvent, greetingArgs""An event (and its JSON arguments) emitted to each client that joins a namespace.
rules[]For mode: rules: event (* for any), match (any (default), contains, exact, regex) and pattern on the arguments, ack (acknowledgement arguments), replyEvent and replyArgs, broadcast, delayMs, enabled.
path/socket.io/Where the server answers.
corsfalseAllow browsers on other origins.

See Socket.IO servers.

FieldDefaultMeaning
serverName, versionthe server’s name, 1.0.0What clients see in initialize.
instructions""Sent with initialize: how to use the server.
tools[]name, title, description, inputSchema and outputSchema (JSON Schema as JSON text), result, isError, delayMs, enabled.
resources[]uri (with {parts} for a template), name, title, description, mimeType, text, enabled.
prompts[]name, title, description, arguments (name, description, required), messages (role, text), enabled.
path/mcpThe Streamable HTTP endpoint (HTTP+SSE clients use /sse).
corsfalseAllow browser-based clients on other origins.

Results, resource texts and prompt messages can use {{args.name}}, {{args}}, {{params.name}}, dynamic and environment variables. See MCP servers.

FieldDefaultMeaning
events[]Sent in order to each client after it connects: event (empty: message), data, id.
intervalMs0Pause between events (0: all at once).
repeatfalseStart over after the last event.
FieldDefaultMeaning
records[]name (e.g. api.example.test or *.example.test), type (A, AAAA, CNAME, TXT, MX, NS, PTR, SRV, CAA), value (as in a zone file, e.g. 10 mail.example.test), ttl (default 60), enabled.
upstream""Names without a record: empty = “no such name”, system = this computer’s resolver, or a server such as 1.1.1.1.
FieldDefaultMeaning
target""host:port every client connection is relayed to.
upstreamTlsfalseConnect to the target with TLS (clients still talk plain TCP to the relay).

One file per load test. The file name (without .yaml) is its id, used by zorvik load and in the run history. See Load testing.

loadtests/Checkout smoke.yaml
name: Checkout smoke
seq: 0
dataFile: data/users.csv
targets:
- request: Auth/Login.yaml
captures:
- { variable: token, from: json, path: $.accessToken }
- request: Cart/Add to cart.yaml
weight: 3
- request: Checkout/Pay.yaml
model: virtualUsers
stages:
- { durationSecs: 10, target: 20 }
- { durationSecs: 60, target: 20 }
- { durationSecs: 10, target: 0 }
thinkTimeMs: 500
thresholds:
- { metric: p95, op: "<", value: 300 }
- { metric: errorRate, op: "<", value: 1 }
- { metric: p99, op: "<", value: 800, target: Checkout/Pay.yaml }
FieldTypeDefaultMeaning
namestringDisplay name.
seqnumber0Position in the sidebar.
targetslist[]Requests to send (below).
modelstringvirtualUsersvirtualUsers (closed model) or arrivalRate (open model, requests per second).
stageslist[]durationSecs and target (both required): ramp linearly to target users or requests per second over durationSecs.
thinkTimeMsnumber0Virtual users: pause after each answer.
maxInFlightnumber1000Arrival rate: most requests in flight; more are dropped.
keepAlivebooltrueReuse connections.
timeoutMsnumberapp settingPer-request timeout; 0 = no limit.
httpVersionstringapp settingauto, http1 or http2 (http3 is refused when the test starts).
thresholdslist[]Pass/fail rules (below).
dataFilestringCSV or JSON data file, relative to the workspace folder (or absolute).
docsstringMarkdown notes.

targets[]:

FieldDefaultMeaning
requestRequest path relative to requests/.
weight1How often, relative to the others. 0 = not sent.
enabledtruefalse = not sent.
captures[]variable, from (json (default), header, regex), path. See Captures.

thresholds[]:

FieldDefaultMeaning
metricp50, p90, p95, p99, p999, avg, max (ms), errorRate (%), rps (req/s).
op<, <=, >, >= (quote them in YAML).
valueThe limit.
targetA request path: only that request.
enabledtrue

See Thresholds. When a request is renamed or moved in Zorvik, load tests that send it are updated in place (their file name doesn’t change).

An OpenAPI import keeps the document in specs/, named after the import (.json when the document is JSON, else .yaml). The imported folder’s _folder.yaml points at it (openapi.spec), and each imported request names its operation (openapi.operation). Zorvik uses it to check every response of those requests against the documented status codes and schemas (a test named “Matches the API spec”), and to update the folder from a new version of the document. Documents up to 50 MB are read.

A workspace can hold any other files: data files for runs and load tests, files sent as bodies, .proto files. Zorvik reads them only where a request, run or load test refers to them. Paths in those fields are relative to the workspace folder (or absolute). By default, files outside the workspace folder can’t be used (Settings → Data & privacy → Files outside the workspace), so a workspace you clone can’t make Zorvik send your private files.

Zorvik creates requests/ and environments/ when it opens a workspace, and servers/, loadtests/ and specs/ when something is first saved there. It adds nothing else: no lock files, caches or .gitignore.