Echolot
Requests

Scripts and tests

Check every response without code in the Tests tab, or with JavaScript in pre-request and test scripts.

Tests tell you whether a response is what you expect. Echolot gives you two ways to write them: checks you set up in a request's Tests tab, with no code, and scripts in JavaScript that run before a request is sent and after it is answered. Both run after every send, in the runner and on the command line.

Checks in the Tests tab

The Tests tab of List products with four checks, each with a green tick: status equals 200, JSON value $.items.length is greater than 0, header Content-Type contains json, response time is less than 1000
  1. Open a request, for example Catalog › List products in the Alpenglow workspace.
  2. Choose the Tests tab.
  3. Choose Add Test and pick what to check.
  4. Pick a comparison from the menu beside it and type the expected value.
  5. Send the request. Each check shows a green tick or a red cross, and a failed one says why under it.

Checks are saved with the request and run on every response from then on. The box at the start of a row turns a check off without removing it; the minus button removes it. The Tests tab's badge counts the checks, and the bottom of the tab shows how many passed, such as 4 passed or 1 of 4 failed.

The expected value is a template, so {{variables}} work in it: Catalog › Get product checks that $.sku equals {{productId}}.

What you can check

CheckWhat it readsComparisons
StatusThe status codeequals, does not equal, is less than, is greater than, is one of
HeaderA response header, by nameexists, does not exist, equals, does not equal, contains, does not contain, matches regex
JSON valueThe value at a JSONPath in a JSON bodyexists, does not exist, equals, does not equal, contains, does not contain, is less than, is at most, is greater than, is at least, matches regex, is of type, is one of
BodyThe whole body as textcontains, does not contain, equals, matches regex
Response time (ms)The total time of the exchange, in millisecondsis less than, is at most, is greater than, is at least
Size (bytes)The body's size in bytesis less than, is at most, is greater than, is at least
JSON SchemaThe body, against a JSON Schema you pastematches
GraphQL errorsThe errors array of a GraphQL responseequals, is less than, is greater than (their number); contains, does not contain (their messages)
MCP errorWhether an MCP call failed: true or falseequals
Tool output schemaAn MCP tool result's structuredContent, against the tool's own outputSchemamatches
OpenAPI specThe body, against the linked OpenAPI spec's schema for the response's statusmatches
gRPC statusA gRPC call's status, by name or codeequals, does not equal, is one of

A new check starts with a sensible value: Status equals 200, Response time (ms) is less than 1000, GraphQL errors equals 0, MCP error equals false, gRPC status equals OK, and JSON Schema with {"type": "object"}.

How values compare

  • equals compares numbers as numbers, so 200 equals 200.0, and ignores spaces around the value.
  • is one of takes a comma-separated list: 200, 201, 204.
  • matches regex passes when the regular expression matches anywhere in the value.
  • is of type takes object, array, string, number, integer, boolean or null.
  • The number comparisons need numbers on both sides; anything else fails with a message saying so.

JSON values

The path is a JSONPath of keys and indexes, starting with $: $.items[0].sku, $.data.search.totalCount. A negative index counts from the end: $.items[-1]. Add .length to count the items of an array or the keys of an object: $.items.length is greater than 0. Echolot reads only the part of the body on the way to the value, so JSON checks work on bodies of any size.

Body with contains or does not contain also works on bodies of any size. The other body comparisons, and JSON Schema and GraphQL errors, read the whole body and stop at 50 MB.

Checks for other kinds of request

  • GraphQL: servers answer errors with status 200, so add GraphQL errors equals 0 to catch them. Use contains to check that a particular message comes back.
  • MCP calls: MCP error equals false passes when the call succeeded. It fails on a result marked as an error and on a JSON-RPC error, and the message shows what the server said. Tool output schema checks a tool result against the schema the tool declares; it fails when the tool declares none. See MCP requests.
  • gRPC: gRPC status takes names such as OK and NOT_FOUND or codes from 0 to 16; 5, not_found and NOT_FOUND are the same status. A failed check shows the status message. See gRPC.
  • OpenAPI spec: the request's folder (or a folder above it) must be linked to a spec, and the request must follow one of its operations. The check then validates the body against the schema the spec documents for the status that came back. A status the spec documents without a JSON body passes; an undocumented status fails. See Specs.

