Driving Owner Map over HTTP
Everything the page does with a model, a script can do. One endpoint does the work; the rest is
authentication and polling. The app takes an aggregate measured from a git history
plus a task field naming which of three lanes to run, and returns a single JSON
envelope whose body shape depends on that lane.
Read one thing first: Owner Map never runs git and has no access to
your repository. It has no clone step, no forge credentials and no webhook. You run
git log yourself, and you send the numbers. The browser app computes those numbers
locally; over the API you compute them and put them in facts.
https://api.skillsafe.ai/v1/app-api
Every request carries Authorization: Bearer <token> and
Content-Type: application/json — and nothing else. There is
no X-App-Slug header: the token is bound to this app when it is
minted, and the slug appears in exactly one place, the JSON body of
POST /guest. A bogus slug header is accepted and ignored, so do not use one as a
check that anything is wired correctly. Get a token from
the token page without opening a developer console.
The response envelope
Every endpoint returns the same wrapper. Success carries data; failure carries
error. Nothing returns a bare value, so a client can branch on the presence of
error alone. The model's own answer is a JSON string nested inside that
wrapper, at data.output.output — parse the wrapper, then parse that string.
{"ok": true, "data": {"job_id": "job_...", "status": "succeeded",
"charged_credits": 1131, "truncated": false,
"output": {"output": "{\"lane\": \"risk\", \"posture\": \"critical\", ...}"}}}
{"ok": false, "error": {"code": "payment_required", "message": "...", "details": {}}}
Errors worth branching on
| code | HTTP | what happened | what to do |
|---|---|---|---|
| unauthorized | 401 | No token, an expired token, or a token minted for another app. | Mint a new one with POST /guest, or sign in again for a personal token. |
| payment_required | 402 | Balance below the run's minimum, or below what the hold needs. | Call POST /estimate first and compare min_credits and hold_credits against GET /me. The page disables its own button rather than submitting into this. |
| validation_error | 400 | The body was not a JSON object, or a field had the wrong type. | Check you sent the input object itself - see the warning in step 3. |
| rate_limited | 429 | Too many requests, or 30/min exceeded on similarity search. | Back off and retry; never tight-loop. |
| not_found | 404 | Unknown job id, or a collection this release does not declare. | Check the id; a job id is only valid for the account that created it. |
A run that finishes with "truncated": true is not an error: the balance sat between
min_credits and hold_credits, so the output cap was reduced and the reply
is cut short. Treat it as incomplete rather than as an answer.
1. Get a token
A guest token is enough for GET /me and POST /estimate.
Running a lane is metered, so it needs a personal token, which comes from signing
in - the token page reveals and copies the one this browser holds
without a developer console. Every POST /guest mints a new guest identity, so
reuse one token across calls: a fresh guest cannot see the previous guest's saved runs.
This is the only call that names the slug, and it names it in the body.
BASE=https://api.skillsafe.ai/v1/app-api
TOKEN=$(curl -sS -X POST "$BASE/guest" \
-H 'Content-Type: application/json' \
-d '{"slug":"owner-map"}' | python3 -c 'import json,sys;print(json.load(sys.stdin)["data"]["token"])')
# a tiny helper every later step reuses
call() { curl -sS -X POST "$BASE$1" -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' --data-binary "$2"; }
import json, urllib.request
BASE = "https://api.skillsafe.ai/v1/app-api"
def post(path, payload, token=None, headers=None):
body = json.dumps(payload).encode()
req = urllib.request.Request(BASE + path, data=body, method="POST")
req.add_header("Content-Type", "application/json")
if token:
req.add_header("Authorization", "Bearer " + token)
for k, v in (headers or {}).items():
req.add_header(k, v)
with urllib.request.urlopen(req) as r:
env = json.load(r)
if not env.get("ok"):
raise RuntimeError(env["error"]["code"] + ": " + env["error"]["message"])
return env["data"]
TOKEN = post("/guest", {"slug": "owner-map"})["token"] # the ONLY place the slug appears
const BASE = "https://api.skillsafe.ai/v1/app-api";
async function post(path, payload, token, headers = {}) {
const res = await fetch(BASE + path, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
...headers
},
body: JSON.stringify(payload)
});
const env = await res.json();
if (!env.ok) throw new Error(`${env.error.code}: ${env.error.message}`);
return env.data;
}
const { token: TOKEN } = await post("/guest", { slug: "owner-map" });
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
const base = "https://api.skillsafe.ai/v1/app-api"
type envelope struct {
OK bool `json:"ok"`
Data json.RawMessage `json:"data"`
Error *struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
func post(path, token string, payload any, headers map[string]string) (json.RawMessage, error) {
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", base+path, bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
for k, v := range headers {
req.Header.Set(k, v)
}
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.OK {
return nil, fmt.Errorf("%s: %s", env.Error.Code, env.Error.Message)
}
return env.Data, nil
}
func main() {
raw, err := post("/guest", "", map[string]string{"slug": "owner-map"}, nil)
if err != nil {
panic(err)
}
var guest struct{ Token string `json:"token"` }
json.Unmarshal(raw, &guest)
fmt.Println("token length", len(guest.Token))
}
import java.net.URI;
import java.net.http.*;
// A JSON library of your choice; the shapes below are plain maps and strings.
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static final HttpClient HTTP = HttpClient.newHttpClient();
static String post(String path, String token, String jsonBody) throws Exception {
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create(BASE + path))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody));
if (token != null) b.header("Authorization", "Bearer " + token);
HttpResponse<String> res = HTTP.send(b.build(), HttpResponse.BodyHandlers.ofString());
return res.body(); // {"ok":true,"data":{...}} - branch on "ok"
}
String guest = post("/guest", null, "{\"slug\":\"owner-map\"}");
// pull data.token out of `guest` with your JSON library
require "json"
require "net/http"
BASE = URI("https://api.skillsafe.ai/v1/app-api")
def post(path, payload, token = nil, headers = {})
uri = URI(BASE.to_s + path)
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req["Authorization"] = "Bearer #{token}" if token
headers.each { |k, v| req[k] = v }
req.body = JSON.dump(payload)
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
TOKEN = post("/guest", { "slug" => "owner-map" })["token"]
<?php
const BASE = "https://api.skillsafe.ai/v1/app-api";
function post(string $path, array $payload, ?string $token = null, array $headers = []): array {
$h = ["Content-Type: application/json"];
if ($token) $h[] = "Authorization: Bearer " . $token;
foreach ($headers as $k => $v) $h[] = "$k: $v";
$ch = curl_init(BASE . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => $h,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
]);
$env = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($env["ok"])) throw new RuntimeException($env["error"]["code"] . ": " . $env["error"]["message"]);
return $env["data"];
}
$token = post("/guest", ["slug" => "owner-map"])["token"];
using System.Net.Http.Json;
using System.Text.Json;
const string Base = "https://api.skillsafe.ai/v1/app-api";
var http = new HttpClient();
async Task<JsonElement> PostAsync(string path, object payload, string? token = null,
(string, string)? extra = null) {
var req = new HttpRequestMessage(HttpMethod.Post, Base + path) {
Content = JsonContent.Create(payload)
};
if (token is not null) req.Headers.Add("Authorization", "Bearer " + token);
if (extra is not null) req.Headers.Add(extra.Value.Item1, extra.Value.Item2);
var res = await http.SendAsync(req);
var env = await res.Content.ReadFromJsonAsync<JsonElement>();
if (!env.GetProperty("ok").GetBoolean()) {
var e = env.GetProperty("error");
throw new Exception($"{e.GetProperty("code")}: {e.GetProperty("message")}");
}
return env.GetProperty("data");
}
var guest = await PostAsync("/guest", new { slug = "owner-map" });
var token = guest.GetProperty("token").GetString()!;
2. Check the session and the balance
GET /me tells you whether the token is a person or a guest, and what the balance is.
Compare it against the hold from step 4 before you run anything: a 402 after submitting is a
failure of the client, not of the user.
curl -sS "$BASE/me" -H "Authorization: Bearer $TOKEN"
# {"ok":true,"data":{"subject_type":"user","credits":184203,...}}
# subject_type is "guest" for a guest token; guests can estimate but not run.
def get(path, token):
req = urllib.request.Request(BASE + path)
req.add_header("Authorization", "Bearer " + token)
with urllib.request.urlopen(req) as r:
env = json.load(r)
if not env.get("ok"):
raise RuntimeError(env["error"]["code"])
return env["data"]
me = get("/me", TOKEN)
print(me["subject_type"], me.get("credits"))
async function get(path, token) {
const res = await fetch(BASE + path, { headers: { Authorization: `Bearer ${token}` } });
const env = await res.json();
if (!env.ok) throw new Error(env.error.code);
return env.data;
}
const me = await get("/me", TOKEN);
console.log(me.subject_type, me.credits);
func get(path, token string) (json.RawMessage, error) {
req, _ := http.NewRequest("GET", base+path, nil)
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var env envelope
json.NewDecoder(res.Body).Decode(&env)
if !env.OK {
return nil, fmt.Errorf("%s", env.Error.Code)
}
return env.Data, nil
}
raw, _ := get("/me", token)
var me struct {
SubjectType string `json:"subject_type"`
Credits int64 `json:"credits"`
}
json.Unmarshal(raw, &me)
fmt.Println(me.SubjectType, me.Credits)
static String get(String path, String token) throws Exception {
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(BASE + path))
.header("Authorization", "Bearer " + token)
.GET().build();
return HTTP.send(req, HttpResponse.BodyHandlers.ofString()).body();
}
System.out.println(get("/me", token)); // data.subject_type, data.credits
def get(path, token)
uri = URI(BASE.to_s + path)
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) }
env = JSON.parse(res.body)
raise env["error"]["code"] unless env["ok"]
env["data"]
end
me = get("/me", TOKEN)
puts "#{me['subject_type']} #{me['credits']}"
function get_(string $path, string $token): array {
$ch = curl_init(BASE . $path);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . $token],
CURLOPT_RETURNTRANSFER => true,
]);
$env = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($env["ok"])) throw new RuntimeException($env["error"]["code"]);
return $env["data"];
}
$me = get_("/me", $token);
echo $me["subject_type"], " ", $me["credits"] ?? 0, PHP_EOL;
async Task<JsonElement> GetAsync(string path, string token) {
var req = new HttpRequestMessage(HttpMethod.Get, Base + path);
req.Headers.Add("Authorization", "Bearer " + token);
var res = await http.SendAsync(req);
var env = await res.Content.ReadFromJsonAsync<JsonElement>();
if (!env.GetProperty("ok").GetBoolean()) throw new Exception(env.GetProperty("error").GetProperty("code").ToString());
return env.GetProperty("data");
}
var me = await GetAsync("/me", token);
Console.WriteLine($"{me.GetProperty("subject_type")} {me.GetProperty("credits")}");
3. Build the run input
The input object is the same for every lane except for two fields. task is
"risk", "handover" or "split"; changed_files is
the raw change-set paste and belongs only to the split lane, whose grouped form also appears inside
facts.change_set.
Do not wrap it as {"input": {...}}. A wrapped body is accepted: it returns
200 and a plausible-looking hold, because the wrapper is priced as text. The model then never sees
your task or your facts, and a real run is billed against a payload the
prompt cannot read, with no error anywhere to catch it. Assert the shape with a free
POST /estimate and sanity-check the hold against a run you have seen before.
facts is the aggregate the app measures in the browser. Everything in it is optional -
the model works from what is present - but nothing outside it may be asserted by the reply, so a
thin facts gets a thin answer. The shape below is the useful minimum; the full shape,
including the directory rollup, the CODEOWNERS reality check, per-file top contributors and the
commit sample, is described in llms.txt.
Produce the history the app expects with:
git log --no-merges --numstat --date=short --pretty=format:"::%H|%an|%ae|%ad|%s"
Flags carry stable ids and are the accountability mechanism: every flag you send with severity
critical or high must come back in coverage_check. If you
send no flags, nothing is checked.
cat > payload.json <<'JSON'
{
"task": "risk",
"repo_label": "atlas-pay",
"notes": "Wei starts a three-month leave in four weeks.",
"facts": {
"method": {"knowledge_score": "sum of 0.5 ^ (age_days / 365) * (1 + log10(1 + lines))",
"bus_factor": "fewest authors whose shares reach 0.5"},
"totals": {"commits": 148, "authors": 6, "files": 17, "firstDate": "2023-03-07",
"lastDate": "2026-08-01", "repoBusFactor": 3, "repoHhi": 0.186,
"bots": 1, "botCommits": 23},
"authors": [
{"name": "Wei Zhang", "commits": 21, "share": 0.178, "status": "on-leave",
"last_commit_days_ago": 109},
{"name": "Dmitri Sokolov", "commits": 28, "share": 0.261, "status": "active",
"last_commit_days_ago": 49}
],
"top_risk_files": [
{"path": "services/auth/mfa.py", "risk": 97, "band": "critical", "bus_factor": 1,
"hhi": 0.77, "primary_owner": "Wei Zhang", "primary_share": 0.866,
"primary_status": "on-leave", "sensitive": true, "generated": false,
"human_commits": 11, "last_touched": "2026-07-18",
"top_contributors": [{"name": "Wei Zhang", "share": 0.866, "status": "on-leave"},
{"name": "Sofia Marchetti", "share": 0.134, "status": "active"}],
"declared_owners": ["@platform-security"], "codeowners_rule": "/services/auth/",
"why": ["bus factor 1", "one author holds 87% of recent knowledge",
"matches a sensitive path rule"]}
],
"flags": [
{"id": "ORPH-1", "severity": "critical",
"label": "services/auth/mfa.py is sensitive and has no active main author",
"detail": "Wei Zhang holds 87% and is marked on-leave", "path": "services/auth/mfa.py"}
]
}
}
JSON
FACTS = {
"method": {"knowledge_score": "sum of 0.5 ^ (age_days / 365) * (1 + log10(1 + lines))",
"bus_factor": "fewest authors whose shares reach 0.5"},
"totals": {"commits": 148, "authors": 6, "files": 17, "repoBusFactor": 3,
"firstDate": "2023-03-07", "lastDate": "2026-08-01", "bots": 1, "botCommits": 23},
"authors": [
{"name": "Wei Zhang", "commits": 21, "share": 0.178, "status": "on-leave",
"last_commit_days_ago": 109},
{"name": "Dmitri Sokolov", "commits": 28, "share": 0.261, "status": "active",
"last_commit_days_ago": 49},
],
"top_risk_files": [{
"path": "services/auth/mfa.py", "risk": 97, "band": "critical", "bus_factor": 1,
"primary_owner": "Wei Zhang", "primary_share": 0.866, "primary_status": "on-leave",
"sensitive": True, "generated": False, "human_commits": 11,
"top_contributors": [{"name": "Wei Zhang", "share": 0.866, "status": "on-leave"},
{"name": "Sofia Marchetti", "share": 0.134, "status": "active"}],
"declared_owners": ["@platform-security"], "codeowners_rule": "/services/auth/",
"why": ["bus factor 1", "matches a sensitive path rule"],
}],
"flags": [{"id": "ORPH-1", "severity": "critical",
"label": "services/auth/mfa.py is sensitive and has no active main author",
"detail": "Wei Zhang holds 87% and is marked on-leave",
"path": "services/auth/mfa.py"}],
}
payload = {"task": "risk", "repo_label": "atlas-pay",
"notes": "Wei starts a three-month leave in four weeks.", "facts": FACTS}
# The split lane adds the raw paste as well:
# payload = {**payload, "task": "split", "changed_files": "services/auth/mfa.py\nweb/src/cart.tsx"}
const FACTS = {
method: { knowledge_score: "sum of 0.5 ^ (age_days / 365) * (1 + log10(1 + lines))",
bus_factor: "fewest authors whose shares reach 0.5" },
totals: { commits: 148, authors: 6, files: 17, repoBusFactor: 3, bots: 1, botCommits: 23,
firstDate: "2023-03-07", lastDate: "2026-08-01" },
authors: [
{ name: "Wei Zhang", commits: 21, share: 0.178, status: "on-leave", last_commit_days_ago: 109 },
{ name: "Dmitri Sokolov", commits: 28, share: 0.261, status: "active", last_commit_days_ago: 49 }
],
top_risk_files: [{
path: "services/auth/mfa.py", risk: 97, band: "critical", bus_factor: 1,
primary_owner: "Wei Zhang", primary_share: 0.866, primary_status: "on-leave",
sensitive: true, generated: false, human_commits: 11,
top_contributors: [{ name: "Wei Zhang", share: 0.866, status: "on-leave" },
{ name: "Sofia Marchetti", share: 0.134, status: "active" }],
declared_owners: ["@platform-security"], codeowners_rule: "/services/auth/",
why: ["bus factor 1", "matches a sensitive path rule"]
}],
flags: [{ id: "ORPH-1", severity: "critical",
label: "services/auth/mfa.py is sensitive and has no active main author",
detail: "Wei Zhang holds 87% and is marked on-leave", path: "services/auth/mfa.py" }]
};
const payload = { task: "risk", repo_label: "atlas-pay",
notes: "Wei starts a three-month leave in four weeks.", facts: FACTS };
payload := map[string]any{
"task": "risk",
"repo_label": "atlas-pay",
"notes": "Wei starts a three-month leave in four weeks.",
"facts": map[string]any{
"totals": map[string]any{"commits": 148, "authors": 6, "files": 17,
"repoBusFactor": 3, "bots": 1, "botCommits": 23},
"authors": []map[string]any{
{"name": "Wei Zhang", "commits": 21, "share": 0.178, "status": "on-leave"},
{"name": "Dmitri Sokolov", "commits": 28, "share": 0.261, "status": "active"},
},
"top_risk_files": []map[string]any{{
"path": "services/auth/mfa.py", "risk": 97, "band": "critical",
"bus_factor": 1, "primary_owner": "Wei Zhang", "primary_share": 0.866,
"primary_status": "on-leave", "sensitive": true, "human_commits": 11,
"declared_owners": []string{"@platform-security"},
"why": []string{"bus factor 1", "matches a sensitive path rule"},
}},
"flags": []map[string]any{{"id": "ORPH-1", "severity": "critical",
"label": "services/auth/mfa.py is sensitive and has no active main author",
"path": "services/auth/mfa.py"}},
},
}
// Build the same object with your JSON library. The important parts:
// task "risk" | "handover" | "split"
// repo_label free text, may be empty
// notes free text, may be empty
// changed_files split lane only, the raw change-set paste
// facts the measured aggregate - totals, authors, top_risk_files, flags, ...
String payload = """
{"task":"risk","repo_label":"atlas-pay",
"notes":"Wei starts a three-month leave in four weeks.",
"facts":{"totals":{"commits":148,"authors":6,"files":17,"repoBusFactor":3},
"authors":[{"name":"Wei Zhang","commits":21,"share":0.178,"status":"on-leave"}],
"top_risk_files":[{"path":"services/auth/mfa.py","risk":97,"band":"critical",
"bus_factor":1,"primary_owner":"Wei Zhang","primary_share":0.866,
"primary_status":"on-leave","sensitive":true,"human_commits":11,
"declared_owners":["@platform-security"],"why":["bus factor 1"]}],
"flags":[{"id":"ORPH-1","severity":"critical",
"label":"services/auth/mfa.py is sensitive and has no active main author",
"path":"services/auth/mfa.py"}]}}
""";
FACTS = {
"totals" => { "commits" => 148, "authors" => 6, "files" => 17, "repoBusFactor" => 3,
"bots" => 1, "botCommits" => 23 },
"authors" => [
{ "name" => "Wei Zhang", "commits" => 21, "share" => 0.178, "status" => "on-leave" },
{ "name" => "Dmitri Sokolov", "commits" => 28, "share" => 0.261, "status" => "active" }
],
"top_risk_files" => [{
"path" => "services/auth/mfa.py", "risk" => 97, "band" => "critical", "bus_factor" => 1,
"primary_owner" => "Wei Zhang", "primary_share" => 0.866, "primary_status" => "on-leave",
"sensitive" => true, "human_commits" => 11, "declared_owners" => ["@platform-security"],
"why" => ["bus factor 1", "matches a sensitive path rule"]
}],
"flags" => [{ "id" => "ORPH-1", "severity" => "critical",
"label" => "services/auth/mfa.py is sensitive and has no active main author",
"path" => "services/auth/mfa.py" }]
}
payload = { "task" => "risk", "repo_label" => "atlas-pay",
"notes" => "Wei starts a three-month leave in four weeks.", "facts" => FACTS }
$facts = [
"totals" => ["commits" => 148, "authors" => 6, "files" => 17, "repoBusFactor" => 3,
"bots" => 1, "botCommits" => 23],
"authors" => [
["name" => "Wei Zhang", "commits" => 21, "share" => 0.178, "status" => "on-leave"],
["name" => "Dmitri Sokolov", "commits" => 28, "share" => 0.261, "status" => "active"],
],
"top_risk_files" => [[
"path" => "services/auth/mfa.py", "risk" => 97, "band" => "critical", "bus_factor" => 1,
"primary_owner" => "Wei Zhang", "primary_share" => 0.866, "primary_status" => "on-leave",
"sensitive" => true, "human_commits" => 11, "declared_owners" => ["@platform-security"],
"why" => ["bus factor 1", "matches a sensitive path rule"],
]],
"flags" => [[
"id" => "ORPH-1", "severity" => "critical",
"label" => "services/auth/mfa.py is sensitive and has no active main author",
"path" => "services/auth/mfa.py",
]],
];
$payload = ["task" => "risk", "repo_label" => "atlas-pay",
"notes" => "Wei starts a three-month leave in four weeks.", "facts" => $facts];
var facts = new {
totals = new { commits = 148, authors = 6, files = 17, repoBusFactor = 3,
bots = 1, botCommits = 23 },
authors = new object[] {
new { name = "Wei Zhang", commits = 21, share = 0.178, status = "on-leave" },
new { name = "Dmitri Sokolov", commits = 28, share = 0.261, status = "active" }
},
top_risk_files = new object[] {
new { path = "services/auth/mfa.py", risk = 97, band = "critical", bus_factor = 1,
primary_owner = "Wei Zhang", primary_share = 0.866, primary_status = "on-leave",
sensitive = true, human_commits = 11,
declared_owners = new[] { "@platform-security" },
why = new[] { "bus factor 1", "matches a sensitive path rule" } }
},
flags = new object[] {
new { id = "ORPH-1", severity = "critical",
label = "services/auth/mfa.py is sensitive and has no active main author",
path = "services/auth/mfa.py" }
}
};
var payload = new { task = "risk", repo_label = "atlas-pay",
notes = "Wei starts a three-month leave in four weeks.", facts };
4. Price it - free, and the only proof the wiring is right
POST /estimate takes the same body as a run, creates no job and charges nothing. Use it
for three separate things:
- The hold.
hold_creditsprices the full output cap;min_creditsis the floor below which the run will not start at all. - The model binding. Assert
model_aliasisgpt-terra,modelis the concrete model that alias currently resolves to, andmarkup_bpsis1000. - The payload shape. Estimate every lane. A lane whose estimate errors has a malformed input; a lane whose hold is implausibly small is the wrapped-body mistake from step 3.
call /estimate "$(cat payload.json)"
# {"ok":true,"data":{"model":"gpt-5.6-terra","model_alias":"gpt-terra","markup_bps":1000,
# "hold_credits":6042,"min_credits":907,"sponsor_enabled":false}}
# every lane, since the hold differs per lane
for lane in risk handover split; do
python3 - "$lane" <<'PY' > lane.json
import json, sys
p = json.load(open("payload.json"))
p["task"] = sys.argv[1]
if sys.argv[1] == "split":
p["changed_files"] = "services/auth/mfa.py\nweb/src/cart.tsx"
json.dump(p, sys.stdout)
PY
printf '%s ' "$lane"
call /estimate "$(cat lane.json)" | python3 -c 'import json,sys;print(json.load(sys.stdin)["data"]["hold_credits"])'
done
est = post("/estimate", payload, TOKEN)
assert est["model_alias"] == "gpt-terra", est["model_alias"]
assert est["markup_bps"] == 1000, est["markup_bps"]
print(est["model"], est["hold_credits"], est["min_credits"])
for lane in ("risk", "handover", "split"):
body = dict(payload, task=lane)
if lane == "split":
body["changed_files"] = "services/auth/mfa.py\nweb/src/cart.tsx"
print(lane, post("/estimate", body, TOKEN)["hold_credits"])
const est = await post("/estimate", payload, TOKEN);
if (est.model_alias !== "gpt-terra") throw new Error("unexpected model alias " + est.model_alias);
if (est.markup_bps !== 1000) throw new Error("unexpected markup " + est.markup_bps);
console.log(est.model, est.hold_credits, est.min_credits);
for (const task of ["risk", "handover", "split"]) {
const body = { ...payload, task };
if (task === "split") body.changed_files = "services/auth/mfa.py\nweb/src/cart.tsx";
const perLane = await post("/estimate", body, TOKEN);
console.log(task, perLane.hold_credits);
}
raw, err = post("/estimate", token, payload, nil)
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(raw, &est)
if est.ModelAlias != "gpt-terra" || est.MarkupBps != 1000 {
panic("unexpected model binding")
}
fmt.Println(est.Model, est.Hold, est.Min)
for _, lane := range []string{"risk", "handover", "split"} {
payload["task"] = lane
if lane == "split" {
payload["changed_files"] = "services/auth/mfa.py\nweb/src/cart.tsx"
}
r, _ := post("/estimate", token, payload, nil)
fmt.Println(lane, string(r))
}
String est = post("/estimate", token, payload);
// assert data.model_alias == "gpt-terra" and data.markup_bps == 1000 before running anything
System.out.println(est);
for (String lane : new String[] {"risk", "handover", "split"}) {
String body = payload.replace("\"task\":\"risk\"", "\"task\":\"" + lane + "\"");
System.out.println(lane + " " + post("/estimate", token, body));
}
est = post("/estimate", payload, TOKEN)
raise "unexpected alias #{est['model_alias']}" unless est["model_alias"] == "gpt-terra"
raise "unexpected markup" unless est["markup_bps"] == 1000
puts "#{est['model']} #{est['hold_credits']} #{est['min_credits']}"
%w[risk handover split].each do |lane|
body = payload.merge("task" => lane)
body["changed_files"] = "services/auth/mfa.py\nweb/src/cart.tsx" if lane == "split"
puts "#{lane} #{post('/estimate', body, TOKEN)['hold_credits']}"
end
$est = post("/estimate", $payload, $token);
if ($est["model_alias"] !== "gpt-terra" || $est["markup_bps"] !== 1000) {
throw new RuntimeException("unexpected model binding");
}
echo $est["model"], " ", $est["hold_credits"], " ", $est["min_credits"], PHP_EOL;
foreach (["risk", "handover", "split"] as $lane) {
$body = array_merge($payload, ["task" => $lane]);
if ($lane === "split") $body["changed_files"] = "services/auth/mfa.py\nweb/src/cart.tsx";
echo $lane, " ", post("/estimate", $body, $token)["hold_credits"], PHP_EOL;
}
var est = await PostAsync("/estimate", payload, token);
if (est.GetProperty("model_alias").GetString() != "gpt-terra"
|| est.GetProperty("markup_bps").GetInt32() != 1000) {
throw new Exception("unexpected model binding");
}
Console.WriteLine($"{est.GetProperty("model")} {est.GetProperty("hold_credits")}");
foreach (var lane in new[] { "risk", "handover", "split" }) {
var body = new { task = lane, repo_label = "atlas-pay", notes = "", facts,
changed_files = lane == "split" ? "services/auth/mfa.py" : null };
var perLane = await PostAsync("/estimate", body, token);
Console.WriteLine($"{lane} {perLane.GetProperty("hold_credits")}");
}
5. Run it, and poll the job
POST /run returns a job_id immediately; poll GET /jobs/{id}
until status is terminal (succeeded or failed). Always send an
Idempotency-Key: a hash over the payload including the task, plus an
attempt counter. A retry of the same attempt is then free of the risk of double billing, while a
deliberate second attempt uses a new key.
KEY="owner-map:risk:$(python3 -c 'import hashlib,sys;print(hashlib.sha256(open("payload.json","rb").read()).hexdigest()[:16])'):a1"
JOB=$(curl -sS -X POST "$BASE/run" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-H "Idempotency-Key: $KEY" --data-binary @payload.json \
| python3 -c 'import json,sys;print(json.load(sys.stdin)["data"]["job_id"])')
until [ "$(curl -sS "$BASE/jobs/$JOB" -H "Authorization: Bearer $TOKEN" \
| python3 -c 'import json,sys;print(json.load(sys.stdin)["data"]["status"])')" != "running" ]; do
sleep 2
done
curl -sS "$BASE/jobs/$JOB" -H "Authorization: Bearer $TOKEN" > job.json
import hashlib, time
def idem_key(p, attempt=1):
blob = json.dumps(p, sort_keys=True).encode()
return "owner-map:%s:%s:a%d" % (p["task"], hashlib.sha256(blob).hexdigest()[:16], attempt)
job = post("/run", payload, TOKEN, {"Idempotency-Key": idem_key(payload)})
job_id = job["job_id"]
while True:
j = get("/jobs/" + job_id, TOKEN)
if j["status"] != "running":
break
time.sleep(2)
print(j["status"], j.get("charged_credits"), j.get("truncated"))
reply = json.loads(j["output"]["output"]) # the model's own JSON
import { createHash } from "node:crypto";
function idemKey(p, attempt = 1) {
const blob = JSON.stringify(p);
const hash = createHash("sha256").update(blob).digest("hex").slice(0, 16);
return `owner-map:${p.task}:${hash}:a${attempt}`;
}
const job = await post("/run", payload, TOKEN, { "Idempotency-Key": idemKey(payload) });
let j;
do {
await new Promise((r) => setTimeout(r, 2000));
j = await get(`/jobs/${job.job_id}`, TOKEN);
} while (j.status === "running");
console.log(j.status, j.charged_credits, j.truncated);
const reply = JSON.parse(j.output.output);
blob, _ := json.Marshal(payload)
sum := sha256.Sum256(blob)
key := fmt.Sprintf("owner-map:%s:%x:a1", payload["task"], sum[:8])
raw, err = post("/run", token, payload, map[string]string{"Idempotency-Key": key})
if err != nil {
panic(err)
}
var started struct{ JobID string `json:"job_id"` }
json.Unmarshal(raw, &started)
var job struct {
Status string `json:"status"`
Charged int64 `json:"charged_credits"`
Output struct{ Output string `json:"output"` } `json:"output"`
}
for {
r, err := get("/jobs/"+started.JobID, token)
if err != nil {
panic(err)
}
json.Unmarshal(r, &job)
if job.Status != "running" {
break
}
time.Sleep(2 * time.Second)
}
fmt.Println(job.Status, job.Charged)
String key = "owner-map:risk:" + Integer.toHexString(payload.hashCode()) + ":a1";
HttpRequest runReq = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/run"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + token)
.header("Idempotency-Key", key)
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
String started = HTTP.send(runReq, HttpResponse.BodyHandlers.ofString()).body();
// pull data.job_id, then poll GET /jobs/{id} every two seconds until status != "running"
require "digest"
def idem_key(payload, attempt = 1)
hash = Digest::SHA256.hexdigest(JSON.dump(payload))[0, 16]
"owner-map:#{payload['task']}:#{hash}:a#{attempt}"
end
job = post("/run", payload, TOKEN, { "Idempotency-Key" => idem_key(payload) })
loop do
@j = get("/jobs/#{job['job_id']}", TOKEN)
break if @j["status"] != "running"
sleep 2
end
puts "#{@j['status']} #{@j['charged_credits']}"
reply = JSON.parse(@j["output"]["output"])
$key = "owner-map:risk:" . substr(hash("sha256", json_encode($payload)), 0, 16) . ":a1";
$job = post("/run", $payload, $token, ["Idempotency-Key" => $key]);
do {
sleep(2);
$j = get_("/jobs/" . $job["job_id"], $token);
} while ($j["status"] === "running");
echo $j["status"], " ", $j["charged_credits"] ?? 0, PHP_EOL;
$reply = json_decode($j["output"]["output"], true);
using System.Security.Cryptography;
using System.Text;
var blob = JsonSerializer.Serialize(payload);
var hash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(blob)))[..16].ToLower();
var key = $"owner-map:risk:{hash}:a1";
var started = await PostAsync("/run", payload, token, ("Idempotency-Key", key));
var jobId = started.GetProperty("job_id").GetString()!;
JsonElement job;
do {
await Task.Delay(2000);
job = await GetAsync("/jobs/" + jobId, token);
} while (job.GetProperty("status").GetString() == "running");
var reply = JsonDocument.Parse(job.GetProperty("output").GetProperty("output").GetString()!);
6. Stream it instead
POST /run-stream is the same body over Server-Sent Events. Frames arrive as
event: delta with a {"text": "..."} payload, then one
event: job with the terminal job, or event: error. The final job frame is
authoritative: deltas can drop the tail, so parse the reply from the job, not from the accumulated
stream. The page uses the deltas only to advance its progress stages.
curl -sS -N -X POST "$BASE/run-stream" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -H "Idempotency-Key: $KEY" -H 'Accept: text/event-stream' \ --data-binary @payload.json
req = urllib.request.Request(BASE + "/run-stream",
data=json.dumps(payload).encode(), method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", "Bearer " + TOKEN)
req.add_header("Accept", "text/event-stream")
req.add_header("Idempotency-Key", idem_key(payload))
event, acc, final = None, [], None
with urllib.request.urlopen(req) as r:
for line in r:
line = line.decode().rstrip("\n")
if line.startswith("event: "):
event = line[7:]
elif line.startswith("data: "):
frame = json.loads(line[6:])
if event == "delta":
acc.append(frame.get("text", ""))
elif event == "job":
final = frame
elif event == "error":
raise RuntimeError(frame.get("code", "stream error"))
reply = json.loads(final["output"]["output"]) # trust the job frame, not "".join(acc)
const res = await fetch(`${BASE}/run-stream`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${TOKEN}`,
Accept: "text/event-stream",
"Idempotency-Key": idemKey(payload)
},
body: JSON.stringify(payload)
});
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = "", event = null, final = null;
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
const lines = buf.split("\n");
buf = lines.pop();
for (const line of lines) {
if (line.startsWith("event: ")) event = line.slice(7).trim();
else if (line.startsWith("data: ")) {
const frame = JSON.parse(line.slice(6));
if (event === "job") final = frame;
else if (event === "error") throw new Error(frame.code || "stream error");
}
}
}
const reply = JSON.parse(final.output.output);
body, _ := json.Marshal(payload)
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("Accept", "text/event-stream")
req.Header.Set("Idempotency-Key", key)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
sc := bufio.NewScanner(res.Body)
sc.Buffer(make([]byte, 1024*1024), 8*1024*1024)
var event string
var final json.RawMessage
for sc.Scan() {
line := sc.Text()
switch {
case strings.HasPrefix(line, "event: "):
event = strings.TrimSpace(line[7:])
case strings.HasPrefix(line, "data: "):
if event == "job" {
final = json.RawMessage(line[6:])
} else if event == "error" {
panic(line[6:])
}
}
}
fmt.Println(len(final), "bytes of terminal job")
HttpRequest streamReq = HttpRequest.newBuilder()
.uri(URI.create(BASE + "/run-stream"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + token)
.header("Accept", "text/event-stream")
.header("Idempotency-Key", key)
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
HTTP.send(streamReq, HttpResponse.BodyHandlers.ofLines()).body().forEach(line -> {
if (line.startsWith("event: ")) System.out.println("-- " + line.substring(7));
else if (line.startsWith("data: ")) System.out.println(line.substring(6));
});
// keep the frame that followed "event: job"; that one is authoritative
uri = URI(BASE.to_s + "/run-stream")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req["Authorization"] = "Bearer #{TOKEN}"
req["Accept"] = "text/event-stream"
req["Idempotency-Key"] = idem_key(payload)
req.body = JSON.dump(payload)
event = nil
final = 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[7..]
elsif line.start_with?("data: ")
final = JSON.parse(line[6..]) if event == "job"
raise line[6..] if event == "error"
end
end
end
end
end
reply = JSON.parse(final["output"]["output"])
$ch = curl_init(BASE . "/run-stream");
$event = null;
$final = null;
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json",
"Authorization: Bearer " . $token,
"Accept: text/event-stream",
"Idempotency-Key: " . $key],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_WRITEFUNCTION => function ($ch, $chunk) use (&$event, &$final) {
foreach (explode("\n", $chunk) as $line) {
if (str_starts_with($line, "event: ")) $event = trim(substr($line, 7));
elseif (str_starts_with($line, "data: ") && $event === "job")
$final = json_decode(substr($line, 6), true);
}
return strlen($chunk);
},
]);
curl_exec($ch);
curl_close($ch);
$reply = json_decode($final["output"]["output"], true);
var streamReq = new HttpRequestMessage(HttpMethod.Post, Base + "/run-stream") {
Content = JsonContent.Create(payload)
};
streamReq.Headers.Add("Authorization", "Bearer " + token);
streamReq.Headers.Add("Accept", "text/event-stream");
streamReq.Headers.Add("Idempotency-Key", key);
using var res = await http.SendAsync(streamReq, HttpCompletionOption.ResponseHeadersRead);
using var reader = new StreamReader(await res.Content.ReadAsStreamAsync());
string? evt = null, final = null;
while (await reader.ReadLineAsync() is string line) {
if (line.StartsWith("event: ")) evt = line[7..].Trim();
else if (line.StartsWith("data: ") && evt == "job") final = line[6..];
}
// `final` is the terminal job; parse data.output.output out of it
7. Read the reply, and check it against your own flags
The model's answer is one JSON object, the same envelope for every lane. The per-lane part is
body:
risk-body.owner_notes,body.codeowners_actions; the artifact is a CODEOWNERS draft.handover-body.pairings,body.phases,body.at_risk_if_delayed; the artifact is a dated plan.split-body.prs,body.leftovers,body.merge_order; the artifact is the pull-request plan.
Two checks are worth writing into any client, because they are the two failures a reader will not
notice: every critical or high flag you sent must appear in
coverage_check, and in the split lane every path you sent must be placed in exactly one
pull request or explicitly listed in leftovers. The page performs both and displays the
difference rather than hiding it.
{"lane": "risk", "lane_inferred": false, "title": "atlas-pay: ownership and bus-factor review",
"posture": "critical", "verdict": "...", "summary": "...",
"headline_numbers": [{"label": "sensitive files with bus factor 1", "value": "7"}],
"findings": [{"id": "OM-1", "title": "...", "severity": "critical",
"path": "services/auth/mfa.py", "owner": "Wei Zhang",
"evidence": "...", "why": "...", "action": "...", "effort": "days"}],
"coverage_check": [{"flag_id": "ORPH-1", "status": "confirmed", "finding_id": "OM-1", "note": ""}],
"artifact": {"kind": "markdown", "filename": "CODEOWNERS.draft", "content": "..."},
"next_lane": {"lane": "handover", "reason": "..."},
"assumptions": ["..."], "open_questions": ["..."],
"body": {"owner_notes": [...], "codeowners_actions": [...]}}
python3 - <<'PY'
import json
job = json.load(open("job.json"))["data"]
reply = json.loads(job["output"]["output"])
payload = json.load(open("payload.json"))
print(reply["posture"], "-", reply["verdict"])
for f in reply["findings"]:
print(" ", f["id"], f["severity"], f["title"])
sent = {f["id"] for f in payload["facts"]["flags"]
if f["severity"] in ("critical", "high")}
seen = {c["flag_id"] for c in reply["coverage_check"]}
missing = sent - seen
print("unaccounted flags:", ", ".join(sorted(missing)) or "none")
PY
print(reply["posture"], "-", reply["verdict"])
for f in reply["findings"]:
print(" ", f["id"], f["severity"], f["title"], "|", f["action"])
sent = {f["id"] for f in payload["facts"]["flags"] if f["severity"] in ("critical", "high")}
missing = sent - {c["flag_id"] for c in reply["coverage_check"]}
if missing:
print("UNACCOUNTED:", sorted(missing)) # treat as unreviewed, not as cleared
if reply["lane"] == "split":
placed = {p for pr in reply["body"]["prs"] for p in pr["paths"]}
placed |= {l["path"] for l in reply["body"]["leftovers"]}
print("unplaced:", sorted(set(payload["changed_files"].split()) - placed))
console.log(reply.posture, "-", reply.verdict);
for (const f of reply.findings) console.log(" ", f.id, f.severity, f.title, "|", f.action);
const sent = new Set(payload.facts.flags
.filter((f) => f.severity === "critical" || f.severity === "high").map((f) => f.id));
const seen = new Set(reply.coverage_check.map((c) => c.flag_id));
const missing = [...sent].filter((id) => !seen.has(id));
if (missing.length) console.warn("UNACCOUNTED:", missing);
if (reply.lane === "split") {
const placed = new Set(reply.body.prs.flatMap((pr) => pr.paths)
.concat(reply.body.leftovers.map((l) => l.path)));
const unplaced = payload.changed_files.split("\n").filter((p) => p && !placed.has(p));
if (unplaced.length) console.warn("unplaced files:", unplaced);
}
var reply struct {
Lane string `json:"lane"`
Posture string `json:"posture"`
Verdict string `json:"verdict"`
Findings []struct {
ID string `json:"id"`
Severity string `json:"severity"`
Title string `json:"title"`
Action string `json:"action"`
} `json:"findings"`
Coverage []struct {
FlagID string `json:"flag_id"`
Status string `json:"status"`
} `json:"coverage_check"`
}
json.Unmarshal([]byte(job.Output.Output), &reply)
fmt.Println(reply.Posture, "-", reply.Verdict)
seen := map[string]bool{}
for _, c := range reply.Coverage {
seen[c.FlagID] = true
}
for _, f := range reply.Findings {
fmt.Println(" ", f.ID, f.Severity, f.Title)
}
// compare `seen` against the critical/high flag ids you put in facts.flags
// Parse data.output.output as JSON, then: // reply.posture healthy | watch | at-risk | critical // reply.findings[] id, title, severity, path, owner, evidence, why, action, effort // reply.coverage_check[] flag_id, status, finding_id, note // reply.body owner_notes / pairings / prs, depending on the lane // Then assert every critical and high flag id you sent appears among the // coverage_check flag_ids, and warn loudly if one does not. System.out.println(job);
puts "#{reply['posture']} - #{reply['verdict']}"
reply["findings"].each { |f| puts " #{f['id']} #{f['severity']} #{f['title']}" }
sent = payload["facts"]["flags"]
.select { |f| %w[critical high].include?(f["severity"]) }.map { |f| f["id"] }
missing = sent - reply["coverage_check"].map { |c| c["flag_id"] }
warn "UNACCOUNTED: #{missing.join(', ')}" unless missing.empty?
echo $reply["posture"], " - ", $reply["verdict"], PHP_EOL;
foreach ($reply["findings"] as $f) {
echo " ", $f["id"], " ", $f["severity"], " ", $f["title"], PHP_EOL;
}
$sent = array_map(fn($f) => $f["id"],
array_filter($payload["facts"]["flags"],
fn($f) => in_array($f["severity"], ["critical", "high"], true)));
$seen = array_map(fn($c) => $c["flag_id"], $reply["coverage_check"]);
$missing = array_diff($sent, $seen);
if ($missing) fwrite(STDERR, "UNACCOUNTED: " . implode(", ", $missing) . PHP_EOL);
var root = reply.RootElement;
Console.WriteLine($"{root.GetProperty("posture")} - {root.GetProperty("verdict")}");
foreach (var f in root.GetProperty("findings").EnumerateArray()) {
Console.WriteLine($" {f.GetProperty("id")} {f.GetProperty("severity")} {f.GetProperty("title")}");
}
var seen = root.GetProperty("coverage_check").EnumerateArray()
.Select(c => c.GetProperty("flag_id").GetString()).ToHashSet();
// compare against the critical/high flag ids you sent in facts.flags and warn on any gap
Rate limits, cost and idempotency in one paragraph
POST /estimate is free and creates no job. A run is metered: the hold prices the full
output cap and the charge is what the run actually used, usually far less. Send an
Idempotency-Key on every run - a content hash of the payload including the
task, plus an attempt counter - so a retried request cannot bill twice. Note that the
platform answers a replayed key with the original job even when the body differs, so a
deliberate second attempt needs a different key.
What the app itself sends
The browser app computes facts from your history export with the algorithm described
in llms.txt - a one-year half-life on each commit's weight, ownership
shares, bus factor, HHI, a sensitive-path match, a CODEOWNERS reality check and a bounded
low-discrepancy sample of commit subjects. It sends the aggregate and never the log. If you build
your own aggregate, you decide what to include; the reply's quality tracks what you send, and
anything absent from facts is something the model is forbidden to assert.
Owner Map is a derived work built on three published agent skills: @openai/security-ownership-map, @sickn33/git-pr-workflows-onboard and @nvidia/mcore-split-pr. Not affiliated with those skills' authors, nor with Git or GitHub.