{{baseUrl}} in request URLs.// hephaestus.defaults { "baseUrl": "https://api.example.com" } // per-request override (different server) const override = { baseUrl: "https://auth.example.com" };
baseUrl has no protocol. Values: https | http.// Single status const override = { expectedStatus: 204 }; // Multiple statuses const override = { expectedStatus: [200, 201] }; // Negative testing const override = { expectedStatus: 422 };
true for requests that return an empty body (e.g. 204 No Content). Skips body parsing.const override = { expectedStatus: 204, expectEmpty: true };
const override = { maxResponseTime: 3000 }; // 3 seconds
false, the auth block is ignored entirely.// Basic auth const override = { auth: { enabled: true, type: "basic", user: "{{login}}", pass: "{{password}}" } }; // Bearer const override = { auth: { enabled: true, type: "bearer", token: "{{prod.token}}" } }; // OAuth2 client_credentials — auto-refresh const override = { auth: { enabled: true, type: "oauth2cc", oauth2cc: { tokenUrl: "https://auth.example.com/oauth/token", clientId: "{{oauth_client_id}}", clientSecret: "{{oauth_client_secret}}", scope: "api:read api:write" } } };
{{currentDate}} and custom date variables. Tokens: yyyy MM dd hh mm ss.Built-in variables (always available): {{currentDate}}, {{monthsAgo1}}, {{monthsAgo3}}, {{monthsAgo6}}, {{monthsAgo12}}
today, today+7d, today-1m, startOfMonth, etc.const override = { dates: { "dateFrom": "startOfMonth", "dateTo": "endOfMonth", "nextWeek": "today+7d", } }; // Use {{dateFrom}}, {{dateTo}}, {{nextWeek}} in request URL/body
Expressions: today · yesterday · tomorrow · startOfMonth · endOfMonth · startOfNextMonth · startOfPrevMonth · startOfYear · today±Nd · today±Nw · today±Nm · today±Ny
exists, absent, eq, ne, gt/gte/lt/lte, type, minLen/maxLen, includes, matches, soft, when.const override = { assertions: { "data.id": { exists: true }, "data.status": { eq: "active" }, "data.count": { gte: 1, lte: 100 }, "data.items": { type: "array", minLen: 1 }, "data.email": { matches: "@" }, "meta.error": { absent: true }, "data.token": { exists: true, soft: true }, "data.extra": { exists: true, when: "ctx.api.status === 200" }, } };
assertions map. Violations are aggregated into a single test.const override = { assertEach: { path: "data.items", minCount: 1, maxCount: 100, rules: { "id": { type: "number", gt: 0 }, "status": { eq: "active" }, "email": { matches: "@", soft: true }, } } };
string | number | boolean | object | array | null | any | absent.const override = { assertShape: { "data": "object", "data.id": "number", "data.name": "string", "data.items": "array", "data.active": "boolean", "meta": "any", // exists, any type "error": "absent", // must NOT be present } };
200 even when the body carries an errors[] array — this catches that. graphql: true is shorthand for { noErrors: true }.const override = { graphql: { noErrors: true, // errors[] empty / absent errorCount: 2, // OR exactly N errors (negative testing) errorContains: "not found", // some error.message contains this dataShape: { "user.id": "number" } // type checks under data.* } };
dataShape paths are relative to data (so "user.id" checks data.user.id) and use the same type set as assertShape. Every check is independent; combine as needed.
const override = { assertOrder: { path: "data.items", by: "createdAt", direction: "desc", // "asc" | "desc" type: "date" // "string" | "number" | "date" } };
expect, transform, filter, ignoreCase, soft, when.const override = { keysToFind: [ { path: "data.id", name: "User ID" }, { path: "data.status", expect: "active" }, { path: "data.score", expect: v => v >= 0 }, { path: "data.role", soft: true, when: "ctx.api.status !== 404" }, ] };
const override = { varsToSave: { token: { path: "data.accessToken", scope: "collection", name: "prod.token" }, userId: { path: "data.user.id", scope: "environment", name: "USER_ID" }, } };
Scope values: collection (default) · environment · local
const override = { assertHeaders: [ { name: "X-Request-Id" }, // exists { name: "Content-Type", expect: "application/json" }, // contains { name: "X-Version", equals: "v2" }, // exact { name: "X-Deprecated", absent: true }, // must be absent { name: "X-Rate-Limit", label: "Rate limit > 0", expect: v => Number(v) > 0 }, // predicate ] };
const override = { snapshot: { enabled: true, mode: "non-strict", // "strict" | "non-strict" | "structural" autoSaveMissing: true, // save on first run checkPaths: ["data.status", "data.items[*].id"], ignorePaths: ["data.timestamp", "data.requestId"], } };
strict — full deep-equal comparison (with ignorePaths). non-strict — all baseline keys must be present in current response (allows new keys). structural — compares the shape only (every leaf path → its type, array indices collapsed to [*]): catches a field added/removed or a type change, but ignores volatile values (timestamps, ids, counts) and array length.
Stored in hephaestus.snapshots collection variable. View with Snapshot Viewer or 📋 snapshot-view request.
collection-vars is the only backend that works offline; postman-api is unavailable in the local/Newman runtime — it warns once and falls back to collection-vars.const override = { snapshot: { enabled: true, storage: "collection-vars" } }; // storage: "postman-api" → warns once, then uses collection-vars
snapshotRecord: true.// 1. Set record: true and run the request ONCE const override = { snapshot: { enabled: true, record: true } }; // equivalent top-level form: const override = { snapshotRecord: true }; // 2. Remove record after the baseline is rewritten
Use after an intentional API change to re-baseline. Unlike autoSaveMissing (which only saves when no baseline exists), record overwrites an existing baseline.
tv4 library.const override = { schema: { enabled: true, definition: { type: "object", required: ["id", "status"], properties: { id: { type: "number" }, status: { type: "string", enum: ["active", "inactive"] }, } } } };
// Set in collection Pre-request Script: pm.collectionVariables.set('hephaestus.plugins', JSON.stringify([ { name: 'slack-notifier', code: pm.collectionVariables.get('hephaestus.plugin.slack') }, { name: 'custom-assertions', code: pm.collectionVariables.get('hephaestus.plugin.custom') }, ])); // Plugin receives (ctx) with: // ctx.api.body, ctx.api.status, ctx.api.headers, ctx.api.responseTime // ctx.config, ctx.request, ctx.iteration, ctx._meta
// In hephaestus.defaults (all requests): { "envRequired": ["BASE_URL", "OAUTH_CLIENT_ID"] } // Per-request override: const override = { envRequired: ["PAYMENT_API_KEY"] };
*** in console output.true, emits a structured [HEPHAESTUS_CI] {json} line to console after each request. Used by scripts/ci-to-junit.js and CI pipelines.// Enable in defaults for CI runs: { "ci": true } // Or via Newman --env-var: newman run col.json --env-var ci=true
const override = { retryOnStatus: { statuses: [503, 429], // retry on these HTTP codes maxRetries: 3, // default: 3 respectRetryAfter: true, // opt-in: honor the Retry-After header retryAfterCapMs: 10000 // max wait honored (default: 10s) } };
Counter stored in pm.variables (key: hephaestus.retry.<requestName>), auto-cleared on success or exhaustion.
Retry-After (respectRetryAfter: true): on a retried response the engine reads the server's Retry-After header (delta-seconds or HTTP-date) and blocks that long before re-running, up to retryAfterCapMs. If the requested backoff exceeds the cap it stops retrying instead of hammering. The wait is a bounded blocking busy-wait — the Postman sandbox has no async sleep that survives setNextRequest.
true, ALL assertion failures (keysToFind, assertions, assertEach, assertShape, assertOrder, assertUnique) are logged to console but do NOT fail the test run. Ideal for smoke testing.// In hephaestus.defaults — smoke test mode: { "softFail": true } // Per-request override — just this request is soft: const override = { softFail: true };
ci: true) is always emitted regardless of logLevel.// silent — no console output (CI JSON still written) // minimal — one compact line per request: [H] GET Users → ✅ 200 | 123ms | 🔎×3 // normal — full box layout (default) // verbose — box + response headers (first 10) { "logLevel": "minimal" }
softFail.const override = { assertUnique: { path: "data.items", // path to array by: "id", // field to extract (optional — compares whole item if omitted) label: "item IDs" // optional test label } };
pm.variables with random test data before the request. Use {{varName}} in URL, body, and headers.// In override or hephaestus.defaults: const override = { randomData: { email: "random.email", // "user_a3f2@test.com" userId: "random.int:1:9999", // "4287" token: "random.uuid", // "550e8400-..." name: "random.str:12", // "xk8mP2nQ7w3b" active: "random.bool", // "true" dob: "random.date", // "1994-07-21" } }; // ctx.random also available directly in plugins: // ctx.random.uuid(), .email(), .str(n), .int(min,max), .float(), .bool(), .pick(arr), .date()
"ru" is byte-identical to prior releases; "en" is the exact same engine rendered in English.// In hephaestus.defaults — switch the whole run to English: { "locale": "en" } // Per-request override: const override = { locale: "en" };
Backed by the i18n catalog in engine/src/shared/i18n.js. Only the presentation layer changes — assertions, snapshots, and behaviour are unaffected.
pm.test: required security headers present, server-disclosure headers absent, no error/stack-trace leakage in the body, and a CORS check for wildcard Access-Control-Allow-Origin combined with Allow-Credentials.// Enable with built-in defaults: const override = { securityAudit: { enabled: true } }; // Full shape — every field is optional and falls back to the built-in list: const override = { securityAudit: { enabled: true, checkCors: true, // flag wildcard ACAO + Allow-Credentials requireHeaders: ["strict-transport-security", "content-security-policy", "x-frame-options", "x-content-type-options"], forbidHeaders: ["server", "x-powered-by", "x-aspnet-version"], forbidBodyPatterns: ["SQLSTATE", "stack trace", "Traceback (most recent call last)", "ORA-0", "db error", "Warning: mysql"], } };
requireHeaders — each must be present. forbidHeaders — server-disclosure headers that must be absent. forbidBodyPatterns — substrings that must NOT appear in the body (leaked errors/stack traces). checkCors — fails when Access-Control-Allow-Origin: * is paired with credentials. Providing a list replaces the corresponding built-in default for that field.
defaults / override key. Point your editor at it to get key autocomplete, enum hints, and inline error highlighting while editing config — no extension required.Shipped by default. setup/defaults.json already carries a $schema key, so opening it in VS Code (or any editor that honours $schema) gives autocomplete, enum hints and inline error highlighting straight away — no extension, no settings:
{
"$schema": "../docs/override.schema.json",
"baseUrl": "https://api.example.com"
}
The build strips $schema when it embeds the defaults into the template collection, so the shipped engine config stays clean — the key is purely an editor hint. Add the same line to any other config JSON you edit to associate it too.
Prefer a workspace mapping? Instead of the in-file key, map the schema in .vscode/settings.json (git-ignored, so create it locally):
// .vscode/settings.json { "json.schemas": [ { "fileMatch": ["setup/defaults.json"], "url": "./docs/override.schema.json" } ] }
The schema autocompletes known keys and tolerates unknown ones (additionalProperties is open), so custom plugin fields are never flagged. Remove any $schema / _comment keys before pasting a config into collectionVariables["hephaestus.defaults"] — the engine ignores them, but they keep the stored config clean.