Skip to content

DocsUse it from

4 min read · updated

JavaScript and TypeScript

Call a server from Node with fetch, in JavaScript or TypeScript, with no package.

A server is a JSON API, and fetch is built into Node 22, so JavaScript needs no package either. The same script is shown in TypeScript, with the shape of an answer typed.

Figure · A JavaScript client

One POST out, one JSON answer back; the rest is reading it.

The numbers are the recorded decision's; a server on another base answers with its own.

Recorded decision: Gemma 4 31B, decisio 0.9.0, RTX PRO 6000 at 600 W, 2026-10-08; record 1 of 60 of decisio-examples' support-routing run; the probabilities are rounded to two places.

With a server running (Get started starts one), save this as ask.mjs and run node ask.mjs; it sends the recorded support ticket and prints each question’s likeliest option:

const body = {
"state": "I was charged twice for October. Both charges are 49.00 EUR, same day. Please refund one.",
"questions": {
"urgent": {
"type": "noul",
"instructions": "Does this ticket need a response within the hour?"
},
"queue": {
"type": "choice",
"instructions": "Which team should handle this ticket?",
"criteria": {
"billing": "Invoices, payments, refunds, prices, VAT",
"access": "Login, passwords, two-factor, SSO, permissions, invitations, account security",
"bug": "The product behaves wrongly: errors, crashes, wrong numbers, lost data",
"feature": "A request for something the product does not do yet",
"cancellation": "Cancelling, pausing or closing an account, deleting data",
"other": "Press, partners, jobs, sales pitches, wrong address, thanks, general questions"
}
},
"impact": {
"type": "score",
"instructions": "Rate the business impact using only the reported facts.",
"criteria": [
"No function impaired",
"One user impaired, with a workaround",
"Many users blocked from a core function",
"Data loss or legal exposure"
]
}
}
};
const response = await fetch("http://127.0.0.1:8000/v1/systemone", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
const { answers } = await response.json();
for (const [name, answer] of Object.entries(answers)) {
if (answer.type === "noul") {
console.log(name, Number(answer.noul.toFixed(3)));
} else {
const [top, p] = Object.entries(answer.probabilities).sort((a, b) => b[1] - a[1])[0];
console.log(name, top, Number(p.toFixed(3)));
}
}
// urgent 0.159
// queue billing 0.962
// impact 1 0.648

Run against the CPU stand-in at v0.11.1 on 2026-10-14: the answers are the stand-in's, not the measured system's.

The comment lines show what the measured system answered (recorded; the stand-in prints other numbers).

Recorded decision: Gemma 4 31B, decisio 0.9.0, RTX PRO 6000 at 600 W, 2026-10-08; record 1 of 60 of decisio-examples' support-routing run; the printed probabilities are rounded to three places.

WordMeaning
noula yes/no question; its answer is the probability of yes
probabilitiesa probability for each option of a choice, or each level of a score; they add up to 1
top-level awaitawait outside a function, which a .mjs file allows

The script posts the body as JSON and reads answers, which holds one answer per question under the name you gave it. Check response.ok before reading: a request the server cannot read is answered with 422, and one the engine cannot answer with 400, each with a detail in the body.

Types for the parts of the answer the script reads, and nothing else; Node 22 runs this file by stripping the types (node --experimental-strip-types ask.ts):

const body: { state: string; questions: Record<string, unknown> } = {
"state": "I was charged twice for October. Both charges are 49.00 EUR, same day. Please refund one.",
"questions": {
"urgent": {
"type": "noul",
"instructions": "Does this ticket need a response within the hour?"
},
"queue": {
"type": "choice",
"instructions": "Which team should handle this ticket?",
"criteria": {
"billing": "Invoices, payments, refunds, prices, VAT",
"access": "Login, passwords, two-factor, SSO, permissions, invitations, account security",
"bug": "The product behaves wrongly: errors, crashes, wrong numbers, lost data",
"feature": "A request for something the product does not do yet",
"cancellation": "Cancelling, pausing or closing an account, deleting data",
"other": "Press, partners, jobs, sales pitches, wrong address, thanks, general questions"
}
},
"impact": {
"type": "score",
"instructions": "Rate the business impact using only the reported facts.",
"criteria": [
"No function impaired",
"One user impaired, with a workaround",
"Many users blocked from a core function",
"Data loss or legal exposure"
]
}
}
};
const response = await fetch("http://127.0.0.1:8000/v1/systemone", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
type Answer = { type: string; noul?: number; probabilities?: Record<string, number> };
const { answers } = (await response.json()) as { answers: Record<string, Answer> };
for (const [name, answer] of Object.entries(answers)) {
if (answer.type === "noul") {
console.log(name, Number(answer.noul!.toFixed(3)));
} else {
const [top, p] = Object.entries(answer.probabilities!).sort((a, b) => b[1] - a[1])[0];
console.log(name, top, Number(p.toFixed(3)));
}
}
// urgent 0.159
// queue billing 0.962
// impact 1 0.648

Run against the CPU stand-in at v0.11.1 on 2026-10-14: the answers are the stand-in's, not the measured system's.

The server has no authentication: the Authorization header and any API key are accepted and ignored. Call it from code you run, a backend or a script, with the server on 127.0.0.1 or behind a reverse proxy that authenticates.

  • Python: the same call from Python.
  • Frameworks: the JavaScript integrations that were checked.
  • The HTTP API: every route, request field and response field.