Add checks from a response

You don't have to type every check:

  • Generate from Response… in the Tests tab proposes checks from the latest response. Checks that hold for any good response, such as the status, the content type and the shape of the body as a JSON Schema, are ticked already. Values that change between calls, and the time, are offered but not ticked. Choose the inferred schema to read a proposed schema. Choose Add 1 Test (or Add N Tests) to keep the ticked ones. Send the request first: the button needs a response.
  • In the response's Body in the JSON tree view, right-click a value and choose Add Assertion: it adds a JSON value check that the path equals that value.
  • In the response's Headers, right-click a header and choose Add Check: it adds a Header check that the header equals that value.
The Generate Tests from Response sheet for List products, with the status, the content type and the inferred schema ticked, and a value check offered but not ticked

Results

The response pane's Tests tab lists each check with its outcome and the value found (got 200), then each test from the scripts. The Tests page in the sidebar (View › Tests, ⌥⌘T) lists every request that has checks, with how they stand in its latest response; click one to open the request.

Scripts

Scripts are JavaScript with Postman's pm.* API, so most Postman scripts run unchanged. A request can have two:

  • A pre-request script runs before the request is sent. It can change the request, set variables, or skip the request.
  • A test script runs after the response arrives. It checks the response with pm.test and can save values for later requests.
  1. Open a request and choose the Scripts tab.
  2. Choose Pre-request or Tests at the top. A dot after the name, such as Tests •, marks a script that has code.
  3. Write the script. The editor colours JavaScript and completes pm. paths as you type. Snippets adds a ready-made script for common cases.
  4. Send the request.

For example, a test script for Catalog › List products:

pm.test("Status code is 200", () => {
  pm.response.to.have.status(200);
});

pm.test("Returns tents", () => {
  const body = pm.response.json();
  pm.expect(body.items.length).to.be.above(0);
});

And a pre-request script that adds a header to every request it runs for:

pm.request.headers.add({ key: "X-Request-Id", value: pm.variables.replaceIn("{{$guid}}") });

Each pm.test appears in the response's Tests tab under Scripts, after the checks from the Tests tab. A script that throws shows as Script error with its message. What scripts write with console.log, console.info, console.warn and console.error appears in the response's Console tab, marked pre or test; the tab is there once scripts have run.

Snippets

SnippetFor
Status code is 200, Response time is below 500 ms, JSON value exists, Header is present, Body contains textTests
Save a value for later requestsTests: pm.environment.set("token", pm.response.json().token)
Run another request nextTests: pm.execution.setNextRequest("Request name")
Set a variable, Add a header, Sign the request with HMAC-SHA256, Get a token first, Skip this requestPre-request

Scripts on folders and the workspace

A folder and the workspace can have scripts too, for every request inside them. Open them from the folder's context menu in the sidebar (Folder Settings…) or from Workspace Settings…, on the Scripts tab.

For each request, Echolot runs the workspace's script first, then each folder's from the outside in, then the request's own. The same order holds for pre-request and test scripts. Only saved requests run scripts.

MCP calls have no Scripts tab and run no scripts. Every other kind of request has one.

The pm API at a glance

The full list is in the scripting API reference.

ObjectWhat it does
pm.test(name, fn), pm.expect(value)A named test and Chai-style assertions: pm.expect(x).to.equal(1)
pm.responseThe response in test scripts: code, status, headers, responseTime, responseSize, json(), text(), and pm.response.to.have.status(200), .header(), .body(), .jsonBody(), pm.response.to.be.ok, .success, .json, .clientError, .serverError, .notFound
pm.requestThe request: url, method, headers, body; a pre-request script can add, replace and remove headers and query parameters
pm.variablesEvery scope, closest first: get, set, has, unset, replaceIn, toObject
pm.environment, pm.collectionVariables, pm.globalsThe active environment, the workspace's variables, and global variables
pm.iterationDataThe data row of a run: get, has, toObject
pm.inforequestName, requestId, iteration (from 0), iterationCount, eventName
pm.executionsetNextRequest(name) (null ends the run) and skipRequest()
pm.cookiesThe response's cookies
pm.sendRequestSends another request
consolelog, info, warn, error
CryptoJSMD5, SHA1, SHA256, SHA512, HmacSHA1, HmacSHA256, HmacSHA512, and CryptoJS.enc (Hex, Base64, Utf8, Latin1)

