Skip to main content

Classic Scenario Recipes

Combine concepts and scripting into real-world workflows. Each scenario gives: goal → flow → scripts you can paste straight into the UI → expected result. All script logic has been verified end-to-end in the real app.

Examples use the local test server (/oauth2/token, /protected, /echo, /json, etc.). Swap in your own paths and Pre-URLs when pointing at your own APIs.


Scenario 1: Login for Token, Auto-Attach for the Whole Group​

Goal: call the login API, store the token, and have every subsequent request attach it automatically — no manual copying.

Request A — login (post-response script stores the token)​

POST /oauth2/token, form body: grant_type=password&username=qruser&password=qrpass&client_id=qr-client&client_secret=qr-secret

Add one script step to the post-response chain:

async function onResponse(context, url, response) {
var j = response.body.parseJSON();
if (j && j.access_token) {
sdk.env.set('authToken', j.access_token); // store on the runtime blackboard
sdk.env.set('tokenType', j.token_type || 'Bearer');
}
return response;
}

Request B — attach the token (the same script can be copied to every authorized API)​

GET /protected, add one script step to the pre-send chain:

async function onRequest(context, url, request) {
var tok = sdk.env.get('authToken'); // take from the blackboard
var ty = sdk.env.get('tokenType') || 'Bearer';
if (tok) { request.headers.set('Authorization', ty + ' ' + tok); }
return request;
}

Expected result​

