Write the commentary from your own close pack
Send the measured facts — every flagged line with both variances in amount and
percent, the favourability computed from the caption's polarity, the rule that flagged it,
its share of the gross movement, the activity-note lines the engine matched to it, the
subtotals that do not foot and the lines that are decisive on their own — and get
back one JSON object: drivers (exactly one entry per id in
facts.flagged_ids, each sourced either to the activity notes or explicitly
unclear), period_summary, watch_items,
questions and unverified. The binding constraint is the
interesting one: a line the notes never mention may not be explained,
only returned as unclear for the controller — and that is not a request, it is
checked. All of it is mechanically checkable, and the checker ships with the app:
/fluxkit.js is plain ES5 with no dependencies and no network calls, so your
pipeline can compute the same facts and run the same reconciliation — the partition
counted rather than sampled, every invented driver named, and every currency and
percentage figure traced back to something measured at the precision it was written to
— before a sentence ever reaches a close pack. Every code step below is shown in
cURL, Python, JavaScript, Go, Java, Ruby, PHP and C#; pick a language once and the whole
page follows.
Basics
Base URL https://api.skillsafe.ai/v1/app-api, app slug
flux-desk. Every request sends
Authorization: Bearer <token> and JSON bodies with
Content-Type: application/json. Responses are wrapped in an envelope:
{"data": …} on success, {"error": {"code", "message"}}
on failure. Drivers are written by the gpt-terra model alias
(currently gpt-5.6-terra) at a publisher markup of 1000 bps
— 10%. Credits are units of 1/10 000 of a US dollar, so 10 000 credits is
$1.00. /estimate, /me and /guest are free;
/run and /run-stream are metered. Run input caps at 1 MB
of JSON.
Error codes
| code | status | what it means |
|---|---|---|
unauthorized | 401 | Missing or stale token. Mint a guest token or sign in again. |
forbidden | 403 | The token belongs to a different app. |
payment_required | 402 | Balance below min_credits. Call /estimate first and compare against /me. |
validation_error | 400 | Malformed body. error.details names the field. A where value that is not an operator object lands here. |
rate_limited | 429 | Back off. /similar is 30 req/min per IP, tighter than the other data endpoints. |
not_found | 404 | Unknown job or record id. |
internal | 5xx | Retry with the SAME Idempotency-Key — it returns the original job instead of billing again. |
The whole deterministic half needs no API at all. /fluxkit.js parses the pack,
computes every variance, applies materiality, foots the subtotals and writes the commentary
document, both CSVs and the measurement JSON in a browser tab or in Node with no account and
no network. Only the driver sentences are metered.
Step 1 · Get a token
Two ways in. A personal token bills your own wallet and can run;
a guest token is free to mint, can call /me and the free
/estimate, and is enough to assert the model binding in CI. Never paste a
token into source control — read it from your shell or your secret store, and use
the token page rather than the DevTools console to find the one
this browser already holds.
# Option A - take the token this browser already holds: open /tokens.html,
# press "Copy shell export", and paste the line it prints.
export SKILLSAFE_TOKEN="aut_xxxxxxxxxxxxxxxxxxxx"
# Option B - mint a guest token with no browser at all. A guest can call /me and
# the free /estimate, which is enough to verify the model binding; writing the
# drivers needs a personal token so it bills your own wallet.
curl -s -X POST https://api.skillsafe.ai/v1/app-api/guest \
-H 'Content-Type: application/json' \
-d '{"slug":"flux-desk"}'
# => {"data":{"token":"aut_...","subject_type":"guest","credits":0}}
import json, urllib.request
BASE = "https://api.skillsafe.ai/v1/app-api"
SLUG = "flux-desk"
def call(path, body=None, token=None, method=None):
data = None if body is None else json.dumps(body).encode()
req = urllib.request.Request(BASE + path, data=data,
method=method or ("POST" if data else "GET"))
req.add_header("Content-Type", "application/json")
if token:
req.add_header("Authorization", "Bearer " + token)
with urllib.request.urlopen(req) as r:
payload = json.load(r)
if "error" in payload:
raise RuntimeError(payload["error"]["code"] + ": " + payload["error"]["message"])
return payload["data"]
# Paste a personal token from /tokens.html, or read it from your secret store.
# Falling back to a guest token keeps the free calls working with no account.
MY_TOKEN = "YOUR_TOKEN"
TOKEN = MY_TOKEN if MY_TOKEN != "YOUR_TOKEN" else call("/guest", {"slug": SLUG})["token"]
const BASE = "https://api.skillsafe.ai/v1/app-api";
const SLUG = "flux-desk";
async function call(path, body, opts = {}) {
const res = await fetch(BASE + path, {
method: opts.method || (body ? "POST" : "GET"),
headers: {
"Content-Type": "application/json",
...(opts.token ? { Authorization: "Bearer " + opts.token } : {}),
...(opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : {})
},
body: body ? JSON.stringify(body) : undefined
});
const payload = await res.json();
if (payload.error) throw new Error(payload.error.code + ": " + payload.error.message);
return payload.data;
}
// A personal token pasted from /tokens.html, or a guest token for the free calls.
const TOKEN = MY_TOKEN || (await call("/guest", { slug: SLUG })).token;
package main
import (
"bytes"
"encoding/json"
"errors"
"net/http"
)
const base = "https://api.skillsafe.ai/v1/app-api"
const slug = "flux-desk"
type envelope struct {
Data json.RawMessage `json:"data"`
Error *struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
func call(path string, body any, token, idem string) (json.RawMessage, error) {
var buf bytes.Buffer
method := "GET"
if body != nil {
method = "POST"
if err := json.NewEncoder(&buf).Encode(body); err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, base+path, &buf)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
if idem != "" {
req.Header.Set("Idempotency-Key", idem)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var env envelope
if err := json.NewDecoder(res.Body).Decode(&env); err != nil {
return nil, err
}
if env.Error != nil {
return nil, errors.New(env.Error.Code + ": " + env.Error.Message)
}
return env.Data, nil
}
import java.net.URI;
import java.net.http.*;
import com.fasterxml.jackson.databind.*;
class Flux {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static final String SLUG = "flux-desk";
static final HttpClient HTTP = HttpClient.newHttpClient();
static final ObjectMapper M = new ObjectMapper();
static JsonNode call(String path, Object body, String token, String idem) throws Exception {
HttpRequest.Builder b = HttpRequest.newBuilder(URI.create(BASE + path))
.header("Content-Type", "application/json");
if (token != null) b.header("Authorization", "Bearer " + token);
if (idem != null) b.header("Idempotency-Key", idem);
b = body == null
? b.GET()
: b.POST(HttpRequest.BodyPublishers.ofString(M.writeValueAsString(body)));
JsonNode env = M.readTree(HTTP.send(b.build(),
HttpResponse.BodyHandlers.ofString()).body());
if (env.has("error")) {
throw new RuntimeException(env.at("/error/code").asText()
+ ": " + env.at("/error/message").asText());
}
return env.get("data");
}
}
require "json"
require "net/http"
BASE = "https://api.skillsafe.ai/v1/app-api"
SLUG = "flux-desk"
def call(path, body = nil, token: nil, idem: nil, method: nil)
uri = URI(BASE + path)
klass = (method || (body ? "POST" : "GET")) == "POST" ? Net::HTTP::Post : Net::HTTP::Get
req = klass.new(uri)
req["Content-Type"] = "application/json"
req["Authorization"] = "Bearer #{token}" if token
req["Idempotency-Key"] = idem if idem
req.body = JSON.dump(body) if body
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
payload = JSON.parse(res.body)
raise "#{payload['error']['code']}: #{payload['error']['message']}" if payload["error"]
payload["data"]
end
MY_TOKEN = "YOUR_TOKEN"
TOKEN = MY_TOKEN == "YOUR_TOKEN" ? call("/guest", { "slug" => SLUG })["token"] : MY_TOKEN
<?php
const BASE = "https://api.skillsafe.ai/v1/app-api";
const SLUG = "flux-desk";
function call(string $path, $body = null, ?string $token = null, ?string $idem = null) {
$headers = ["Content-Type: application/json"];
if ($token) { $headers[] = "Authorization: Bearer " . $token; }
if ($idem) { $headers[] = "Idempotency-Key: " . $idem; }
$ch = curl_init(BASE . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_CUSTOMREQUEST => $body === null ? "GET" : "POST",
CURLOPT_POSTFIELDS => $body === null ? null : json_encode($body),
]);
$payload = json_decode(curl_exec($ch), true);
curl_close($ch);
if (isset($payload["error"])) {
throw new RuntimeException($payload["error"]["code"] . ": " . $payload["error"]["message"]);
}
return $payload["data"];
}
$myToken = "YOUR_TOKEN";
$token = $myToken === "YOUR_TOKEN" ? call("/guest", ["slug" => SLUG])["token"] : $myToken;
using System.Net.Http.Json;
using System.Text.Json;
static class Flux {
const string Base = "https://api.skillsafe.ai/v1/app-api";
const string Slug = "flux-desk";
static readonly HttpClient Http = new();
public static async Task<JsonElement> CallAsync(
string path, object? body = null, string? token = null, string? idem = null) {
var req = new HttpRequestMessage(
body is null ? HttpMethod.Get : HttpMethod.Post, Base + path);
if (body is not null) req.Content = JsonContent.Create(body);
if (token is not null) req.Headers.Add("Authorization", "Bearer " + token);
if (idem is not null) req.Headers.Add("Idempotency-Key", idem);
var env = await (await Http.SendAsync(req)).Content.ReadFromJsonAsync<JsonElement>();
if (env.TryGetProperty("error", out var err)) {
throw new Exception(err.GetProperty("code").GetString()
+ ": " + err.GetProperty("message").GetString());
}
return env.GetProperty("data");
}
}
Step 2 · Check the session and the balance
/me tells you whether the token is personal or guest and what the balance is.
Compare it against min_credits from the estimate before you submit:
a 402 after submitting is a failure of the caller, not of the wallet.
curl -s https://api.skillsafe.ai/v1/app-api/me \
-H "Authorization: Bearer $SKILLSAFE_TOKEN"
# => {"data":{"subject_type":"user","credits":48210,"app_slug":"flux-desk"}}
# credits are 1/10 000 of a US dollar, so 48210 is $4.8210.
me = call("/me", token=TOKEN)
print(me["subject_type"], me["credits"], "credits =",
"${:.4f}".format(me["credits"] / 10000))
const me = await call("/me", null, { token: TOKEN });
console.log(me.subject_type, me.credits, "credits = $" + (me.credits / 10000).toFixed(4));
data, err := call("/me", nil, token, "")
if err != nil {
panic(err)
}
var me struct {
SubjectType string `json:"subject_type"`
Credits int64 `json:"credits"`
}
json.Unmarshal(data, &me)
fmt.Println(me.SubjectType, me.Credits)
JsonNode me = Flux.call("/me", null, token, null);
System.out.println(me.get("subject_type").asText() + " " + me.get("credits").asLong());
me = call("/me", token: TOKEN)
puts "#{me['subject_type']} #{me['credits']} credits"
$me = call("/me", null, $token);
printf("%s %d credits\n", $me["subject_type"], $me["credits"]);
var me = await Flux.CallAsync("/me", token: token);
Console.WriteLine($"{me.GetProperty("subject_type").GetString()} " +
$"{me.GetProperty("credits").GetInt64()} credits");
Step 3 · Estimate, and assert the model binding
/estimate costs nothing, creates no job, and is the authoritative proof that
the app is wired to the model you think it is. hold_credits is a
reservation priced at the full output cap — present it to a user as
reserved, never as the price, because charged_credits after settlement is
usually far lower. If the balance sits between min_credits and
hold_credits the run still executes with a reduced output cap and comes back
"truncated": true; treat the drivers that arrived as complete and report the
rest as missing rather than presenting a short list as the whole commentary.
curl -s -X POST https://api.skillsafe.ai/v1/app-api/estimate \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-d @input.json
# => {"data":{"model":"gpt-5.6-terra","model_alias":"gpt-terra","markup_bps":1000,
# "hold_credits":2910,"min_credits":180,"sponsor_enabled":false}}
#
# hold_credits is a RESERVATION priced at the full output cap, not the price.
# charged_credits after the run is usually far lower. Assert the binding here:
# model must read gpt-5.6-terra, model_alias gpt-terra, markup_bps 1000.
est = call("/estimate", payload, token=TOKEN)
assert est["model"] == "gpt-5.6-terra", est["model"]
assert est["model_alias"] == "gpt-terra"
assert est["markup_bps"] == 1000
if me["credits"] < est["min_credits"]:
raise SystemExit("balance below the model minimum - top up before running")
const est = await call("/estimate", payload, { token: TOKEN });
if (est.model !== "gpt-5.6-terra" || est.model_alias !== "gpt-terra" || est.markup_bps !== 1000) {
throw new Error("unexpected model binding: " + JSON.stringify(est));
}
if (me.credits < est.min_credits) throw new Error("balance below the model minimum");
data, err = call("/estimate", payload, token, "")
if err != nil {
panic(err)
}
var est struct {
Model string `json:"model"`
ModelAlias string `json:"model_alias"`
MarkupBps int `json:"markup_bps"`
Hold int64 `json:"hold_credits"`
Min int64 `json:"min_credits"`
}
json.Unmarshal(data, &est)
if est.Model != "gpt-5.6-terra" || est.MarkupBps != 1000 {
panic("unexpected model binding")
}
JsonNode est = Flux.call("/estimate", payload, token, null);
if (!"gpt-5.6-terra".equals(est.get("model").asText())
|| est.get("markup_bps").asInt() != 1000) {
throw new IllegalStateException("unexpected model binding: " + est);
}
est = call("/estimate", payload, token: TOKEN)
raise "unexpected model binding" unless est["model"] == "gpt-5.6-terra" &&
est["model_alias"] == "gpt-terra" && est["markup_bps"] == 1000
abort "balance below the model minimum" if me["credits"] < est["min_credits"]
$est = call("/estimate", $payload, $token);
if ($est["model"] !== "gpt-5.6-terra" || $est["markup_bps"] !== 1000) {
throw new RuntimeException("unexpected model binding");
}
if ($me["credits"] < $est["min_credits"]) {
throw new RuntimeException("balance below the model minimum");
}
var est = await Flux.CallAsync("/estimate", payload, token);
if (est.GetProperty("model").GetString() != "gpt-5.6-terra"
|| est.GetProperty("markup_bps").GetInt32() != 1000) {
throw new InvalidOperationException("unexpected model binding");
}
The input — what facts carries
Build this with FluxKit.factsForModel(FluxKit.analyze(ctx)) or assemble it
yourself; the field names below are the contract, taken from
fluxkit.js rather than from intent. Three of them bind the reply:
flagged_ids is the exact set that must get a driver,
unsourced_ids is the subset that may not be explained, and
always_comment_ids is the firm's standing instruction. The excerpts are cut
on whole rows with the header kept, and the cut is announced in-band — every figure
in facts was measured over the complete pack either way.
Two fields sit outside facts and are easy to miss.
current_datetime is an ISO-8601 stamp of when the request was made — it
is the clock, not evidence: the prompt forbids dating the commentary from it or treating
it as a transaction date, and the period being commented on always comes from
facts and the period labels. retry_note is sent only on the
reformat retry, carries the reason the previous reply was rejected, and must go out under
the same Idempotency-Key base as the first attempt.
{
"facts": {
"period": "May 2026",
"prior_label": "April 2026",
"budget_label": "May budget",
"entity": "Northwind Cloud, consolidated",
"currency": "$",
"materiality": "the greater of 5% of the comparative and $50,000",
"totals": {
"lines_read": 14,
"flagged": 11,
"below_threshold": 3,
"gross_movement_vs_prior": 1120000,
"net_movement_vs_prior": -380000,
"net_movement_vs_budget": -304000,
"balance_sheet_movement_vs_prior": -1450000,
"subtotals_not_footing": 0
},
"flagged_ids": ["L1","L2","L3","L4","L6","L7","L9","L10","L12","L13","L14"],
"unsourced_ids": [],
"always_comment_ids": ["L1","L2","L3","L6","L7","L9","L14"],
"breaker_ids": [],
"flagged_lines": [
{
"id": "L4",
"line": "Hosting and cloud",
"statement": "P&L",
"polarity": "expense",
"subtotal": false,
"current": 1880000, "prior": 1690000, "budget": 1720000,
"delta_vs_prior": 190000, "pct_vs_prior": "11.2%",
"delta_vs_budget": 160000, "pct_vs_budget": "9.3%",
"favourability_vs_prior": "unfavourable",
"favourability_vs_budget": "unfavourable",
"why_flagged": "vs prior + vs budget",
"share_of_gross_movement": "17%",
"activity_notes": [
"Hosting and cloud: 60 additional GPU reservations were taken on one-year committed-use terms for the May model-serving launch."
],
"sourced": true
}
],
"integrity": [],
"load_bearing": [],
"data_notes": []
},
"table_excerpt": "Line,Current,Prior,Budget\nSubscription revenue,8420000,...",
"notes_excerpt": "Subscription revenue: 41 net new logos closed in the month, ...",
"current_datetime": "2026-05-31T18:00:00.000Z"
}
Step 4 · Run and poll
Submit with an Idempotency-Key derived from a hash of the input plus an
attempt counter, and reuse the same key on any retry: a network blip or a malformed first
reply must never bill twice. The reply arrives as a JSON string in
output.output.
# The Idempotency-Key is a hash of the input plus an attempt counter. Retrying
# with the SAME key returns the original job instead of billing a second run -
# which is exactly what the app's reformat-retry lane relies on.
curl -s -X POST https://api.skillsafe.ai/v1/app-api/run \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: flux-desk:9bdddab9:696:a1' \
-d @input.json
# => {"data":{"job_id":"job_...","status":"queued"}}
curl -s https://api.skillsafe.ai/v1/app-api/jobs/job_xxx \
-H "Authorization: Bearer $SKILLSAFE_TOKEN"
# => {"data":{"status":"succeeded","output":{"output":"{\"title\":...}"},
# "charged_credits":624,"truncated":false}}
import time
job = call("/run", payload, token=TOKEN)
while True:
j = call("/jobs/" + job["job_id"], token=TOKEN)
if j["status"] in ("succeeded", "failed", "canceled"):
break
time.sleep(1.5)
if j["status"] != "succeeded":
raise RuntimeError(j.get("error") or j["status"])
commentary = json.loads(j["output"]["output"])
if j.get("truncated"):
print("reply cut short by the balance - the drivers that arrived are complete, "
"the rest are missing and should be reported as missing")
const job = await call("/run", payload, { token: TOKEN, idempotencyKey: key });
let j;
do {
await new Promise((r) => setTimeout(r, 1500));
j = await call("/jobs/" + job.job_id, null, { token: TOKEN });
} while (!["succeeded", "failed", "canceled"].includes(j.status));
if (j.status !== "succeeded") throw new Error(j.error || j.status);
const commentary = JSON.parse(j.output.output);
if (j.truncated) console.warn("reply cut short by the balance");
data, err = call("/run", payload, token, idemKey)
if err != nil {
panic(err)
}
var job struct {
JobID string `json:"job_id"`
}
json.Unmarshal(data, &job)
for {
time.Sleep(1500 * time.Millisecond)
data, err = call("/jobs/"+job.JobID, nil, token, "")
if err != nil {
panic(err)
}
var j struct {
Status string `json:"status"`
Output struct {
Output string `json:"output"`
} `json:"output"`
}
json.Unmarshal(data, &j)
if j.Status == "succeeded" {
fmt.Println(j.Output.Output)
break
}
if j.Status == "failed" || j.Status == "canceled" {
panic(j.Status)
}
}
JsonNode job = Flux.call("/run", payload, token, idemKey);
JsonNode j;
do {
Thread.sleep(1500);
j = Flux.call("/jobs/" + job.get("job_id").asText(), null, token, null);
} while (!java.util.List.of("succeeded", "failed", "canceled")
.contains(j.get("status").asText()));
if (!"succeeded".equals(j.get("status").asText())) throw new RuntimeException(j.toString());
JsonNode commentary = M.readTree(j.at("/output/output").asText());
job = call("/run", payload, token: TOKEN, idem: idem_key)
loop do
sleep 1.5
j = call("/jobs/#{job['job_id']}", token: TOKEN)
next unless %w[succeeded failed canceled].include?(j["status"])
raise j["status"] unless j["status"] == "succeeded"
commentary = JSON.parse(j["output"]["output"])
warn "reply cut short by the balance" if j["truncated"]
break
end
$job = call("/run", $payload, $token, $idemKey);
do {
sleep(2);
$j = call("/jobs/" . $job["job_id"], null, $token);
} while (!in_array($j["status"], ["succeeded", "failed", "canceled"], true));
if ($j["status"] !== "succeeded") { throw new RuntimeException($j["status"]); }
$commentary = json_decode($j["output"]["output"], true);
var job = await Flux.CallAsync("/run", payload, token, idemKey);
JsonElement j;
do {
await Task.Delay(1500);
j = await Flux.CallAsync("/jobs/" + job.GetProperty("job_id").GetString(), token: token);
} while (j.GetProperty("status").GetString() is not ("succeeded" or "failed" or "canceled"));
if (j.GetProperty("status").GetString() != "succeeded") throw new Exception(j.ToString());
var commentary = JsonDocument.Parse(
j.GetProperty("output").GetProperty("output").GetString()!);
The output contract
One JSON object, no prose and no code fence. drivers is an exact partition of
facts.flagged_ids. source is either
"activity-notes" or "unclear", and every id in
facts.unsourced_ids must take the second with the driver text
"driver unclear - flag for controller" verbatim. A driver sourced to the
activity notes for a line the notes never mention is reported by
FluxKit.reconcile as an invention, by name.
{
"title": "Flux commentary - May 2026",
"period_summary": "Three to five sentences. Names the largest measured movement, states the
period's direction, says if the movement hangs on one line, and states any
integrity break plainly and first.",
"drivers": [
{
"line_id": "L4",
"driver": "60 additional GPU reservations were taken on one-year committed-use terms for the May model-serving launch.",
"source": "activity-notes"
},
{
"line_id": "L7",
"driver": "driver unclear - flag for controller",
"source": "unclear"
}
],
"watch_items": ["A line just under the threshold worth watching next period."],
"questions": ["A question for the controller."],
"unverified": ["Something this pass could not confirm from what it was given."]
}
Step 5 · Stream it instead
/run-stream is the same billed run with server-sent events. The frame name
arrives on the event: line — job, delta,
done — so parse that rather than assuming every frame carries text.
A stream that dies mid-reply usually still carries the earlier fields; close the JSON and
keep what arrived, then report every flagged line with no driver as missing rather than
assuming it was fine.
# Server-sent events. The frame name arrives on the "event:" line, so parse that
# rather than assuming every frame is a delta.
curl -N -s -X POST https://api.skillsafe.ai/v1/app-api/run-stream \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream' \
-H 'Idempotency-Key: flux-desk:9bdddab9:696:a1' \
-d @input.json
# event: job
# data: {"job_id":"job_..."}
# event: delta
# data: {"text":"{\"title\":\"Flux commentary"}
# event: done
# data: {"status":"succeeded","charged_credits":624,"truncated":false}
req = urllib.request.Request(BASE + "/run-stream", data=json.dumps(payload).encode())
req.add_header("Content-Type", "application/json")
req.add_header("Accept", "text/event-stream")
req.add_header("Authorization", "Bearer " + TOKEN)
req.add_header("Idempotency-Key", idem_key)
raw, event = "", None
with urllib.request.urlopen(req) as stream:
for line in stream:
line = line.decode().rstrip("\n")
if line.startswith("event:"):
event = line[6:].strip()
elif line.startswith("data:"):
frame = json.loads(line[5:].strip())
if event == "delta":
raw += frame["text"]
elif event == "done":
print("charged", frame.get("charged_credits"))
commentary = json.loads(raw)
const res = await fetch(BASE + "/run-stream", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "text/event-stream",
Authorization: "Bearer " + TOKEN,
"Idempotency-Key": idemKey
},
body: JSON.stringify(payload)
});
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = "", raw = "", event = null;
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
let nl;
while ((nl = buf.indexOf("\n")) !== -1) {
const line = buf.slice(0, nl);
buf = buf.slice(nl + 1);
if (line.startsWith("event:")) event = line.slice(6).trim();
else if (line.startsWith("data:")) {
const frame = JSON.parse(line.slice(5).trim());
if (event === "delta") raw += frame.text;
}
}
}
const commentary = JSON.parse(raw);
req, _ := http.NewRequest("POST", base+"/run-stream", bytes.NewReader(bodyBytes))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "text/event-stream")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Idempotency-Key", idemKey)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
sc := bufio.NewScanner(res.Body)
sc.Buffer(make([]byte, 0, 64*1024), 4*1024*1024)
var raw strings.Builder
event := ""
for sc.Scan() {
line := sc.Text()
switch {
case strings.HasPrefix(line, "event:"):
event = strings.TrimSpace(line[6:])
case strings.HasPrefix(line, "data:") && event == "delta":
var f struct {
Text string `json:"text"`
}
json.Unmarshal([]byte(strings.TrimSpace(line[5:])), &f)
raw.WriteString(f.Text)
}
}
HttpRequest req = HttpRequest.newBuilder(URI.create(Flux.BASE + "/run-stream"))
.header("Content-Type", "application/json")
.header("Accept", "text/event-stream")
.header("Authorization", "Bearer " + token)
.header("Idempotency-Key", idemKey)
.POST(HttpRequest.BodyPublishers.ofString(M.writeValueAsString(payload)))
.build();
StringBuilder raw = new StringBuilder();
String[] event = { "" };
Flux.HTTP.send(req, HttpResponse.BodyHandlers.ofLines()).body().forEach(line -> {
if (line.startsWith("event:")) {
event[0] = line.substring(6).trim();
} else if (line.startsWith("data:") && event[0].equals("delta")) {
try {
raw.append(M.readTree(line.substring(5).trim()).get("text").asText());
} catch (Exception e) { throw new RuntimeException(e); }
}
});
uri = URI(BASE + "/run-stream")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req["Accept"] = "text/event-stream"
req["Authorization"] = "Bearer #{TOKEN}"
req["Idempotency-Key"] = idem_key
req.body = JSON.dump(payload)
raw = +""
event = nil
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req) do |res|
res.read_body do |chunk|
chunk.each_line do |line|
line = line.chomp
if line.start_with?("event:")
event = line[6..].strip
elsif line.start_with?("data:") && event == "delta"
raw << JSON.parse(line[5..].strip)["text"]
end
end
end
end
end
commentary = JSON.parse(raw)
$raw = "";
$event = null;
$ch = curl_init(BASE . "/run-stream");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Accept: text/event-stream",
"Authorization: Bearer " . $token,
"Idempotency-Key: " . $idemKey,
],
CURLOPT_WRITEFUNCTION => function ($ch, $chunk) use (&$raw, &$event) {
foreach (explode("\n", $chunk) as $line) {
$line = rtrim($line);
if (str_starts_with($line, "event:")) {
$event = trim(substr($line, 6));
} elseif (str_starts_with($line, "data:") && $event === "delta") {
$raw .= json_decode(trim(substr($line, 5)), true)["text"];
}
}
return strlen($chunk);
},
]);
curl_exec($ch);
curl_close($ch);
$commentary = json_decode($raw, true);
var req = new HttpRequestMessage(HttpMethod.Post, Base + "/run-stream") {
Content = JsonContent.Create(payload)
};
req.Headers.Add("Accept", "text/event-stream");
req.Headers.Add("Authorization", "Bearer " + token);
req.Headers.Add("Idempotency-Key", idemKey);
using var res = await Http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
using var reader = new StreamReader(await res.Content.ReadAsStreamAsync());
var raw = new StringBuilder();
string? evt = null, line;
while ((line = await reader.ReadLineAsync()) is not null) {
if (line.StartsWith("event:")) evt = line[6..].Trim();
else if (line.StartsWith("data:") && evt == "delta") {
raw.Append(JsonDocument.Parse(line[5..].Trim())
.RootElement.GetProperty("text").GetString());
}
}
var commentary = JsonDocument.Parse(raw.ToString());
Step 6 · Store and search past commentaries
The commentaries collection is the app's system of record, with
acl_read: "owner" and acl_write: "user". Records are created at
/collections/{name}/records — note the trailing segment.
where takes operator objects only, and order_by is silently
ignored in favour of a sort object (the default is
created_at desc). Documents cap at 64 KB, which is why the app trims the
pasted pack before the rendered document. The declared embed fields are
title, period, entity and summary;
they were chosen before the app had users because the platform never backfills vectors.
# Create. Note the path: records are created at /collections/{name}/records.
curl -s -X POST https://api.skillsafe.ai/v1/app-api/collections/commentaries/records \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"title":"Flux commentary - May 2026","period":"May 2026",
"entity":"Northwind Cloud, consolidated","summary":"Cloud infrastructure...",
"state":"fully-sourced","flagged":11,"unsourced":0,"foot_breaks":0,
"ran_at":"2026-05-31T18:00:00Z","doc_md":"# Flux commentary ..."}'
# Query. `where` takes OPERATOR OBJECTS, and order_by is ignored in favour of
# `sort`; the default is created_at desc.
curl -s -X POST https://api.skillsafe.ai/v1/app-api/collections/commentaries/query \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"where":{"state":{"eq":"partly-sourced"}},
"sort":{"field":"ran_at","dir":"desc"},"limit":12}'
# Semantic search over the declared embed fields: title, period, entity, summary.
# 30 req/min per IP and about ten times the cost of a where filter, so use it
# only when an exact match will not do.
curl -s -X POST https://api.skillsafe.ai/v1/app-api/collections/commentaries/similar \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"text":"the month the cloud bill doubled","limit":8}'
call("/collections/commentaries/records", {
"title": commentary["title"],
"period": facts["period"],
"entity": facts["entity"],
"summary": commentary["period_summary"][:500],
"state": state, # fully-sourced / partly-sourced / unsourced
"flagged": facts["totals"]["flagged"],
"unsourced": len(facts["unsourced_ids"]),
"foot_breaks": facts["totals"]["subtotals_not_footing"],
"ran_at": datetime.now(timezone.utc).isoformat(),
"doc_md": document_markdown[:32000],
}, token=TOKEN)
recent = call("/collections/commentaries/query", {
"where": {"state": {"eq": "partly-sourced"}},
"sort": {"field": "ran_at", "dir": "desc"},
"limit": 12,
}, token=TOKEN)["records"]
await call("/collections/commentaries/records", {
title: commentary.title,
period: facts.period,
entity: facts.entity,
summary: commentary.period_summary.slice(0, 500),
state,
flagged: facts.totals.flagged,
unsourced: facts.unsourced_ids.length,
foot_breaks: facts.totals.subtotals_not_footing,
ran_at: new Date().toISOString(),
doc_md: documentMarkdown.slice(0, 32000)
}, { token: TOKEN });
// similar() resolves to the record ARRAY; query() resolves to { records }.
// Accept either shape rather than trusting one - reading .records off an array
// is how a semantic search silently returns nothing forever.
const recordsOf = (r) => (Array.isArray(r) ? r : (r && r.records) || []);
const hits = recordsOf(await call("/collections/commentaries/similar",
{ text: "the month the cloud bill doubled", limit: 8 }, { token: TOKEN }));
record := map[string]any{
"title": commentary.Title,
"period": facts.Period,
"entity": facts.Entity,
"summary": commentary.PeriodSummary,
"state": state,
"flagged": facts.Totals.Flagged,
"unsourced": len(facts.UnsourcedIDs),
"foot_breaks": facts.Totals.SubtotalsNotFooting,
"ran_at": time.Now().UTC().Format(time.RFC3339),
"doc_md": documentMarkdown,
}
if _, err := call("/collections/commentaries/records", record, token, ""); err != nil {
panic(err)
}
query := map[string]any{
"where": map[string]any{"state": map[string]any{"eq": "partly-sourced"}},
"sort": map[string]any{"field": "ran_at", "dir": "desc"},
"limit": 12,
}
data, err = call("/collections/commentaries/query", query, token, "")
Map<String, Object> record = Map.of(
"title", commentary.get("title").asText(),
"period", facts.get("period").asText(),
"entity", facts.get("entity").asText(),
"summary", commentary.get("period_summary").asText(),
"state", state,
"flagged", facts.at("/totals/flagged").asInt(),
"unsourced", facts.get("unsourced_ids").size(),
"foot_breaks", facts.at("/totals/subtotals_not_footing").asInt(),
"ran_at", java.time.Instant.now().toString(),
"doc_md", documentMarkdown);
Flux.call("/collections/commentaries/records", record, token, null);
JsonNode recent = Flux.call("/collections/commentaries/query", Map.of(
"where", Map.of("state", Map.of("eq", "partly-sourced")),
"sort", Map.of("field", "ran_at", "dir", "desc"),
"limit", 12), token, null);
call("/collections/commentaries/records", {
"title" => commentary["title"],
"period" => facts["period"],
"entity" => facts["entity"],
"summary" => commentary["period_summary"][0, 500],
"state" => state,
"flagged" => facts["totals"]["flagged"],
"unsourced" => facts["unsourced_ids"].length,
"foot_breaks" => facts["totals"]["subtotals_not_footing"],
"ran_at" => Time.now.utc.iso8601,
"doc_md" => document_markdown[0, 32000]
}, token: TOKEN)
recent = call("/collections/commentaries/query", {
"where" => { "state" => { "eq" => "partly-sourced" } },
"sort" => { "field" => "ran_at", "dir" => "desc" },
"limit" => 12
}, token: TOKEN)["records"]
call("/collections/commentaries/records", [
"title" => $commentary["title"],
"period" => $facts["period"],
"entity" => $facts["entity"],
"summary" => substr($commentary["period_summary"], 0, 500),
"state" => $state,
"flagged" => $facts["totals"]["flagged"],
"unsourced" => count($facts["unsourced_ids"]),
"foot_breaks" => $facts["totals"]["subtotals_not_footing"],
"ran_at" => gmdate("c"),
"doc_md" => substr($documentMarkdown, 0, 32000),
], $token);
$recent = call("/collections/commentaries/query", [
"where" => ["state" => ["eq" => "partly-sourced"]],
"sort" => ["field" => "ran_at", "dir" => "desc"],
"limit" => 12,
], $token)["records"];
await Flux.CallAsync("/collections/commentaries/records", new {
title = title,
period = period,
entity = entity,
summary = periodSummary,
state = state,
flagged = flaggedCount,
unsourced = unsourcedCount,
foot_breaks = footBreaks,
ran_at = DateTime.UtcNow.ToString("o"),
doc_md = documentMarkdown
}, token);
var recent = await Flux.CallAsync("/collections/commentaries/query", new {
where = new { state = new { eq = "partly-sourced" } },
sort = new { field = "ran_at", dir = "desc" },
limit = 12
}, token);
Check the reply yourself
This is the part worth copying. The app does not ask you to trust the model, and neither
should your pipeline: the same engine that produced facts re-measures the
reply in public. Vendor /fluxkit.js — plain ES5, no dependencies, no
network — and run the identical reconciliation on your own machine.
// fluxkit.js is plain ES5 with no dependencies and no network calls, so the same
// reconciliation the app runs in the tab runs in your pipeline.
const an = FluxKit.analyze({
period_label: "May 2026", prior_label: "April 2026", budget_label: "May budget",
currency: "$", pct_threshold: 5, abs_floor: 50000,
always: "revenue, headcount cost, cash",
table: packCsv, notes: activityNotes
});
const facts = FluxKit.factsForModel(an); // exactly what you send
const model = FluxKit.normalizeModel(commentary); // exactly what came back
const findings = FluxKit.reconcile(model, an);
const summary = FluxKit.summarize(findings); // { pass, warn, fail, info, fails }
if (summary.fails) {
// Each failure is separate and named: missing ids, duplicated ids, ids the
// engine measured as below threshold, ids that are not lines at all, a driver
// sourced to notes that never mention the line, an always-comment line with no
// driver, and any figure that matches nothing measured.
findings.filter((f) => f.level === "fail").forEach((f) => console.error(f.id, f.text));
process.exitCode = 1;
}
// The rendered document, and the guard that reads it back before it leaves.
const md = FluxKit.renderDoc(an, model);
const guard = FluxKit.exportGuard(md, an);
if (!guard.ok) throw new Error("the rendered commentary no longer matches the measurement");
What the checker actually asserts: exactly one driver per flagged line, with missing, duplicated, off-contract and not-even-a-line ids reported separately rather than as one count mismatch; every id the engine measured as unsourced returned as unclear rather than explained; no driver claiming the activity notes for a line they never mention; no always-comment line left without a driver; any driver that is only the caption, a direction and a figure flagged as a restatement; any narrative that calls a balance-sheet line the largest movement of the period, since a balance is a stock and a variance is a flow; and every currency and percentage figure traced back to something measured, at the precision it was written to, so a permitted rounding of $1,153,000 to $1.2M passes while $1,153,204 does not.