Driving Company Analysis from your own code
Company Analysis explains public companies: how a business earns, what its segments are, the forces acting on it, how its industry works, and what the bull and the bear arguments each rest on. Everything the web app does is available over HTTP.
What this API will not return
The boundary is enforced server-side by the app's system prompt and client-side by the
bundle, and it is not a parameter you can turn off. No buy, sell or hold call. No price target.
No return forecast. No verdict that anything is cheap or expensive. And no market-fixed figure -
no share price, market capitalisation, valuation multiple, latest quarter, analyst consensus,
relative pricing claim, dividend yield or ownership flow. Those are converted into the
look_it_up array, which explains what each figure measures and names the document
that holds it. If you need live market data, this is the wrong endpoint.
Base URL and envelope
Every endpoint lives under https://api.skillsafe.ai/v1/app-api and returns the
same envelope: {"ok":true,"data":{...}} on success, or
{"ok":false,"error":{"code":"...","message":"..."}} on failure. Check
error before reading data.
| Code | Meaning | What to do |
|---|---|---|
UNAUTHORIZED | Missing or expired token | Get a fresh one (step 1) |
PAYMENT_REQUIRED | Balance below min_credits | Top up, or shorten the input |
VALIDATION_ERROR | The input did not match the contract | Check task, register, depth and boundary |
RATE_LIMITED | Too many requests | Back off; do not tight-loop |
NOT_FOUND | Unknown job or record id | Check the id you stored |
INTERNAL | Platform fault | Retry once with the same idempotency key |
The input contract
Every run takes one JSON object. task is always "explain" - this app
has a single lane, and the field exists so a future one cannot silently change what your
integration receives.
| Field | Type | Notes |
|---|---|---|
task | string | Required. Always "explain". |
company | string | Required. A name or a ticker. |
focus | string | Optional. What you want to understand. Steers emphasis; licenses nothing. |
context | string | Optional pasted source material. Preferred over memory where they disagree. |
register | string | Required. plain | industry | filings | historical. |
depth | string | Required. orientation | standard | deep. |
as_of_year | number | The year you believe it is; used for staleness reasoning. |
boundary | object | Required, and boundary.not_advice must be true. See below. |
boundary carries refused_acts, convert_not_state and
pasted_text_contains - arrays of act ids the client detected in the user's own
words. Each id in convert_not_state is a promise that the figure will appear in
look_it_up. Send empty arrays if you are not running the client-side guard; the
boundary itself does not depend on them.
1 Get a token
A guest token is issued per app and needs no account. It can browse and it can price a run, but a metered run needs a signed-in user's wallet. For a person, the friendlier route is the token page, which does the SSO round trip and hands you a token you can paste into a shell.
curl -sS -X POST "https://api.skillsafe.ai/v1/app-api/guest" \
-H "Content-Type: application/json" \
-d '{
"slug": "company-analysis"
}'
import json, urllib.request
TOKEN = "YOUR_TOKEN" # or read it from your own secret store
url = "https://api.skillsafe.ai/v1/app-api/guest"
body = json.dumps({
"slug": "company-analysis"
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
with urllib.request.urlopen(req) as r:
print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";
const res = await fetch("https://api.skillsafe.ai/v1/app-api/guest", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"slug": "company-analysis"
}),
});
const { data, error } = await res.json();
if (error) throw new Error(error.code + ": " + error.message);
console.log(data);
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
token := os.Getenv("SKILLSAFE_TOKEN")
body := []byte(`{"slug": "company-analysis"}`)
req, _ := http.NewRequest("POST", "https://api.skillsafe.ai/v1/app-api/guest", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;
public class Main {
public static void main(String[] a) throws Exception {
String token = System.getenv("SKILLSAFE_TOKEN");
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create("https://api.skillsafe.ai/v1/app-api/guest"))
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(
"""
{"slug": "company-analysis"}
"""));
HttpResponse<String> r = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(r.body());
}
}
require "json"
require "net/http"
require "uri"
token = ENV.fetch("SKILLSAFE_TOKEN", "YOUR_TOKEN")
uri = URI("https://api.skillsafe.ai/v1/app-api/guest")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req.body = {
"slug" => "company-analysis"
}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = getenv("SKILLSAFE_TOKEN") ?: "YOUR_TOKEN";
$headers = [
"Content-Type: application/json",
];
$ch = curl_init("https://api.skillsafe.ai/v1/app-api/guest");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<'JSON'
{"slug": "company-analysis"}
JSON);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($out["data"]);
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var token = Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN") ?? "YOUR_TOKEN";
var http = new HttpClient();
var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.skillsafe.ai/v1/app-api/guest");
req.Content = new StringContent(@"{""slug"": ""company-analysis""}",
Encoding.UTF8, "application/json");
var res = await http.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());
2 Check what the token is
Confirms the token is live and tells you whose it is. The response carries exactly three
fields - subject_type, subject_id and credits. There is no
authenticated flag and no profile object, so the only correct test for a signed-in
user is subject_type === "user". A guest reads "guest".
curl -sS -X GET "https://api.skillsafe.ai/v1/app-api/me" \
-H "Authorization: Bearer $TOKEN"
import json, urllib.request
TOKEN = "YOUR_TOKEN" # or read it from your own secret store
url = "https://api.skillsafe.ai/v1/app-api/me"
req = urllib.request.Request(url, method="GET")
req.add_header("Authorization", "Bearer " + TOKEN)
with urllib.request.urlopen(req) as r:
print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";
const res = await fetch("https://api.skillsafe.ai/v1/app-api/me", {
method: "GET",
headers: {
"Authorization": `Bearer ${TOKEN}`,
},
});
const { data, error } = await res.json();
if (error) throw new Error(error.code + ": " + error.message);
console.log(data);
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
token := os.Getenv("SKILLSAFE_TOKEN")
req, _ := http.NewRequest("GET", "https://api.skillsafe.ai/v1/app-api/me", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;
public class Main {
public static void main(String[] a) throws Exception {
String token = System.getenv("SKILLSAFE_TOKEN");
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create("https://api.skillsafe.ai/v1/app-api/me"))
.header("Authorization", "Bearer " + token)
.method("GET", HttpRequest.BodyPublishers.noBody());
HttpResponse<String> r = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(r.body());
}
}
require "json"
require "net/http"
require "uri"
token = ENV.fetch("SKILLSAFE_TOKEN", "YOUR_TOKEN")
uri = URI("https://api.skillsafe.ai/v1/app-api/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.parse(res.body)["data"]
<?php
$token = getenv("SKILLSAFE_TOKEN") ?: "YOUR_TOKEN";
$headers = [
"Authorization: Bearer " . $token,
];
$ch = curl_init("https://api.skillsafe.ai/v1/app-api/me");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($out["data"]);
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var token = Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN") ?? "YOUR_TOKEN";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var req = new HttpRequestMessage(new HttpMethod("GET"), "https://api.skillsafe.ai/v1/app-api/me");
var res = await http.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());
3 Price the run before you make it
Free, and it creates no job. It returns hold_credits - what gets reserved -
plus min_credits, model and markup_bps. Show the hold as
reserved, never as the price: you are charged what the run actually uses, which is
usually far less, because the hold prices the full output cap.
One trap worth knowing. This endpoint posts your argument
as the request body and does not validate its shape. A bare string, null, an empty
array and 42 all return ok:true with a correct model binding and the
same hold. A clean estimate therefore proves the model wiring and proves nothing whatsoever
about your input. Validate the object yourself before you spend - the web app runs a
mustBeObject() check immediately before this call and again before the run.
curl -sS -X POST "https://api.skillsafe.ai/v1/app-api/estimate" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task": "explain",
"company": "Costco Wholesale",
"focus": "Why does the membership fee matter more than the retail margin?",
"context": "",
"register": "plain",
"depth": "standard",
"as_of_year": 2026,
"boundary": {
"not_advice": true,
"refused_acts": [],
"convert_not_state": [
"market_value"
],
"pasted_text_contains": []
}
}'
import json, urllib.request
TOKEN = "YOUR_TOKEN" # or read it from your own secret store
url = "https://api.skillsafe.ai/v1/app-api/estimate"
body = json.dumps({
"task": "explain",
"company": "Costco Wholesale",
"focus": "Why does the membership fee matter more than the retail margin?",
"context": "",
"register": "plain",
"depth": "standard",
"as_of_year": 2026,
"boundary": {
"not_advice": true,
"refused_acts": [],
"convert_not_state": [
"market_value"
],
"pasted_text_contains": []
}
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", "Bearer " + TOKEN)
with urllib.request.urlopen(req) as r:
print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";
const res = await fetch("https://api.skillsafe.ai/v1/app-api/estimate", {
method: "POST",
headers: {
"Authorization": `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"task": "explain",
"company": "Costco Wholesale",
"focus": "Why does the membership fee matter more than the retail margin?",
"context": "",
"register": "plain",
"depth": "standard",
"as_of_year": 2026,
"boundary": {
"not_advice": true,
"refused_acts": [],
"convert_not_state": [
"market_value"
],
"pasted_text_contains": []
}
}),
});
const { data, error } = await res.json();
if (error) throw new Error(error.code + ": " + error.message);
console.log(data);
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
token := os.Getenv("SKILLSAFE_TOKEN")
body := []byte(`{"task": "explain", "company": "Costco Wholesale", "focus": "Why does the membership fee matter more than the retail margin?", "context": "", "register": "plain", "depth": "standard", "as_of_year": 2026, "boundary": {"not_advice": true, "refused_acts": [], "convert_not_state": ["market_value"], "pasted_text_contains": []}}`)
req, _ := http.NewRequest("POST", "https://api.skillsafe.ai/v1/app-api/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()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;
public class Main {
public static void main(String[] a) throws Exception {
String token = System.getenv("SKILLSAFE_TOKEN");
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create("https://api.skillsafe.ai/v1/app-api/estimate"))
.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(
"""
{"task": "explain", "company": "Costco Wholesale", "focus": "Why does the membership fee matter more than the retail margin?", "context": "", "register": "plain", "depth": "standard", "as_of_year": 2026, "boundary": {"not_advice": true, "refused_acts": [], "convert_not_state": ["market_value"], "pasted_text_contains": []}}
"""));
HttpResponse<String> r = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(r.body());
}
}
require "json"
require "net/http"
require "uri"
token = ENV.fetch("SKILLSAFE_TOKEN", "YOUR_TOKEN")
uri = URI("https://api.skillsafe.ai/v1/app-api/estimate")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{token}"
req["Content-Type"] = "application/json"
req.body = {
"task" => "explain",
"company" => "Costco Wholesale",
"focus" => "Why does the membership fee matter more than the retail margin?",
"context" => "",
"register" => "plain",
"depth" => "standard",
"as_of_year" => 2026,
"boundary" => {
"not_advice" => true,
"refused_acts" => [],
"convert_not_state" => [
"market_value"
],
"pasted_text_contains" => []
}
}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = getenv("SKILLSAFE_TOKEN") ?: "YOUR_TOKEN";
$headers = [
"Authorization: Bearer " . $token,
"Content-Type: application/json",
];
$ch = curl_init("https://api.skillsafe.ai/v1/app-api/estimate");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<'JSON'
{"task": "explain", "company": "Costco Wholesale", "focus": "Why does the membership fee matter more than the retail margin?", "context": "", "register": "plain", "depth": "standard", "as_of_year": 2026, "boundary": {"not_advice": true, "refused_acts": [], "convert_not_state": ["market_value"], "pasted_text_contains": []}}
JSON);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($out["data"]);
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var token = Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN") ?? "YOUR_TOKEN";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.skillsafe.ai/v1/app-api/estimate");
req.Content = new StringContent(@"{""task"": ""explain"", ""company"": ""Costco Wholesale"", ""focus"": ""Why does the membership fee matter more than the retail margin?"", ""context"": """", ""register"": ""plain"", ""depth"": ""standard"", ""as_of_year"": 2026, ""boundary"": {""not_advice"": true, ""refused_acts"": [], ""convert_not_state"": [""market_value""], ""pasted_text_contains"": []}}",
Encoding.UTF8, "application/json");
var res = await http.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());
4 Run it
Returns a job. Poll it, or use the streaming endpoint in step 6. Always send
Idempotency-Key: a retried request with the same key returns the original job
rather than billing a second time.
curl -sS -X POST "https://api.skillsafe.ai/v1/app-api/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task": "explain",
"company": "Costco Wholesale",
"focus": "Why does the membership fee matter more than the retail margin?",
"context": "",
"register": "plain",
"depth": "standard",
"as_of_year": 2026,
"boundary": {
"not_advice": true,
"refused_acts": [],
"convert_not_state": [
"market_value"
],
"pasted_text_contains": []
}
}'
import json, urllib.request
TOKEN = "YOUR_TOKEN" # or read it from your own secret store
url = "https://api.skillsafe.ai/v1/app-api/run"
body = json.dumps({
"task": "explain",
"company": "Costco Wholesale",
"focus": "Why does the membership fee matter more than the retail margin?",
"context": "",
"register": "plain",
"depth": "standard",
"as_of_year": 2026,
"boundary": {
"not_advice": true,
"refused_acts": [],
"convert_not_state": [
"market_value"
],
"pasted_text_contains": []
}
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", "Bearer " + TOKEN)
with urllib.request.urlopen(req) as r:
print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";
const res = await fetch("https://api.skillsafe.ai/v1/app-api/run", {
method: "POST",
headers: {
"Authorization": `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"task": "explain",
"company": "Costco Wholesale",
"focus": "Why does the membership fee matter more than the retail margin?",
"context": "",
"register": "plain",
"depth": "standard",
"as_of_year": 2026,
"boundary": {
"not_advice": true,
"refused_acts": [],
"convert_not_state": [
"market_value"
],
"pasted_text_contains": []
}
}),
});
const { data, error } = await res.json();
if (error) throw new Error(error.code + ": " + error.message);
console.log(data);
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
token := os.Getenv("SKILLSAFE_TOKEN")
body := []byte(`{"task": "explain", "company": "Costco Wholesale", "focus": "Why does the membership fee matter more than the retail margin?", "context": "", "register": "plain", "depth": "standard", "as_of_year": 2026, "boundary": {"not_advice": true, "refused_acts": [], "convert_not_state": ["market_value"], "pasted_text_contains": []}}`)
req, _ := http.NewRequest("POST", "https://api.skillsafe.ai/v1/app-api/run", 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()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;
public class Main {
public static void main(String[] a) throws Exception {
String token = System.getenv("SKILLSAFE_TOKEN");
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create("https://api.skillsafe.ai/v1/app-api/run"))
.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(
"""
{"task": "explain", "company": "Costco Wholesale", "focus": "Why does the membership fee matter more than the retail margin?", "context": "", "register": "plain", "depth": "standard", "as_of_year": 2026, "boundary": {"not_advice": true, "refused_acts": [], "convert_not_state": ["market_value"], "pasted_text_contains": []}}
"""));
HttpResponse<String> r = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(r.body());
}
}
require "json"
require "net/http"
require "uri"
token = ENV.fetch("SKILLSAFE_TOKEN", "YOUR_TOKEN")
uri = URI("https://api.skillsafe.ai/v1/app-api/run")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{token}"
req["Content-Type"] = "application/json"
req.body = {
"task" => "explain",
"company" => "Costco Wholesale",
"focus" => "Why does the membership fee matter more than the retail margin?",
"context" => "",
"register" => "plain",
"depth" => "standard",
"as_of_year" => 2026,
"boundary" => {
"not_advice" => true,
"refused_acts" => [],
"convert_not_state" => [
"market_value"
],
"pasted_text_contains" => []
}
}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = getenv("SKILLSAFE_TOKEN") ?: "YOUR_TOKEN";
$headers = [
"Authorization: Bearer " . $token,
"Content-Type: application/json",
];
$ch = curl_init("https://api.skillsafe.ai/v1/app-api/run");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<'JSON'
{"task": "explain", "company": "Costco Wholesale", "focus": "Why does the membership fee matter more than the retail margin?", "context": "", "register": "plain", "depth": "standard", "as_of_year": 2026, "boundary": {"not_advice": true, "refused_acts": [], "convert_not_state": ["market_value"], "pasted_text_contains": []}}
JSON);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($out["data"]);
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var token = Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN") ?? "YOUR_TOKEN";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.skillsafe.ai/v1/app-api/run");
req.Content = new StringContent(@"{""task"": ""explain"", ""company"": ""Costco Wholesale"", ""focus"": ""Why does the membership fee matter more than the retail margin?"", ""context"": """", ""register"": ""plain"", ""depth"": ""standard"", ""as_of_year"": 2026, ""boundary"": {""not_advice"": true, ""refused_acts"": [], ""convert_not_state"": [""market_value""], ""pasted_text_contains"": []}}",
Encoding.UTF8, "application/json");
var res = await http.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());
5 Poll the job
Poll until status is succeeded or failed. A second or
so between polls is plenty; a tight loop earns a RATE_LIMITED.
curl -sS -X GET "https://api.skillsafe.ai/v1/app-api/jobs/JOB_ID" \
-H "Authorization: Bearer $TOKEN"
import json, urllib.request
TOKEN = "YOUR_TOKEN" # or read it from your own secret store
url = "https://api.skillsafe.ai/v1/app-api/jobs/JOB_ID"
req = urllib.request.Request(url, method="GET")
req.add_header("Authorization", "Bearer " + TOKEN)
with urllib.request.urlopen(req) as r:
print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";
const res = await fetch("https://api.skillsafe.ai/v1/app-api/jobs/JOB_ID", {
method: "GET",
headers: {
"Authorization": `Bearer ${TOKEN}`,
},
});
const { data, error } = await res.json();
if (error) throw new Error(error.code + ": " + error.message);
console.log(data);
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
token := os.Getenv("SKILLSAFE_TOKEN")
req, _ := http.NewRequest("GET", "https://api.skillsafe.ai/v1/app-api/jobs/JOB_ID", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;
public class Main {
public static void main(String[] a) throws Exception {
String token = System.getenv("SKILLSAFE_TOKEN");
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create("https://api.skillsafe.ai/v1/app-api/jobs/JOB_ID"))
.header("Authorization", "Bearer " + token)
.method("GET", HttpRequest.BodyPublishers.noBody());
HttpResponse<String> r = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(r.body());
}
}
require "json"
require "net/http"
require "uri"
token = ENV.fetch("SKILLSAFE_TOKEN", "YOUR_TOKEN")
uri = URI("https://api.skillsafe.ai/v1/app-api/jobs/JOB_ID")
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.parse(res.body)["data"]
<?php
$token = getenv("SKILLSAFE_TOKEN") ?: "YOUR_TOKEN";
$headers = [
"Authorization: Bearer " . $token,
];
$ch = curl_init("https://api.skillsafe.ai/v1/app-api/jobs/JOB_ID");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($out["data"]);
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var token = Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN") ?? "YOUR_TOKEN";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var req = new HttpRequestMessage(new HttpMethod("GET"), "https://api.skillsafe.ai/v1/app-api/jobs/JOB_ID");
var res = await http.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());
6 Or stream it
Server-sent events. Deltas arrive as they are generated, which is what lets the web app show
real progress rather than a spinner. The final done event carries the same payload
as a completed job, including charged_credits and truncated.
curl -sS -X POST "https://api.skillsafe.ai/v1/app-api/run-stream" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task": "explain",
"company": "Costco Wholesale",
"focus": "Why does the membership fee matter more than the retail margin?",
"context": "",
"register": "plain",
"depth": "standard",
"as_of_year": 2026,
"boundary": {
"not_advice": true,
"refused_acts": [],
"convert_not_state": [
"market_value"
],
"pasted_text_contains": []
}
}'
import json, urllib.request
TOKEN = "YOUR_TOKEN" # or read it from your own secret store
url = "https://api.skillsafe.ai/v1/app-api/run-stream"
body = json.dumps({
"task": "explain",
"company": "Costco Wholesale",
"focus": "Why does the membership fee matter more than the retail margin?",
"context": "",
"register": "plain",
"depth": "standard",
"as_of_year": 2026,
"boundary": {
"not_advice": true,
"refused_acts": [],
"convert_not_state": [
"market_value"
],
"pasted_text_contains": []
}
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", "Bearer " + TOKEN)
with urllib.request.urlopen(req) as r:
print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";
const res = await fetch("https://api.skillsafe.ai/v1/app-api/run-stream", {
method: "POST",
headers: {
"Authorization": `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"task": "explain",
"company": "Costco Wholesale",
"focus": "Why does the membership fee matter more than the retail margin?",
"context": "",
"register": "plain",
"depth": "standard",
"as_of_year": 2026,
"boundary": {
"not_advice": true,
"refused_acts": [],
"convert_not_state": [
"market_value"
],
"pasted_text_contains": []
}
}),
});
const { data, error } = await res.json();
if (error) throw new Error(error.code + ": " + error.message);
console.log(data);
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
token := os.Getenv("SKILLSAFE_TOKEN")
body := []byte(`{"task": "explain", "company": "Costco Wholesale", "focus": "Why does the membership fee matter more than the retail margin?", "context": "", "register": "plain", "depth": "standard", "as_of_year": 2026, "boundary": {"not_advice": true, "refused_acts": [], "convert_not_state": ["market_value"], "pasted_text_contains": []}}`)
req, _ := http.NewRequest("POST", "https://api.skillsafe.ai/v1/app-api/run-stream", 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()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;
public class Main {
public static void main(String[] a) throws Exception {
String token = System.getenv("SKILLSAFE_TOKEN");
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create("https://api.skillsafe.ai/v1/app-api/run-stream"))
.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(
"""
{"task": "explain", "company": "Costco Wholesale", "focus": "Why does the membership fee matter more than the retail margin?", "context": "", "register": "plain", "depth": "standard", "as_of_year": 2026, "boundary": {"not_advice": true, "refused_acts": [], "convert_not_state": ["market_value"], "pasted_text_contains": []}}
"""));
HttpResponse<String> r = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(r.body());
}
}
require "json"
require "net/http"
require "uri"
token = ENV.fetch("SKILLSAFE_TOKEN", "YOUR_TOKEN")
uri = URI("https://api.skillsafe.ai/v1/app-api/run-stream")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{token}"
req["Content-Type"] = "application/json"
req.body = {
"task" => "explain",
"company" => "Costco Wholesale",
"focus" => "Why does the membership fee matter more than the retail margin?",
"context" => "",
"register" => "plain",
"depth" => "standard",
"as_of_year" => 2026,
"boundary" => {
"not_advice" => true,
"refused_acts" => [],
"convert_not_state" => [
"market_value"
],
"pasted_text_contains" => []
}
}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = getenv("SKILLSAFE_TOKEN") ?: "YOUR_TOKEN";
$headers = [
"Authorization: Bearer " . $token,
"Content-Type: application/json",
];
$ch = curl_init("https://api.skillsafe.ai/v1/app-api/run-stream");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<'JSON'
{"task": "explain", "company": "Costco Wholesale", "focus": "Why does the membership fee matter more than the retail margin?", "context": "", "register": "plain", "depth": "standard", "as_of_year": 2026, "boundary": {"not_advice": true, "refused_acts": [], "convert_not_state": ["market_value"], "pasted_text_contains": []}}
JSON);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($out["data"]);
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var token = Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN") ?? "YOUR_TOKEN";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.skillsafe.ai/v1/app-api/run-stream");
req.Content = new StringContent(@"{""task"": ""explain"", ""company"": ""Costco Wholesale"", ""focus"": ""Why does the membership fee matter more than the retail margin?"", ""context"": """", ""register"": ""plain"", ""depth"": ""standard"", ""as_of_year"": 2026, ""boundary"": {""not_advice"": true, ""refused_acts"": [], ""convert_not_state"": [""market_value""], ""pasted_text_contains"": []}}",
Encoding.UTF8, "application/json");
var res = await http.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());
The wire format
Frames are event: NAME, then data: JSON, then a blank line. The
event names are the ones this app's vendored SDK actually dispatches on - read
_readSse in sdk.js if you want to check rather than trust
this page.
event: job
data: {"job_id":"job_...","status":"running"}
event: delta
data: {"text":"{\"company\":{\"name\":\"Costco Wholesale"}
event: delta
data: {"text":"\",\"what_it_sells\":\"Groceries and household"}
event: done
data: {"job_id":"job_...","status":"succeeded","charged_credits":just-under-the-hold,
"truncated":false,"output":{ ...the brief object... }}
| Event | Carries | What to do with it |
|---|---|---|
job | job_id, status | Store the id; it is what a retry and a poll both key on. |
delta | text | Append. The brief arrives as a JSON string in fragments, so do not try to parse until done. |
done | the full job, including output, charged_credits and truncated | Parse output. This is the only frame whose payload is complete. |
pending | the job so far | Treated as terminal by the SDK: the job outlived the stream. Poll /jobs/{id} from here. |
error | message, job_id | Keep whatever deltas arrived - a half-finished brief still renders, and this app renders it rather than discarding it. |
If the stream dies mid-frame you are left holding a truncated JSON string. That is a normal outcome, not an exceptional one: this app repairs it by closing whatever is still open and reports how many sections survived, rather than showing an error over a brief the user paid for.
7 What comes back
The model returns a single JSON object. Every array may be empty - an empty section is a true statement that the brief had nothing real to say there, and is deliberately preferred to a padded one, so write your integration to tolerate it.
{
"company": {"name": "...", "also_known_as": "...",
"listing": {"exchange": "...", "ticker": "..."},
"country": "...", "founded": "...", "what_it_sells": "..."},
"not_advice": "...",
"register_used": "plain",
"one_paragraph": "...",
"how_it_makes_money": [{"who_pays","for_what","how_the_money_arrives",
"what_must_be_true","stability","basis","as_of"}],
"segments": [{"name","what_it_does","relative_weight","weight_as_of",
"why_it_matters","where_to_verify","stability","basis"}],
"structural_forces": [{"force","mechanism","how_it_shows_up","who_it_favours",
"stability","basis","as_of"}],
"how_the_industry_works":[{"point","why_it_is_like_that","consequence","stability","basis"}],
"bull_case": [{"claim","rests_on","what_would_have_to_be_true",
"where_it_is_weakest","basis"}],
"bear_case": [ ... identical fields ... ],
"reading_the_filings": [{"what_to_look_for","where","why_it_matters",
"what_a_change_would_tell_you"}],
"vocabulary": [{"term","plain_meaning","why_it_exists"}],
"scale": [{"measure","value","as_of","where_to_verify","basis"}],
"look_it_up": [{"figure","what_it_means","where","why_not_here"}],
"limits": ["..."]
}
The two enums, and why they are load-bearing
stability is durable or slow-drift. It says how fast the
claim decays, not what it is about. A slow-drift row carries an as_of
year; if one arrives without it, that is a defect and the web app reports it rather than
papering over it. The third value, current-state, exists in the vocabulary but must
never appear in output - those claims go to look_it_up instead.
basis is structural (follows from how the industry is built),
documented (the company discloses it) or recalled (from training data,
may be stale). Treat recalled rows as leads to verify, not as sources. If every row
in a brief carries the same basis, be suspicious: real knowledge of a company is uneven.
bull_case and bear_case are structurally identical by design and are
never ranked. There is no field anywhere in this contract that says which side is right, and
adding one to your own layer would defeat the point of the boundary.
A run also returns charged_credits - what you actually paid, usually far below
the hold - and truncated, which is true when the balance sat between
min_credits and hold_credits and the output cap was reduced. A
truncated brief is a partial one: render what parsed and say so.
8 Storing briefs
The app declares a briefs collection. Records nest under doc - read
record.doc.company, not record.company. query resolves to
{records, next_cursor} while similar resolves to the records array
itself, so normalise both shapes at every call site. The ran_at field is a declared
timestamp and rejects epoch milliseconds: send an ISO-8601 string with a Z
suffix.
Four fields are embedded for vector search - company,
what_it_sells, summary and focus. Vectors are never
backfilled, so those are fixed.
curl -sS -X POST "https://api.skillsafe.ai/v1/app-api/collections/briefs/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"where": {
"register": "plain"
},
"sort": {
"field": "ran_at",
"dir": "desc"
},
"limit": 20
}'
import json, urllib.request
TOKEN = "YOUR_TOKEN" # or read it from your own secret store
url = "https://api.skillsafe.ai/v1/app-api/collections/briefs/query"
body = json.dumps({
"where": {
"register": "plain"
},
"sort": {
"field": "ran_at",
"dir": "desc"
},
"limit": 20
}).encode()
req = urllib.request.Request(url, data=body, method="POST")
req.add_header("Content-Type", "application/json")
req.add_header("Authorization", "Bearer " + TOKEN)
with urllib.request.urlopen(req) as r:
print(json.load(r)["data"])
const TOKEN = "YOUR_TOKEN";
const res = await fetch("https://api.skillsafe.ai/v1/app-api/collections/briefs/query", {
method: "POST",
headers: {
"Authorization": `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"where": {
"register": "plain"
},
"sort": {
"field": "ran_at",
"dir": "desc"
},
"limit": 20
}),
});
const { data, error } = await res.json();
if (error) throw new Error(error.code + ": " + error.message);
console.log(data);
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
token := os.Getenv("SKILLSAFE_TOKEN")
body := []byte(`{"where": {"register": "plain"}, "sort": {"field": "ran_at", "dir": "desc"}, "limit": 20}`)
req, _ := http.NewRequest("POST", "https://api.skillsafe.ai/v1/app-api/collections/briefs/query", 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()
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}
import java.net.URI;
import java.net.http.*;
public class Main {
public static void main(String[] a) throws Exception {
String token = System.getenv("SKILLSAFE_TOKEN");
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create("https://api.skillsafe.ai/v1/app-api/collections/briefs/query"))
.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(
"""
{"where": {"register": "plain"}, "sort": {"field": "ran_at", "dir": "desc"}, "limit": 20}
"""));
HttpResponse<String> r = HttpClient.newHttpClient()
.send(b.build(), HttpResponse.BodyHandlers.ofString());
System.out.println(r.body());
}
}
require "json"
require "net/http"
require "uri"
token = ENV.fetch("SKILLSAFE_TOKEN", "YOUR_TOKEN")
uri = URI("https://api.skillsafe.ai/v1/app-api/collections/briefs/query")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{token}"
req["Content-Type"] = "application/json"
req.body = {
"where" => {
"register" => "plain"
},
"sort" => {
"field" => "ran_at",
"dir" => "desc"
},
"limit" => 20
}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts JSON.parse(res.body)["data"]
<?php
$token = getenv("SKILLSAFE_TOKEN") ?: "YOUR_TOKEN";
$headers = [
"Authorization: Bearer " . $token,
"Content-Type: application/json",
];
$ch = curl_init("https://api.skillsafe.ai/v1/app-api/collections/briefs/query");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<'JSON'
{"where": {"register": "plain"}, "sort": {"field": "ran_at", "dir": "desc"}, "limit": 20}
JSON);
$out = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($out["data"]);
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
var token = Environment.GetEnvironmentVariable("SKILLSAFE_TOKEN") ?? "YOUR_TOKEN";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var req = new HttpRequestMessage(new HttpMethod("POST"), "https://api.skillsafe.ai/v1/app-api/collections/briefs/query");
req.Content = new StringContent(@"{""where"": {""register"": ""plain""}, ""sort"": {""field"": ""ran_at"", ""dir"": ""desc""}, ""limit"": 20}",
Encoding.UTF8, "application/json");
var res = await http.SendAsync(req);
Console.WriteLine(await res.Content.ReadAsStringAsync());
Idempotency
Pass Idempotency-Key on every run. The web app derives it from a hash of the task,
the company, the focus, the pasted text, the register, the depth and an attempt counter, so a
network blip retried with the same key returns the original job instead of billing twice. If you
retry, reuse the key. If you genuinely want a second brief, change the attempt counter.