# JavaScript and TypeScript

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

Source: https://opendecisio.com/docs/use-from/javascript/

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: One POST out, one JSON answer back; the rest is reading it. A JavaScript script posts the recorded support ticket to the server and prints the likeliest option of each question: urgent 0.16; queue billing 0.96; impact 1 0.65.]

 

## TL;DR

 

With a server running ([Get started](https://opendecisio.com/docs/) 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.

  

| Word | Meaning |
| --- | --- |
| `noul` | a yes/no question; its answer is the probability of yes |
| `probabilities` | a probability for each option of a choice, or each level of a score; they add up to 1 |
| top-level `await` | `await` outside a function, which a `.mjs` file allows |

 

## 1. With fetch

 

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.

 

Common mistake

`fetch` has no timeout of its own: Node’s applies its HTTP client’s defaults, 300 seconds to the response headers. Pass `signal: AbortSignal.timeout(ms)` if you want an earlier stop, and allow for the first request after a start, which warms the engine.

 

## 2. In TypeScript

 

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&#x3C;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&#x3C;string, number> };
const { answers } = (await response.json()) as { answers: Record&#x3C;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.

 

## 3. Where to run it

 

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.

 

## What next

 

- [Python](https://opendecisio.com/docs/use-from/python/): the same call from Python.
- [Frameworks](https://opendecisio.com/docs/use-from/frameworks/): the JavaScript integrations that were checked.
- [The HTTP API](https://opendecisio.com/docs/api/): every route, request field and response field.
