Everything the web app does, you can do from your own code: send an analysis, get back a
structured audit with a verdict, a methodology review, a seven-pitfall scan, calculation
spot-checks and the caveats that must travel with the numbers. Useful for gating a reporting
pipeline, auditing a batch of dashboards, or refusing to publish anything that comes back
“Needs revision”. Base URL https://api.skillsafe.ai/v1/app-api.
The object you send. Only analysis is required.
| Field | Type | Meaning |
|---|---|---|
analysis | string | The analysis as written: prose, markdown/TSV/CSV tables, SQL and its results, methodology notes, the stated conclusion. Clipped at 60,000 characters — from the middle, keeping both ends, because an analysis states its recommendation last and that is the part most worth auditing. |
context | string | Optional. The question the analysis answers, the audience, the decision that hangs on it, how the data was pulled. This materially changes the verdict: the same numbers can pass as a team update and fail as the basis for a budget decision. |
numscan | string | Optional. A summary of mechanical checks you ran yourself over the same text. Treated as a hint, not a fact — each item is verified against the analysis before it is repeated, and anything unconfirmable is dropped. The web app supplies its browser-side number scan here. |
previous | object | Optional. Present only on a re-audit, carrying the earlier verdict, confidence, unresolved pitfalls and failed_checks. The audit then reports, issue by issue, whether the revision resolved it, left it open, or replaced it with something new. |
retry_note | string | Optional, and not for humans. Tells the model its previous reply did not parse and to re-emit the same audit in the required shape. |
Every call carries Authorization: Bearer <token>. Open the token page to sign in, reveal your token and copy a ready-made shell export. It never asks you to open the DevTools console. A guest token works for reading and estimating; a signed-in token is needed to run.
# Every call below reuses this. Get the token from the token page
# linked in step 1 - never paste it into a shared shell history.
export SKILLSAFE_TOKEN="YOUR_TOKEN"
export SKILLSAFE_BASE="https://api.skillsafe.ai/v1/app-api"
import json, os, urllib.request
BASE = "https://api.skillsafe.ai/v1/app-api"
TOKEN = os.environ.get("SKILLSAFE_TOKEN", "YOUR_TOKEN")
def call(method, path, json_body=None):
data = json.dumps(json_body).encode() if json_body is not None else None
req = urllib.request.Request(BASE + path, data=data, method=method)
req.add_header("Authorization", "Bearer " + TOKEN)
if data: req.add_header("Content-Type", "application/json")
with urllib.request.urlopen(req) as r:
env = json.loads(r.read())
if not env.get("ok"):
raise RuntimeError(env["error"]["code"] + ": " + env["error"].get("message", ""))
return env["data"]
const BASE = "https://api.skillsafe.ai/v1/app-api";
// Read it from your own secret store; never hard-code a real token.
const TOKEN = "YOUR_TOKEN";
async function call(method, path, body) {
const res = await fetch(BASE + path, {
method,
headers: {
Authorization: `Bearer ${TOKEN}`,
...(body ? { "Content-Type": "application/json" } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const env = await res.json();
if (!env.ok) throw new Error(`${env.error.code}: ${env.error.message ?? ""}`);
return env.data;
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
)
const base = "https://api.skillsafe.ai/v1/app-api"
func call(method, path string, body any) (map[string]any, error) {
var rdr io.Reader
if body != nil {
b, _ := json.Marshal(body)
rdr = bytes.NewReader(b)
}
req, _ := http.NewRequest(method, base+path, rdr)
req.Header.Set("Authorization", "Bearer "+os.Getenv("SKILLSAFE_TOKEN"))
if body != nil { req.Header.Set("Content-Type", "application/json") }
res, err := http.DefaultClient.Do(req)
if err != nil { return nil, err }
defer res.Body.Close()
var env struct {
OK bool `json:"ok"`
Data map[string]any `json:"data"`
Error struct{ Code, Message string } `json:"error"`
}
if err := json.NewDecoder(res.Body).Decode(&env); err != nil { return nil, err }
if !env.OK { return nil, fmt.Errorf("%s: %s", env.Error.Code, env.Error.Message) }
return env.Data, nil
}
import java.net.URI;
import java.net.http.*;
class AnalysisAuditor {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static final String TOKEN = System.getenv().getOrDefault("SKILLSAFE_TOKEN", "YOUR_TOKEN");
static final HttpClient HTTP = HttpClient.newHttpClient();
static String call(String method, String path, String body) throws Exception {
var b = HttpRequest.newBuilder(URI.create(BASE + path))
.header("Authorization", "Bearer " + TOKEN);
if (body != null) {
b.header("Content-Type", "application/json");
b.method(method, HttpRequest.BodyPublishers.ofString(body));
} else {
b.method(method, HttpRequest.BodyPublishers.noBody());
}
var res = HTTP.send(b.build(), HttpResponse.BodyHandlers.ofString());
return res.body(); // {"ok":true,"data":{...}} - decode with your JSON library
}
}
require "json"
require "net/http"
require "uri"
BASE = "https://api.skillsafe.ai/v1/app-api"
TOKEN = ENV.fetch("SKILLSAFE_TOKEN", "YOUR_TOKEN")
def call(method, path, body = nil)
uri = URI(BASE + path)
klass = { "GET" => Net::HTTP::Get, "POST" => Net::HTTP::Post }.fetch(method)
req = klass.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
if body
req["Content-Type"] = "application/json"
req.body = JSON.generate(body)
end
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
env = JSON.parse(res.body)
raise "#{env["error"]["code"]}: #{env["error"]["message"]}" unless env["ok"]
env["data"]
end
<?php
const BASE = "https://api.skillsafe.ai/v1/app-api";
$TOKEN = getenv("SKILLSAFE_TOKEN") ?: "YOUR_TOKEN";
function call($method, $path, $body = null) {
global $TOKEN;
$headers = ["Authorization: Bearer $TOKEN"];
if ($body !== null) $headers[] = "Content-Type: application/json";
$ch = curl_init(BASE . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $body === null ? null : json_encode($body),
]);
$env = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($env["ok"])) throw new Exception($env["error"]["code"]);
return $env["data"];
}
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
const string Base = "https://api.skillsafe.ai/v1/app-api";
var token = Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN") ?? "YOUR_TOKEN";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
async Task<string> Call(string method, string path, string? body) {
var req = new HttpRequestMessage(new HttpMethod(method), Base + path);
if (body != null)
req.Content = new StringContent(body, Encoding.UTF8, "application/json");
var res = await http.SendAsync(req);
return await res.Content.ReadAsStringAsync(); // {"ok":true,"data":{...}}
}
Confirms who the token belongs to and how many credits are available. Do this before a run: a 402 after submitting is avoidable.
curl -s -X GET "https://api.skillsafe.ai/v1/app-api/me" \
-H "Authorization: Bearer $SKILLSAFE_TOKEN"
r = call("GET", "/me")
print(r)
const me = await call("GET", "/me");
console.log(me);
me, err := call("GET", "/me", nil)
if err != nil { log.Fatal(err) }
fmt.Println(me)
String me = call("GET", "/me", null);
System.out.println(me);
me = call("GET", "/me")
puts me
$me = call("GET", "/me");
print_r($me);
var me = await Call("GET", "/me", null);
Console.WriteLine(me);
Returns the credit hold a run would reserve, plus the resolved model and markup. It creates no job and charges nothing, so it is safe to call on every keystroke.
curl -s -X POST "https://api.skillsafe.ai/v1/app-api/estimate" \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"}'
r = call("POST", "/estimate",
json={
"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.",
"context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.",
"numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"
})
print(r)
const est = await call("POST", "/estimate", {"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"});
console.log(est);
est, err := call("POST", "/estimate", {"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"})
if err != nil { log.Fatal(err) }
fmt.Println(est)
String est = call("POST", "/estimate", "{\"analysis\": \"Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.\", \"context\": \"Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.\", \"numscan\": \"scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)\"}");
System.out.println(est);
est = call("POST", "/estimate", {"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"})
puts est
$est = call("POST", "/estimate", {"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"});
print_r($est);
var est = await Call("POST", "/estimate", "{\"analysis\": \"Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.\", \"context\": \"Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.\", \"numscan\": \"scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)\"}");
Console.WriteLine(est);
Creates a job and returns when it is terminal. Always send an Idempotency-Key: a retried request with the same key returns the original job instead of billing twice.
curl -s -X POST "https://api.skillsafe.ai/v1/app-api/run" \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"}'
r = call("POST", "/run",
json={
"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.",
"context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.",
"numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"
})
print(r)
const job = await call("POST", "/run", {"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"});
console.log(job);
job, err := call("POST", "/run", {"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"})
if err != nil { log.Fatal(err) }
fmt.Println(job)
String job = call("POST", "/run", "{\"analysis\": \"Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.\", \"context\": \"Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.\", \"numscan\": \"scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)\"}");
System.out.println(job);
job = call("POST", "/run", {"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"})
puts job
$job = call("POST", "/run", {"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"});
print_r($job);
var job = await Call("POST", "/run", "{\"analysis\": \"Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.\", \"context\": \"Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.\", \"numscan\": \"scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)\"}");
Console.WriteLine(job);
Same job, delivered as server-sent events. delta events carry incremental text, job carries the job id, and done carries the authoritative full output - trust done over the concatenated deltas, which can drop the tail.
curl -N -X POST "https://api.skillsafe.ai/v1/app-api/run-stream" \
-H "Authorization: Bearer $SKILLSAFE_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: analysis-auditor-$(date +%s)" \
-d '{"analysis": "Q3 conversion review\n\n| Segment | Visitors | Signups | Rate |\n| --- | --- | --- | --- |\n| SMB | 41200 | 1236 | 3.0% |\n| Mid-market | 8800 | 449 | 5.1% |\n| Enterprise | 1100 | 88 | 8.0% |\n| Total | 51100 | 1773 | 5.4% |\n\nBlended rate is 5.4%, up from 4.1% in Q2. Recommend shifting spend to Enterprise.", "context": "Question: which segment should we move Q4 budget into? Audience: exec staff meeting, decision on a $400k budget shift. Q2 numbers came from the old warehouse; Q3 from the new one.", "numscan": "scanned 1 table, 4 data rows, 12 numbers\n- [WARN total_row] Total row: Rate column total 5.4 disagrees with the weighted mean of its column (3.5)"}'
# event: delta data: {"text":"VERDICT: Needs revision..."}
# event: job data: {"job_id":"job_..."}
# event: done data: {"output":{"output":"VERDICT: ..."},"charged_credits":1103}
import json, urllib.request
req = urllib.request.Request(BASE + "/run-stream",
data=json.dumps(INPUT).encode(), method="POST")
req.add_header("Authorization", "Bearer " + TOKEN)
req.add_header("Content-Type", "application/json")
req.add_header("Idempotency-Key", "analysis-auditor-001")
raw = []
with urllib.request.urlopen(req) as r:
event = None
for line in r:
line = line.decode().rstrip("\n")
if line.startswith("event:"):
event = line[6:].strip()
elif line.startswith("data:"):
payload = json.loads(line[5:].strip())
if event == "delta":
raw.append(payload.get("text", ""))
elif event == "done":
raw = [payload["output"]["output"]]
print("".join(raw))
const res = await fetch(`${BASE}/run-stream`, {
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Content-Type": "application/json",
"Idempotency-Key": "analysis-auditor-001",
},
body: JSON.stringify(INPUT),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "", raw = "", event = null;
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() ?? "";
for (const line of lines) {
if (line.startsWith("event:")) event = line.slice(6).trim();
else if (line.startsWith("data:")) {
const payload = JSON.parse(line.slice(5).trim());
if (event === "delta") raw += payload.text ?? "";
if (event === "done") raw = payload.output.output;
}
}
}
console.log(raw);
// SSE: read the body line by line rather than decoding it as one JSON document.
req, _ := http.NewRequest("POST", base+"/run-stream",
bytes.NewReader(inputJSON))
req.Header.Set("Authorization", "Bearer "+os.Getenv("SKILLSAFE_TOKEN"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "analysis-auditor-001")
res, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer res.Body.Close()
sc := bufio.NewScanner(res.Body)
var event, raw string
for sc.Scan() {
line := sc.Text()
switch {
case strings.HasPrefix(line, "event:"):
event = strings.TrimSpace(line[6:])
case strings.HasPrefix(line, "data:"):
var p struct {
Text string `json:"text"`
Output struct{ Output string `json:"output"` } `json:"output"`
}
json.Unmarshal([]byte(strings.TrimSpace(line[5:])), &p)
if event == "delta" { raw += p.Text }
if event == "done" { raw = p.Output.Output }
}
}
fmt.Println(raw)
// Stream the response body and split on SSE line prefixes.
var req = HttpRequest.newBuilder(URI.create(BASE + "/run-stream"))
.header("Authorization", "Bearer " + TOKEN)
.header("Content-Type", "application/json")
.header("Idempotency-Key", "analysis-auditor-001")
.POST(HttpRequest.BodyPublishers.ofString(inputJson))
.build();
var res = HTTP.send(req, HttpResponse.BodyHandlers.ofLines());
var raw = new StringBuilder();
final String[] event = { null };
res.body().forEach(line -> {
if (line.startsWith("event:")) event[0] = line.substring(6).trim();
else if (line.startsWith("data:") && "delta".equals(event[0])) {
// decode {"text":"..."} with your JSON library and append
raw.append(extractText(line.substring(5).trim()));
}
});
System.out.println(raw);
require "net/http"
uri = URI(BASE + "/run-stream")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{TOKEN}"
req["Content-Type"] = "application/json"
req["Idempotency-Key"] = "analysis-auditor-001"
req.body = JSON.generate(INPUT)
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|
if line.start_with?("event:")
event = line[6..].strip
elsif line.start_with?("data:")
p = JSON.parse(line[5..].strip)
raw << p["text"].to_s if event == "delta"
raw = p["output"]["output"] if event == "done"
end
end
end
end
end
puts raw
<?php
$raw = "";
$event = null;
$ch = curl_init(BASE . "/run-stream");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $TOKEN",
"Content-Type: application/json",
"Idempotency-Key: analysis-auditor-001"],
CURLOPT_POSTFIELDS => json_encode($INPUT),
CURLOPT_WRITEFUNCTION => function ($ch, $chunk) use (&$raw, &$event) {
foreach (explode("\n", $chunk) as $line) {
if (str_starts_with($line, "event:")) $event = trim(substr($line, 6));
elseif (str_starts_with($line, "data:")) {
$p = json_decode(trim(substr($line, 5)), true);
if ($event === "delta") $raw .= $p["text"] ?? "";
if ($event === "done") $raw = $p["output"]["output"];
}
}
return strlen($chunk);
},
]);
curl_exec($ch);
curl_close($ch);
echo $raw;
var req = new HttpRequestMessage(HttpMethod.Post, Base + "/run-stream");
req.Headers.Add("Idempotency-Key", "analysis-auditor-001");
req.Content = new StringContent(inputJson, Encoding.UTF8, "application/json");
var res = await http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
using var stream = await res.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
string? evt = null;
var raw = new StringBuilder();
while (!reader.EndOfStream) {
var line = await reader.ReadLineAsync();
if (line is null) continue;
if (line.StartsWith("event:")) evt = line[6..].Trim();
else if (line.StartsWith("data:") && evt == "delta") {
// decode {"text":"..."} with System.Text.Json and append
raw.Append(ExtractText(line[5..].Trim()));
}
}
Console.WriteLine(raw);
data.output.output is plain text in exactly this shape. This is what the app's
parser decodes; a reply that breaks any rule below is discarded and retried once.
VERDICT: <Ready to share | Share with noted caveats | Needs revision>
CONFIDENCE: <integer 0-100>
SUMMARY: <2 to 4 sentences>
## Methodology
- <finding>
## Pitfall scan
- <Pitfall name> - <CLEAR|SUSPECT|FOUND>: <evidence>
## Calculation checks
- <PASS|FAIL>: <the check, with the actual numbers>
## Required caveats
- <caveat>
## Suggested improvements
- <suggestion>
## Open questions
- <question>
VERDICT: is the first line and
must be exactly one of the three phrases. CONFIDENCE: is a bare integer 0-100.
SUMMARY: may wrap and ends at the first blank line. All six ##
headings must appear, spelled exactly, in that order. Every line inside a section is a
- bullet, which may wrap onto indented continuation lines. An empty section
carries the single bullet - None.
A FOUND pitfall or a FAILed calculation check forbids “Ready to share”. There is no minor qualifier that gets around it. The renderer independently recomputes this from the parsed sections and flags the reply if the verdict line contradicts its own findings — worth reproducing in your own client rather than trusting the verdict string alone.
Join explosion, survivorship bias, incomplete period comparison, denominator shifting,
average of averages, timezone mismatch, selection bias. Each appears once in the pitfall
scan marked CLEAR, SUSPECT or FOUND, with one line of
evidence quoted from the analysis.
numscan flag that cannot be confirmed against the text is dropped rather than
repeated.
Every response is {"ok": true, "data": {...}} or
{"ok": false, "error": {"code": "...", "message": "..."}}. Check
ok before reading data.
| Status | Code | What to do |
|---|---|---|
| 400 | VALIDATION_ERROR | The input shape is wrong. error.details names the field. |
| 401 | UNAUTHORIZED | Missing, malformed or expired token. Mint a new one from the token page. |
| 402 | PAYMENT_REQUIRED | The balance is below the run's hold. Call /estimate first and compare against /me. |
| 404 | NOT_FOUND | Wrong slug or job id. |
| 429 | RATE_LIMITED | Back off and retry with the same idempotency key. |
| 5xx | INTERNAL | Retry with the same idempotency key; a completed job is returned rather than re-billed. |
Idempotency-Key on every /run
and /run-stream. Derive it from a hash of the input plus an attempt counter, so
a network retry collapses server-side while a genuine re-run gets its own key. The app does
exactly this, including on its automatic reformat retry.