ActionResult
Send A first (login)200, response contains access_token, token stored
Then send B (attach)200, response {authorized:true, ...}
Disable scripts, send B again401 (pre-send chain doesn't run → no token injected — proof the script is what attaches it)

Key: A and B must be in the same group bound to the same environment (runtime variables are scoped per environment), and path fragments are completed by the environment Pre-URL.


Scenario 2: AES-Encrypt the Request Body, Decrypt the Response (Multi-Script Chain)​

Goal: AES-encrypt the body before sending (only ciphertext on the wire), decrypt the response after it arrives; also demonstrates chaining multiple scripts and sharing data between them.

Why can it be split into multiple scripts? Scripts in the same chain share globalThis: script ① stashes the keys/plaintext, scripts ②③ pick them up. Each script stays single-purpose and can be toggled independently.

Pre-send chain​

Script ① (prepare context and keys):

async function onRequest(context, url, request) {
globalThis.__traceId = $uuid.v4();
globalThis.__aesKey = '0123456789abcdef'; // 16 bytes → AES-128
globalThis.__aesIv = 'abcdef0123456789';
globalThis.__plain = request.body.parseText();
request.headers.set('X-Trace-Id', globalThis.__traceId);
return request;
}

Script ② (encrypt the body):

async function onRequest(context, url, request) {
var cipher = $crypto.encryptAES(globalThis.__plain, globalThis.__aesKey, globalThis.__aesIv);
request.body = cipher; // only ciphertext on the wire
request.headers.set('X-Encrypt', 'AES-128-CBC');
return request;
}

(Optional) script ③ adds the token-attach logic from Scenario 1.

Post-response chain (decrypt + rewrite the response)​

async function onResponse(context, url, response) {
var j = response.body.parseJSON();
var cipher = j && j.body && j.body.utf8; // /echo echoes the request body in body.utf8
var recovered = cipher ? $crypto.decryptAES(cipher, globalThis.__aesKey, globalThis.__aesIv) : '';
var obj = null; try { obj = JSON.parse(recovered); } catch (e) {}
response.body = JSON.stringify({
traceId: globalThis.__traceId, decrypted: recovered,
roundtripOk: recovered === globalThis.__plain, jsonValid: !!obj,
});
return response;
}

Request body (before encryption): {"order":123,"note":"secret-payload","amount":99.5}

Expected result​

After sending you get:

{"traceId":"...","decrypted":"{\"order\":123,\"note\":\"secret-payload\",\"amount\":99.5}","roundtripOk":true,"jsonValid":true}
  • roundtripOk:true: the decrypted result matches the original plaintext byte for byte
  • The rewritten response body is visible in the result (proof the post-response rewrite took effect)

⚠️ $crypto arguments are positional strings; on failure it only returns a string starting with Error: — it does not throw or abort (verified: bad ciphertext → Invalid ciphertext, wrong key/iv → CryptoKit error). Chinese / non-ASCII plaintext encrypts and decrypts correctly (the old claim that "non-ASCII aborts" has been disproven). If you need to write a decrypted result into an HTTP header, make sure the value is ASCII (header values reject non-ASCII, otherwise a TypeError is thrown); put UTF-8 content in the body or query params instead.


Scenario 3: Extract · Assert · Dynamic Variables · Signing​

A set of lightweight automations — most need no JS at all, using declarative extract / assert steps.

3.1 Pass a response field to the next request (extract)​

  • Add an extract step to request A's post-response chain: variable author, source response body JSON, expression $.slideshow.author.
  • Add a script step to request B's pre-send chain: var v=sdk.env.get('author'); if(v){ request.headers.set('X-Extracted', v); } return request;

Verified: request B's /echo echoes an X-Extracted header matching the author in request A's response.

3.2 Assertions​

Add assert steps to the post-response chain: status code = 200, $.slideshow.title exists. Results are listed one by one (pass/fail, expected vs actual).

3.3 Dynamic variables​

Write them directly in params / headers / URL:

GET /echo?ts={{$timestamp}}&r={{$randomInt}}&u={{$uuid}}

Verified: every send echoes ts as the current timestamp, r as a different random integer, and u as a valid UUID. Full list: concepts, dynamic variables.

3.4 Signing (MD5, standard and interoperable)​

Many open APIs sign with MD5(param concatenation + secret). Use the verified-standard $hash.md5:

async function onRequest(context, url, request) {
var appId = sdk.env.get('appId');
var secret = sdk.env.get('appSecret'); // keep secrets in environment variables
var ts = String(Math.floor(Date.now() / 1000));
var sign = $hash.md5(appId + ts + secret); // concatenation order per your backend's convention
request.queries.set('appId', appId);
request.queries.set('ts', ts);
request.queries.set('sign', sign);
return request;
}

Verified: $hash.md5('abc') = 900150983cd24fb0d6963f7d28e17f72, matching the backend.

✅ The native $hmac ($hmac.sha256(message, key) etc.) has been vector-by-vector verified to match standard RFC HMAC (note the argument order: data first, key second) and can be used for external signing; the $hash family is standard too. Only $jwt degrades object payloads into [object Object] (stringify first with JSON.stringify, or sign on the server).


Scenario 4: Reuse Shared Logic with Library Scripts and require()​

Goal: extract "inject token", "unified signing" and "logging" into reusable units — reference them in many places, maintain them in one.

Two ways to reuse:

  • Library script step: a full script with its own onRequest/onResponse — add it as a step and it runs in order, sharing globalThis with the other steps.
  • require(): save pure functions in a script library with module.exports, then load them from any script — best for signing helpers, codecs and other shared utilities:
// script library "sign-lib" (no entry function needed)
function sign(method, path, body, secret) {
return $hmac.sha256(method + path + body, secret);
}
module.exports = { sign };
// any request script
async function onRequest(context, url, request) {
const { sign } = require('@libs/sign-lib');
request.headers.set('X-Sign', sign('POST', request.path, request.body.parseText(), sdk.env.get('appSecret')));
return request;
}

Use Copy require statement in the library manager for the stable identifier form. Built-in libs (@libs/base64, @libs/hex) are available everywhere without setup. Full mechanics: scripting, Require Script Libraries.


Scenario Cheat Sheet​

I want to…Which steps
Auto-attach the token on every API after loginPost-response script stores + pre-send script reads and injects the header
Encrypt the request body / decrypt the responsePre-send $crypto.encryptAES + post-response decryptAES
Pass a response field to the next requestExtract step + script reading the variable
Validate status codes / fieldsAssert step
Generate random values / timestamps each sendDynamic variables like {{$uuid}}
Sign an open API$hash.md5(...) (standard)
Reuse the same logic in many placesLibrary script, or require('@libs/<key>') a script library