Skip to main content

Examples

Six complete scripts you can paste into an AUTESSA_SCRIPT tool and adapt. Each one follows the house rules: inputs come in as strings through variables.get, helper results are JSON strings that need parsing, and the result always leaves through response.setResponse(JSON.stringify(...)).

All recipes assume a script tool execution context. In computed columns the context is slimmer — no connector or tool helpers, no responseStream.

Validate inputs and return a structured error​

Validate first, fail with a machine-readable error the agent can relay or retry on, and only do the work when the inputs hold up. Declared variables: email, amount.

const errors = [];

const email = variables.get("email");
if (!email || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
errors.push("email must be a valid email address");
}

const amount = parseFloat(variables.get("amount")); // tool variables are strings
if (isNaN(amount) || amount <= 0) {
errors.push("amount must be a positive number");
}

if (errors.length > 0) {
console.warn("validation failed:", errors.join("; "));
response.setResponse(JSON.stringify({ ok: false, errors: errors }));
} else {
response.setResponse(JSON.stringify({
ok: true,
email: email.trim().toLowerCase(),
amountCents: Math.round(amount * 100)
}));
}

Expected output: {"ok":true,"email":"ada@example.com","amountCents":4999} on good input; {"ok":false,"errors":["amount must be a positive number"]} when validation fails — either way the script succeeds and the agent gets something it can act on.

Fetch an API and reshape the response​

The classic transform tool: one api.get, two JSON.parse calls (envelope, then body), and a reshape so the agent sees only what it needs. Declared variables: apiToken.

const raw = api.get(JSON.stringify({
endpoint: "https://api.example.com/v1/orders?status=open",
headers: { "Authorization": "Bearer " + variables.get("apiToken") }
}));

const res = JSON.parse(raw); // parse 1: the envelope
if (res.error || res.statusCode !== 200) {
response.setResponse(JSON.stringify({
error: res.error ? res.errorMessage : "upstream returned " + res.statusCode
}));
} else {
const data = JSON.parse(res.body); // parse 2: the body string
const summary = data.orders.map(o => ({
id: o.id,
customer: o.customer.name,
total: o.line_items.reduce((sum, li) => sum + li.price * li.quantity, 0)
}));
console.log("summarized", summary.length, "open orders");
response.setResponse(JSON.stringify({ count: summary.length, orders: summary }));
}

Expected output: {"count":2,"orders":[{"id":"ord_1","customer":"Acme","total":129.5}, ...]} — a compact shape instead of the vendor's full payload.

Extract with the LLM, then act on the result​

A two-step LLM chain: extract structured fields from free text, then branch on them — here, drafting a reply only for cancellation requests. Declared variables: emailBody.

const body = variables.get("emailBody");

// Step 1: extract structured fields.
const extractRaw = llm.invoke(
"You extract fields from customer emails. Respond with only a JSON object: " +
"{\"intent\": \"cancellation|billing|question|other\", \"urgency\": \"low|high\"}. " +
"No prose, no code fences.",
body,
100
);
const extract = JSON.parse(extractRaw);
if (!extract.success) {
response.setResponse(JSON.stringify({ error: extract.errorCode + ": " + extract.errorMessage }));
} else {
let fields;
try {
fields = JSON.parse(extract.content);
} catch (e) {
fields = { intent: "other", urgency: "high" }; // format drift fallback
}
console.log("extracted:", JSON.stringify(fields));

// Step 2: branch — only cancellations get a drafted reply.
let draft = null;
if (fields.intent === "cancellation") {
const draftRaw = llm.invoke(
"You draft short, empathetic customer-service replies. Plain text only.",
"Draft a reply acknowledging this cancellation request and asking for feedback:\n\n" + body,
300
);
const draftRes = JSON.parse(draftRaw);
if (draftRes.success) draft = draftRes.content;
}

response.setResponse(JSON.stringify({ intent: fields.intent, urgency: fields.urgency, draft: draft }));
}

Expected output: {"intent":"cancellation","urgency":"high","draft":"Hi Sam, we're sorry to see you go..."} — or "draft": null for non-cancellations.

