Driving FIX Log Desk from code
Everything the page does through the AI lanes, you can do over HTTP. The browser-side reader is not an API — it runs in the page and needs no account — so what is documented here is the metered part: estimating a lane, running it, and reading back the JSON contract that the page renders.
Base URL and envelope
Every endpoint below hangs off https://api.skillsafe.ai/v1/app-api. The app is
addressed by the token you present, which is scoped to this app — there is no
X-App-Slug header, and adding one changes nothing.
Every response is one of two shapes:
{ "ok": true, "data": { ... } }
{ "ok": false, "error": { "code": "VALIDATION_ERROR", "message": "...", "details": { ... } } }
Errors
| code | HTTP | what it means and what to do |
|---|---|---|
UNAUTHORIZED | 401 | No token, a malformed token, or one that has expired. Mint a new one. |
FORBIDDEN | 403 | A guest token on a metered call. Running a lane needs a signed-in user unless the publisher sponsors guests. |
INSUFFICIENT_CREDITS | 402 | The balance is below min_credits. Check /me against /estimate before submitting. |
VALIDATION_ERROR | 400 | The input object did not match what the app accepts. The offending field is in error.details. |
NOT_FOUND | 404 | No job with that id for this subject. |
RATE_LIMITED | 429 | Back off and retry with the SAME Idempotency-Key. Never tight-loop. |
INTERNAL | 500 | Retry once with the same Idempotency-Key; the reservation is reused. |
The input contract — task comes first
The run input is the object itself, not wrapped in an input key. Its first and
most important field is task, which selects the lane. Everything else is the same
for all four lanes.
| field | type | what it is |
|---|---|---|
task | string | Required. One of session, lifecycle, execution, surveillance. If it is missing or unrecognised the model picks the closest lane and names the lane it chose in headline rather than blending two contracts. |
facts | object | Required. The measurement block. The page builds it from its own reader; you can download exactly this object from the read-out panel with the Facts JSON button and post it back. It carries file, quarantined, fix_versions, message_types, symbols, time_span_utc, sessions, flags, flag_counts, redaction_note, context, plus the lane-specific expansion (session_detail and health; orders and cancel_rejects; execution; surveillance). |
log_excerpt | string | The raw messages, one per line, each prefixed with its M<n> index. Cut on whole-message boundaries when it is too large, never inside a message. Secrets are already redacted. |
clip | object | messages_total, messages_sent, chars_cut, clipped, note. Tells the model that the facts cover the whole file even when the excerpt does not. |
context | object | our_comp_id, counterparty, asset_class, note. All optional. note is what the user is trying to find out, and it changes what the review leads with. |
The four lanes
| task | question | verdict words | what body carries |
|---|---|---|---|
session | Did the transport work? | healthy | degraded | broken | body.session_state[], body.logon_terms, body.timeline[], body.gap_analysis[], body.reject_analysis[], body.recovery_steps[] |
lifecycle | What happened to each order? | reconciled | discrepancies | unreconstructable | body.orders[] (one per order in facts.orders, including the clean ones), body.races[], body.state_machine_notes[] |
execution | What can honestly be said about these fills? | documented | gaps | not-assessable | body.benchmark_statement, body.per_order[], body.venue_notes[], body.best_execution_factors[] (all five FINRA 5310 factors), body.data_gaps[] |
surveillance | What would a surveillance screen make of this tape? | no-signatures | inputs-present | escalate-to-review | body.signature_readout[], body.candidates[], body.reporting_notes[], body.not_concluded |
One worked input per lane
Identical apart from task. The facts object differs per lane only in which section is expanded; the page rebuilds it on every lane switch, and so should you.
task: "session" — Did the transport work?
{
"task": "session",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": false,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
}
task: "lifecycle" — What happened to each order?
{
"task": "lifecycle",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": false,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
}
task: "execution" — What can honestly be said about these fills?
{
"task": "execution",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": false,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
}
task: "surveillance" — What would a surveillance screen make of this tape?
{
"task": "surveillance",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": false,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
}
The output contract
The model returns one JSON object and nothing else. These keys are present on
every lane; only body differs. This is taken from the parsing code the page
actually runs, not from intent — a field listed here is a field the renderer reads.
{
"lane": "session | lifecycle | execution | surveillance",
"title": "string, under 80 characters",
"verdict": "one of the lane's verdict words",
"confidence": "high | medium | low",
"headline": "one sentence",
"summary": "two to five sentences",
"findings": [
{ "id": "F1", "severity": "critical | high | medium | low | info",
"title": "string", "what": "string", "evidence": ["M17", "O:ORD-2001"],
"why": "string", "remedy": "string" }
],
"reconciliation": [
{ "flag_id": "X1", "status": "confirmed | revised | cleared", "note": "string" }
],
"checklist": [ { "item": "string", "owner": "string" } ],
"open_questions": ["string"],
"citations": ["M17", "O:ORD-2001", "X4"],
"next_lane": { "lane": "session | lifecycle | execution | surveillance", "why": "string" },
"question_answered": "string, or null - answers context.question directly",
"body": { }
}
Nullable fields are real. Any quantity, price or rate the file does not
establish comes back null, never 0. fill_rate: null
(nothing came back at all) and fill_rate: 0 (acknowledged, then cancelled
unfilled) are different facts and the renderer keeps them apart.
Two contracts the page enforces after the fact
Reconciliation. reconciliation must carry one entry for every flag
in facts.flags whose severity is critical or high. The page
renders a table of those flags against what the review said, and any it walked past is displayed
as UNACCOUNTED FOR rather than quietly dropped. If you are consuming this API yourself,
do the same check — it is the difference between a review and a summary.
The citation grammar is closed. Every reference must be one of
M<n> (a message index, and every integer from 1 to
facts.file.message_count is valid), O:<ClOrdID> (an order, by
any id in its chain), X<n> (a flag),
S:<sender> -> <target> (one direction of a session),
SEQ:<session>#<n> (one sequence number, present or missing),
SYM:<symbol>, V:<venue>, or T<tag>.
facts.citable enumerates exactly what qualifies. The page checks every reference
— including the ones inside body, not just the ones in
citations — and shows an invented one struck through.
SEQ: exists because a message that never arrived has no M<n>,
and in a gap analysis the hole is the subject. There is deliberately no form for a fill, a
price or a time: cite the message that carries it and give the value in words.
Steps
1. Get a token
Every call needs Authorization: Bearer <token>. A guest
token is enough for /me and /estimate; running a lane is metered and
needs a personal token, which comes from signing in. The easiest way to get
either without opening DevTools is the token panel:
fixlog-desk.skillsafe.ai/tokens.html - it reads the token this browser
already holds, shows the session state, and will mint a fresh guest token on request.
To mint a guest token from code, POST to /guest with no body and no
authorization; the token comes back in data.token.
curl -s -X POST \
"https://api.skillsafe.ai/v1/app-api/guest" \
-H "Authorization: Bearer YOUR_TOKEN"
import json, urllib.request
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
def call(method, path, body=None, headers=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(BASE + path, data=data, method=method)
req.add_header("Authorization", "Bearer " + TOKEN)
if body is not None:
req.add_header("Content-Type", "application/json")
for k, v in (headers or {}).items():
req.add_header(k, v)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
out = call("POST", "/guest")
print(json.dumps(out["data"], indent=2))
const TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const BASE = "https://api.skillsafe.ai/v1/app-api";
async function call(method, path, body, extra) {
const res = await fetch(BASE + path, {
method,
headers: Object.assign({ "Authorization": "Bearer " + TOKEN },
body ? { "Content-Type": "application/json" } : {}, extra || {}),
body: body ? JSON.stringify(body) : undefined
});
const json = await res.json();
if (!res.ok) throw new Error(json.error ? json.error.code + ": " + json.error.message : res.status);
return json.data;
}
const out = await call("POST", "/guest");
console.log(out);
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const token = "YOUR_TOKEN" // from https://fixlog-desk.skillsafe.ai/tokens.html
const base = "https://api.skillsafe.ai/v1/app-api"
func main() {
req, _ := http.NewRequest("POST", base+"/guest", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
raw, _ := io.ReadAll(res.Body)
var out map[string]interface{}
json.Unmarshal(raw, &out)
fmt.Println(out["data"])
}
import java.net.URI;
import java.net.http.*;
public class Example {
static final String TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
public static void main(String[] args) throws Exception {
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/guest"))
.header("Authorization", "Bearer " + TOKEN)
.method("POST", HttpRequest.BodyPublishers.noBody());
HttpResponse<String> res = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}
require "json"
require "net/http"
require "uri"
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI(BASE + "/guest")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer " + TOKEN
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.pretty_generate(JSON.parse(res.body)["data"])
<?php
$token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
$base = "https://api.skillsafe.ai/v1/app-api";
$ch = curl_init($base . "/guest");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $token]);
$res = curl_exec($ch);
curl_close($ch);
$out = json_decode($res, true);
print_r($out["data"]);
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Example {
const string Token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const string Base = "https://api.skillsafe.ai/v1/app-api";
static async Task Main() {
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + Token);
var res = await http.GetAsync(Base + "/guest");
Console.WriteLine(await res.Content.ReadAsStringAsync());
}
}
2. Check who you are and what you can spend
/me tells you the subject type (user or guest) and
the credit balance. Compare the balance against the min_credits from the next step
before you submit anything - a 402 after submit is a failure of your client, not of the user.
curl -s -X GET \
"https://api.skillsafe.ai/v1/app-api/me" \
-H "Authorization: Bearer YOUR_TOKEN"
import json, urllib.request
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
def call(method, path, body=None, headers=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(BASE + path, data=data, method=method)
req.add_header("Authorization", "Bearer " + TOKEN)
if body is not None:
req.add_header("Content-Type", "application/json")
for k, v in (headers or {}).items():
req.add_header(k, v)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
out = call("GET", "/me")
print(json.dumps(out["data"], indent=2))
const TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const BASE = "https://api.skillsafe.ai/v1/app-api";
async function call(method, path, body, extra) {
const res = await fetch(BASE + path, {
method,
headers: Object.assign({ "Authorization": "Bearer " + TOKEN },
body ? { "Content-Type": "application/json" } : {}, extra || {}),
body: body ? JSON.stringify(body) : undefined
});
const json = await res.json();
if (!res.ok) throw new Error(json.error ? json.error.code + ": " + json.error.message : res.status);
return json.data;
}
const out = await call("GET", "/me");
console.log(out);
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const token = "YOUR_TOKEN" // from https://fixlog-desk.skillsafe.ai/tokens.html
const base = "https://api.skillsafe.ai/v1/app-api"
func main() {
req, _ := http.NewRequest("GET", base+"/me", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
raw, _ := io.ReadAll(res.Body)
var out map[string]interface{}
json.Unmarshal(raw, &out)
fmt.Println(out["data"])
}
import java.net.URI;
import java.net.http.*;
public class Example {
static final String TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
public static void main(String[] args) throws Exception {
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/me"))
.header("Authorization", "Bearer " + TOKEN)
.method("GET", HttpRequest.BodyPublishers.noBody());
HttpResponse<String> res = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}
require "json"
require "net/http"
require "uri"
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI(BASE + "/me")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer " + TOKEN
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.pretty_generate(JSON.parse(res.body)["data"])
<?php
$token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
$base = "https://api.skillsafe.ai/v1/app-api";
$ch = curl_init($base . "/me");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $token]);
$res = curl_exec($ch);
curl_close($ch);
$out = json_decode($res, true);
print_r($out["data"]);
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Example {
const string Token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const string Base = "https://api.skillsafe.ai/v1/app-api";
static async Task Main() {
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + Token);
var res = await http.GetAsync(Base + "/me");
Console.WriteLine(await res.Content.ReadAsStringAsync());
}
}
3. Estimate - free, and the authoritative model check
/estimate costs nothing and creates no job. It returns model,
model_alias, markup_bps, hold_credits and
min_credits. hold_credits is a reservation priced against the
full output cap, not the price: what is actually charged is nearly always lower. Estimate the
lane you are about to run - the four lanes send different facts and reserve different amounts, so
a hold from one lane is not the hold for another.
curl -s -X POST \
"https://api.skillsafe.ai/v1/app-api/estimate" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"task": "lifecycle", "facts": "{...}", "log_excerpt": "...", "context": {"our_comp_id": "BUYSIDE1"}}'
import json, urllib.request
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
def call(method, path, body=None, headers=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(BASE + path, data=data, method=method)
req.add_header("Authorization", "Bearer " + TOKEN)
if body is not None:
req.add_header("Content-Type", "application/json")
for k, v in (headers or {}).items():
req.add_header(k, v)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
body = {
"task": "lifecycle",
"facts": "{...}",
"log_excerpt": "...",
"context": {
"our_comp_id": "BUYSIDE1"
}
}
out = call("POST", "/estimate", body)
print(json.dumps(out["data"], indent=2))
const TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const BASE = "https://api.skillsafe.ai/v1/app-api";
async function call(method, path, body, extra) {
const res = await fetch(BASE + path, {
method,
headers: Object.assign({ "Authorization": "Bearer " + TOKEN },
body ? { "Content-Type": "application/json" } : {}, extra || {}),
body: body ? JSON.stringify(body) : undefined
});
const json = await res.json();
if (!res.ok) throw new Error(json.error ? json.error.code + ": " + json.error.message : res.status);
return json.data;
}
const body = {
"task": "lifecycle",
"facts": "{...}",
"log_excerpt": "...",
"context": {
"our_comp_id": "BUYSIDE1"
}
};
const out = await call("POST", "/estimate", body);
console.log(out);
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const token = "YOUR_TOKEN" // from https://fixlog-desk.skillsafe.ai/tokens.html
const base = "https://api.skillsafe.ai/v1/app-api"
func main() {
body := []byte(`{"task": "lifecycle", "facts": "{...}", "log_excerpt": "...", "context": {"our_comp_id": "BUYSIDE1"}}`)
req, _ := http.NewRequest("POST", base+"/estimate", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
raw, _ := io.ReadAll(res.Body)
var out map[string]interface{}
json.Unmarshal(raw, &out)
fmt.Println(out["data"])
}
import java.net.URI;
import java.net.http.*;
public class Example {
static final String TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
public static void main(String[] args) throws Exception {
String body = """
{
"task": "lifecycle",
"facts": "{...}",
"log_excerpt": "...",
"context": {
"our_comp_id": "BUYSIDE1"
}
}
""";
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/estimate"))
.header("Authorization", "Bearer " + TOKEN)
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(body));
HttpResponse<String> res = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}
require "json"
require "net/http"
require "uri"
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI(BASE + "/estimate")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer " + TOKEN
req["Content-Type"] = "application/json"
req.body = "{\"task\": \"lifecycle\", \"facts\": \"{...}\", \"log_excerpt\": \"...\", \"context\": {\"our_comp_id\": \"BUYSIDE1\"}}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.pretty_generate(JSON.parse(res.body)["data"])
<?php
$token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
$base = "https://api.skillsafe.ai/v1/app-api";
$ch = curl_init($base . "/estimate");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $token, "Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\"task\": \"lifecycle\", \"facts\": \"{...}\", \"log_excerpt\": \"...\", \"context\": {\"our_comp_id\": \"BUYSIDE1\"}}");
$res = curl_exec($ch);
curl_close($ch);
$out = json_decode($res, true);
print_r($out["data"]);
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Example {
const string Token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const string Base = "https://api.skillsafe.ai/v1/app-api";
static async Task Main() {
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + Token);
var body = new StringContent(@"{""task"": ""lifecycle"", ""facts"": ""{...}"", ""log_excerpt"": ""..."", ""context"": {""our_comp_id"": ""BUYSIDE1""}}",
Encoding.UTF8, "application/json");
var res = await http.PostAsync(Base + "/estimate", body);
Console.WriteLine(await res.Content.ReadAsStringAsync());
}
}
4. Run a lane and poll for the result
POST the run input to /run; you get back a job_id. Poll
/jobs/{id} until status is terminal. Always send an
Idempotency-Key derived from the task plus a hash of the input: a network
blip that makes you retry must not bill the user twice. The app itself uses
fix-<task>-<input hash>-<attempt>.
curl -s -X POST \
"https://api.skillsafe.ai/v1/app-api/run" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Idempotency-Key: fix-lifecycle-3k9x2m1a-1" \
-H "Content-Type: application/json" \
-d '{"task": "lifecycle", "facts": "{ the whole facts object - see the Input contract section }", "log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...", "clip": {"messages_total": 73, "messages_sent": 73, "chars_cut": 0, "clipped": false, "note": "This excerpt holds every message in the file."}, "context": {"our_comp_id": "BUYSIDE1", "counterparty": "BRKR", "asset_class": "equities", "note": "The desk says they cancelled and it still filled."}}'
import json, urllib.request
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
def call(method, path, body=None, headers=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(BASE + path, data=data, method=method)
req.add_header("Authorization", "Bearer " + TOKEN)
if body is not None:
req.add_header("Content-Type", "application/json")
for k, v in (headers or {}).items():
req.add_header(k, v)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
body = {
"task": "lifecycle",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": False,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
}
out = call("POST", "/run", body, {'Idempotency-Key': 'fix-lifecycle-3k9x2m1a-1'})
print(json.dumps(out["data"], indent=2))
const TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const BASE = "https://api.skillsafe.ai/v1/app-api";
async function call(method, path, body, extra) {
const res = await fetch(BASE + path, {
method,
headers: Object.assign({ "Authorization": "Bearer " + TOKEN },
body ? { "Content-Type": "application/json" } : {}, extra || {}),
body: body ? JSON.stringify(body) : undefined
});
const json = await res.json();
if (!res.ok) throw new Error(json.error ? json.error.code + ": " + json.error.message : res.status);
return json.data;
}
const body = {
"task": "lifecycle",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": false,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
};
const out = await call("POST", "/run", body, {"Idempotency-Key": "fix-lifecycle-3k9x2m1a-1"});
console.log(out);
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const token = "YOUR_TOKEN" // from https://fixlog-desk.skillsafe.ai/tokens.html
const base = "https://api.skillsafe.ai/v1/app-api"
func main() {
body := []byte(`{"task": "lifecycle", "facts": "{ the whole facts object - see the Input contract section }", "log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...", "clip": {"messages_total": 73, "messages_sent": 73, "chars_cut": 0, "clipped": false, "note": "This excerpt holds every message in the file."}, "context": {"our_comp_id": "BUYSIDE1", "counterparty": "BRKR", "asset_class": "equities", "note": "The desk says they cancelled and it still filled."}}`)
req, _ := http.NewRequest("POST", base+"/run", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Idempotency-Key", "fix-lifecycle-3k9x2m1a-1")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
raw, _ := io.ReadAll(res.Body)
var out map[string]interface{}
json.Unmarshal(raw, &out)
fmt.Println(out["data"])
}
import java.net.URI;
import java.net.http.*;
public class Example {
static final String TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
public static void main(String[] args) throws Exception {
String body = """
{
"task": "lifecycle",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": false,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
}
""";
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/run"))
.header("Authorization", "Bearer " + TOKEN)
.header("Idempotency-Key", "fix-lifecycle-3k9x2m1a-1")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(body));
HttpResponse<String> res = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}
require "json"
require "net/http"
require "uri"
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI(BASE + "/run")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer " + TOKEN
req["Idempotency-Key"] = "fix-lifecycle-3k9x2m1a-1"
req["Content-Type"] = "application/json"
req.body = "{\"task\": \"lifecycle\", \"facts\": \"{ the whole facts object - see the Input contract section }\", \"log_excerpt\": \"M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...\", \"clip\": {\"messages_total\": 73, \"messages_sent\": 73, \"chars_cut\": 0, \"clipped\": false, \"note\": \"This excerpt holds every message in the file.\"}, \"context\": {\"our_comp_id\": \"BUYSIDE1\", \"counterparty\": \"BRKR\", \"asset_class\": \"equities\", \"note\": \"The desk says they cancelled and it still filled.\"}}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.pretty_generate(JSON.parse(res.body)["data"])
<?php
$token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
$base = "https://api.skillsafe.ai/v1/app-api";
$ch = curl_init($base . "/run");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $token, "Content-Type: application/json", "Idempotency-Key: fix-lifecycle-3k9x2m1a-1"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\"task\": \"lifecycle\", \"facts\": \"{ the whole facts object - see the Input contract section }\", \"log_excerpt\": \"M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...\", \"clip\": {\"messages_total\": 73, \"messages_sent\": 73, \"chars_cut\": 0, \"clipped\": false, \"note\": \"This excerpt holds every message in the file.\"}, \"context\": {\"our_comp_id\": \"BUYSIDE1\", \"counterparty\": \"BRKR\", \"asset_class\": \"equities\", \"note\": \"The desk says they cancelled and it still filled.\"}}");
$res = curl_exec($ch);
curl_close($ch);
$out = json_decode($res, true);
print_r($out["data"]);
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Example {
const string Token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const string Base = "https://api.skillsafe.ai/v1/app-api";
static async Task Main() {
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + Token);
http.DefaultRequestHeaders.Add("Idempotency-Key", "fix-lifecycle-3k9x2m1a-1");
var body = new StringContent(@"{""task"": ""lifecycle"", ""facts"": ""{ the whole facts object - see the Input contract section }"", ""log_excerpt"": ""M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|..."", ""clip"": {""messages_total"": 73, ""messages_sent"": 73, ""chars_cut"": 0, ""clipped"": false, ""note"": ""This excerpt holds every message in the file.""}, ""context"": {""our_comp_id"": ""BUYSIDE1"", ""counterparty"": ""BRKR"", ""asset_class"": ""equities"", ""note"": ""The desk says they cancelled and it still filled.""}}",
Encoding.UTF8, "application/json");
var res = await http.PostAsync(Base + "/run", body);
Console.WriteLine(await res.Content.ReadAsStringAsync());
}
}
5. Poll the job
status moves through queued and running to
succeeded or failed. On success the model's text is in
data.output. If data.truncated is true the reply was cut short by the
output cap - surface that to the user rather than presenting a clipped answer as complete.
curl -s -X GET \
"https://api.skillsafe.ai/v1/app-api/jobs/job_REPLACE_ME" \
-H "Authorization: Bearer YOUR_TOKEN"
import json, urllib.request
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
def call(method, path, body=None, headers=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(BASE + path, data=data, method=method)
req.add_header("Authorization", "Bearer " + TOKEN)
if body is not None:
req.add_header("Content-Type", "application/json")
for k, v in (headers or {}).items():
req.add_header(k, v)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
out = call("GET", "/jobs/job_REPLACE_ME")
print(json.dumps(out["data"], indent=2))
const TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const BASE = "https://api.skillsafe.ai/v1/app-api";
async function call(method, path, body, extra) {
const res = await fetch(BASE + path, {
method,
headers: Object.assign({ "Authorization": "Bearer " + TOKEN },
body ? { "Content-Type": "application/json" } : {}, extra || {}),
body: body ? JSON.stringify(body) : undefined
});
const json = await res.json();
if (!res.ok) throw new Error(json.error ? json.error.code + ": " + json.error.message : res.status);
return json.data;
}
const out = await call("GET", "/jobs/job_REPLACE_ME");
console.log(out);
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const token = "YOUR_TOKEN" // from https://fixlog-desk.skillsafe.ai/tokens.html
const base = "https://api.skillsafe.ai/v1/app-api"
func main() {
req, _ := http.NewRequest("GET", base+"/jobs/job_REPLACE_ME", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
raw, _ := io.ReadAll(res.Body)
var out map[string]interface{}
json.Unmarshal(raw, &out)
fmt.Println(out["data"])
}
import java.net.URI;
import java.net.http.*;
public class Example {
static final String TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
public static void main(String[] args) throws Exception {
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/jobs/job_REPLACE_ME"))
.header("Authorization", "Bearer " + TOKEN)
.method("GET", HttpRequest.BodyPublishers.noBody());
HttpResponse<String> res = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}
require "json"
require "net/http"
require "uri"
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI(BASE + "/jobs/job_REPLACE_ME")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer " + TOKEN
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.pretty_generate(JSON.parse(res.body)["data"])
<?php
$token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
$base = "https://api.skillsafe.ai/v1/app-api";
$ch = curl_init($base . "/jobs/job_REPLACE_ME");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $token]);
$res = curl_exec($ch);
curl_close($ch);
$out = json_decode($res, true);
print_r($out["data"]);
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Example {
const string Token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const string Base = "https://api.skillsafe.ai/v1/app-api";
static async Task Main() {
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + Token);
var res = await http.GetAsync(Base + "/jobs/job_REPLACE_ME");
Console.WriteLine(await res.Content.ReadAsStringAsync());
}
}
6. Stream it instead
/run-stream is the same call over Server-Sent Events. Events carry incremental
text in delta and the terminal job in job. This is what the page itself
uses, because a lane takes long enough to want a progress card - and because the named sections of
the output contract arriving in the stream are what advance it.
curl -s -N -X POST \
"https://api.skillsafe.ai/v1/app-api/run-stream" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Idempotency-Key: fix-lifecycle-3k9x2m1a-1" \
-H "Content-Type: application/json" \
-d '{"task": "lifecycle", "facts": "{ the whole facts object - see the Input contract section }", "log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...", "clip": {"messages_total": 73, "messages_sent": 73, "chars_cut": 0, "clipped": false, "note": "This excerpt holds every message in the file."}, "context": {"our_comp_id": "BUYSIDE1", "counterparty": "BRKR", "asset_class": "equities", "note": "The desk says they cancelled and it still filled."}}'
import json, urllib.request
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
def call(method, path, body=None, headers=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(BASE + path, data=data, method=method)
req.add_header("Authorization", "Bearer " + TOKEN)
if body is not None:
req.add_header("Content-Type", "application/json")
for k, v in (headers or {}).items():
req.add_header(k, v)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
body = {
"task": "lifecycle",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": False,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
}
out = call("POST", "/run-stream", body, {'Idempotency-Key': 'fix-lifecycle-3k9x2m1a-1'})
print(json.dumps(out["data"], indent=2))
const TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const BASE = "https://api.skillsafe.ai/v1/app-api";
async function call(method, path, body, extra) {
const res = await fetch(BASE + path, {
method,
headers: Object.assign({ "Authorization": "Bearer " + TOKEN },
body ? { "Content-Type": "application/json" } : {}, extra || {}),
body: body ? JSON.stringify(body) : undefined
});
const json = await res.json();
if (!res.ok) throw new Error(json.error ? json.error.code + ": " + json.error.message : res.status);
return json.data;
}
const body = {
"task": "lifecycle",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": false,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
};
const out = await call("POST", "/run-stream", body, {"Idempotency-Key": "fix-lifecycle-3k9x2m1a-1"});
console.log(out);
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const token = "YOUR_TOKEN" // from https://fixlog-desk.skillsafe.ai/tokens.html
const base = "https://api.skillsafe.ai/v1/app-api"
func main() {
body := []byte(`{"task": "lifecycle", "facts": "{ the whole facts object - see the Input contract section }", "log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...", "clip": {"messages_total": 73, "messages_sent": 73, "chars_cut": 0, "clipped": false, "note": "This excerpt holds every message in the file."}, "context": {"our_comp_id": "BUYSIDE1", "counterparty": "BRKR", "asset_class": "equities", "note": "The desk says they cancelled and it still filled."}}`)
req, _ := http.NewRequest("POST", base+"/run-stream", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Idempotency-Key", "fix-lifecycle-3k9x2m1a-1")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
raw, _ := io.ReadAll(res.Body)
var out map[string]interface{}
json.Unmarshal(raw, &out)
fmt.Println(out["data"])
}
import java.net.URI;
import java.net.http.*;
public class Example {
static final String TOKEN = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
public static void main(String[] args) throws Exception {
String body = """
{
"task": "lifecycle",
"facts": "{ the whole facts object - see the Input contract section }",
"log_excerpt": "M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...",
"clip": {
"messages_total": 73,
"messages_sent": 73,
"chars_cut": 0,
"clipped": false,
"note": "This excerpt holds every message in the file."
},
"context": {
"our_comp_id": "BUYSIDE1",
"counterparty": "BRKR",
"asset_class": "equities",
"note": "The desk says they cancelled and it still filled."
}
}
""";
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/run-stream"))
.header("Authorization", "Bearer " + TOKEN)
.header("Idempotency-Key", "fix-lifecycle-3k9x2m1a-1")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(body));
HttpResponse<String> res = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());
}
}
require "json"
require "net/http"
require "uri"
TOKEN = "YOUR_TOKEN" # from https://fixlog-desk.skillsafe.ai/tokens.html
BASE = "https://api.skillsafe.ai/v1/app-api"
uri = URI(BASE + "/run-stream")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer " + TOKEN
req["Idempotency-Key"] = "fix-lifecycle-3k9x2m1a-1"
req["Content-Type"] = "application/json"
req.body = "{\"task\": \"lifecycle\", \"facts\": \"{ the whole facts object - see the Input contract section }\", \"log_excerpt\": \"M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...\", \"clip\": {\"messages_total\": 73, \"messages_sent\": 73, \"chars_cut\": 0, \"clipped\": false, \"note\": \"This excerpt holds every message in the file.\"}, \"context\": {\"our_comp_id\": \"BUYSIDE1\", \"counterparty\": \"BRKR\", \"asset_class\": \"equities\", \"note\": \"The desk says they cancelled and it still filled.\"}}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.pretty_generate(JSON.parse(res.body)["data"])
<?php
$token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
$base = "https://api.skillsafe.ai/v1/app-api";
$ch = curl_init($base . "/run-stream");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $token, "Content-Type: application/json", "Idempotency-Key: fix-lifecycle-3k9x2m1a-1"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\"task\": \"lifecycle\", \"facts\": \"{ the whole facts object - see the Input contract section }\", \"log_excerpt\": \"M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|...\", \"clip\": {\"messages_total\": 73, \"messages_sent\": 73, \"chars_cut\": 0, \"clipped\": false, \"note\": \"This excerpt holds every message in the file.\"}, \"context\": {\"our_comp_id\": \"BUYSIDE1\", \"counterparty\": \"BRKR\", \"asset_class\": \"equities\", \"note\": \"The desk says they cancelled and it still filled.\"}}");
$res = curl_exec($ch);
curl_close($ch);
$out = json_decode($res, true);
print_r($out["data"]);
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Example {
const string Token = "YOUR_TOKEN"; // from https://fixlog-desk.skillsafe.ai/tokens.html
const string Base = "https://api.skillsafe.ai/v1/app-api";
static async Task Main() {
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + Token);
http.DefaultRequestHeaders.Add("Idempotency-Key", "fix-lifecycle-3k9x2m1a-1");
var body = new StringContent(@"{""task"": ""lifecycle"", ""facts"": ""{ the whole facts object - see the Input contract section }"", ""log_excerpt"": ""M1 20260223-13:29:55.010 OUT 8=FIX.4.4|9=101|35=A|..."", ""clip"": {""messages_total"": 73, ""messages_sent"": 73, ""chars_cut"": 0, ""clipped"": false, ""note"": ""This excerpt holds every message in the file.""}, ""context"": {""our_comp_id"": ""BUYSIDE1"", ""counterparty"": ""BRKR"", ""asset_class"": ""equities"", ""note"": ""The desk says they cancelled and it still filled.""}}",
Encoding.UTF8, "application/json");
var res = await http.PostAsync(Base + "/run-stream", body);
Console.WriteLine(await res.Content.ReadAsStringAsync());
}
}
Notes that save a debugging session
- Estimate every lane you offer.
hold_creditsdiffers per lane because the facts and the prompt sections differ. Showing lane A's hold for lane B's run is the most common way a client misstates a price. hold_creditsis a reservation, not a price. Present it as reserved. What is charged comes back on the terminal job ascharged_creditsand is usually far lower, because the hold prices the full output cap.- Handle
truncated. A balance betweenmin_creditsandhold_creditsstill runs, with a reduced output cap. Say the reply was cut short; do not present it as complete. - Reuse the Idempotency-Key on retry. Including the automatic reformat retry. Two lanes over the same paste are two distinct runs and must not share a key; the same lane retried is one run and must.
- Build
factswith the page, not by hand. The reader is where the value is, and it is free. Load the log in the browser, press Facts JSON, and post that object.
What this app will not do, however you ask it
- Compute slippage, spread capture or implementation shortfall when the log carries no market data. A limit price is the trader's instruction, not the market.
- Use a quarantined message's contents. Anything that fails its own CheckSum or BodyLength is evidence that data is missing, never data.
- Conclude that anyone manipulated a market. The tape lane computes signature statistics, names
the screens that consume them, and always gives an ordinary trading explanation alongside.
The strongest disposition available is
refer-to-compliance.