require gives lodash, ajv, tv4, crypto-js, uuid, atob and btoa. _ (lodash), tv4, btoa, atob and setTimeout are also there without it. Postman's older globals (postman.setEnvironmentVariable, tests[...], environment, globals, data, iteration) still work. Any other module fails with "The module … is not available in Echolot scripts."

Variables that scripts set

What pm.environment.set, pm.collectionVariables.set and pm.globals.set write is a current value: it lives as long as the window, overrides the saved value of that scope when requests resolve, and is never written to the workspace files. So a token a script saves reaches later requests without ending up in Git. To keep a value from a response in the workspace, use a capture instead.

pm.variables.set sets a value for the current run only: a run starts without the values scripts set this way.

Sending another request

pm.sendRequest sends a request from a script, such as one that fetches a token first. It takes a URL or an object with url, method, header and body, and either a callback or the promise it returns:

pm.sendRequest({
  url: pm.variables.replaceIn("{{baseURL}}/auth/token"),
  method: "POST",
  header: { "Content-Type": "application/json" },
  body: { mode: "raw", raw: JSON.stringify({ email: "heidi@alpenglow.ch", password: "alpenglow" }) },
}, (error, response) => {
  if (!error) pm.environment.set("token", response.json().access_token);
});

The request goes out like any other send from Echolot, with the same proxy and certificates, but without the workspace's or folder's auth. Scripts wait up to 30 seconds for the answers.

Skipping requests and choosing the next one

pm.execution.skipRequest() in a pre-request script stops the request from being sent; the response pane and the runner say it was skipped. pm.execution.setNextRequest("Get order") in a run jumps to that request next, and setNextRequest(null) ends the run. See the runner.

Scripts from outside Echolot

Scripts are code, so Echolot runs only the ones you trust on this Mac. Scripts you write in Echolot are trusted as you save them. Scripts that arrive another way, from an import, from Git, or from an edit in another editor, wait:

  • The Scripts tab shows These scripts came from outside Echolot, with Trust Scripts.
  • Sending the request stops with Review Scripts…. It opens Run these scripts?, with each script that has not run yet and where it comes from (the request, a folder or the workspace). Read them, then choose Trust and Send, or Cancel.

Trust is kept on this Mac, per workspace, for the exact code: when the script changes outside Echolot, it asks again. The trust never travels with the workspace, so a shared workspace cannot trust its own scripts.

The command-line tool runs every script of the workspace you give it. echolot mcp serve runs only the scripts the workspace had when it started.

The sandbox and its limits

Scripts run in a JavaScript sandbox inside Echolot. They have no file system, no network of their own and no access to the Mac: the only ways out are pm.sendRequest, console and the variables. Every send starts a fresh sandbox.

LimitValue
JavaScript time for all the scripts of one event (pre-request or test)5 seconds
Memory128 MB
Waiting for pm.sendRequest answers30 seconds
Response body a script seesThe first 32 MB

A script that runs too long fails with "the script took longer than 5 s"; one that uses too much memory fails with "the script used more memory than allowed". Scripts can still read the workspace's variables, secrets included, and send them with pm.sendRequest, which is why scripts from outside wait for your trust.

Troubleshooting

  • Generate from Response… is greyed out: send the request first. It works for HTTP and GraphQL requests.
  • "no OpenAPI spec is linked to this request's folder": link the folder to a spec, or remove the OpenAPI spec check.
  • "“…” is not a JSONPath this version understands": use keys and indexes only, such as $.items[0].sku.
  • A script doesn't run: check that the request is saved, that it isn't an MCP call, and that the script isn't waiting for your trust.

On this page