Read-modify-write AutessaDB rows through tools​

The script-as-orchestrator pattern from the tools helper: read a row with a published get tool, decide in JavaScript, write back with an update tool. Here, tool aB3dE5fGh1 fetches a ticket row and J9kL2mNpQ4 updates it. Declared variables: rowId.

const row = JSON.parse(tools.invokeById("aB3dE5fGh1", JSON.stringify({
rowId: variables.get("rowId")
})));
if (row && row.error) {
response.setResponse(JSON.stringify({ error: row.errorCode + ": " + row.errorMessage }));
} else {
// Decide: escalate tickets with more than 3 replies that aren't already high priority.
const replyCount = parseInt(row.reply_count) || 0;
if (replyCount > 3 && row.priority !== "high") {
const update = JSON.parse(tools.invokeById("J9kL2mNpQ4", JSON.stringify({
rowId: variables.get("rowId"),
values: { priority: { textValue: "high" } }
})));
if (update && update.error) {
response.setResponse(JSON.stringify({ error: update.errorCode + ": " + update.errorMessage }));
} else {
console.log("escalated ticket", variables.get("rowId"));
response.setResponse(JSON.stringify({ escalated: true, replyCount: replyCount }));
}
} else {
response.setResponse(JSON.stringify({ escalated: false, replyCount: replyCount }));
}
}

Expected output: {"escalated":true,"replyCount":5} when the threshold is crossed, {"escalated":false,...} otherwise. Every side effect goes through a published, user-visible tool.

Call a vendor API with a connector token​

When you already know the vendor endpoint, connector.getAccessToken plus api.get is the whole pattern — the platform refreshes the OAuth token for you. Here, connector 15 is a HubSpot connection.

const tok = JSON.parse(connector.getAccessToken(15));
if (!tok.success) {
response.setResponse(JSON.stringify({ error: tok.errorCode + ": " + tok.errorMessage }));
} else {
const res = JSON.parse(api.get(JSON.stringify({
endpoint: "https://api.hubapi.com/crm/v3/objects/contacts?limit=10&properties=email,firstname",
headers: { "Authorization": "Bearer " + tok.accessToken }
})));

if (res.statusCode !== 200) {
response.setResponse(JSON.stringify({ error: "HubSpot returned " + res.statusCode }));
} else {
const page = JSON.parse(res.body);
response.setResponse(JSON.stringify({
contacts: page.results.map(c => ({
id: c.id,
email: c.properties.email,
firstName: c.properties.firstname
}))
}));
}
}

Expected output: {"contacts":[{"id":"51","email":"ada@example.com","firstName":"Ada"}, ...]}. Use connector.listConnectors() first if you don't know the connector id.

Stream progress from a long-running script​

For scripts that poll or loop, responseStream keeps the caller informed while the work runs. It only exists in streaming executions, so guard every use — the same script then also works in non-streaming runs. Declared variables: jobUrl.

function progress(msg) {
if (typeof responseStream !== "undefined") {
responseStream.addStreamMessage(msg);
}
console.log(msg); // always land it in the logs too
}

progress("Starting export job...");
const start = JSON.parse(api.post(JSON.stringify({ endpoint: variables.get("jobUrl") })));
if (start.statusCode !== 202) {
response.setResponse(JSON.stringify({ error: "job start returned " + start.statusCode }));
} else {
const statusUrl = JSON.parse(start.body).statusUrl;

let status = "pending";
for (let attempt = 1; attempt <= 10 && status === "pending"; attempt++) {
waitUtils.sleep(3000); // 3s between polls
const poll = JSON.parse(api.get(JSON.stringify({ endpoint: statusUrl })));
status = JSON.parse(poll.body).status;
progress("Poll " + attempt + "/10: " + status);
}

response.setResponse(JSON.stringify({
status: status,
done: status === "complete"
}));
}

Expected output: the caller sees Starting export job..., Poll 1/10: pending, ... as they happen, then the final result {"status":"complete","done":true} (or {"status":"pending","done":false} if ten polls weren't enough).