Drive Mission Patch from your own code
The one metered lane, design, is available over HTTP. It takes a mission - what a person or a group is doing - with up to four names to stitch, a style and a patch size, and returns a patch built only from the app's fixed drawer of parts: a shape, two lines of border lettering, one emblem, a backdrop, a flight path, up to nine stars, one accent and six thread colours. It says whether the brief was kept as asked, simplified or swapped for an original design, and writes one sentence on what each element stands for.
The division of labour first: the model picks parts; the page draws and measures them. patchkit.js runs in node as well as in the browser. It draws the patch as an embroidered SVG, measures how tall the border letters and how big the stars come out at the real size, counts the thread colours, checks the contrast of each colour pair, gives a rough stitch count, and re-checks a reply against its brief. Letter heights, star sizes and stitch counts are rough estimates for a patch maker to confirm.
Get a token on the token page (step 2 below also shows how to mint a guest token in code).
Base URL and the envelope
Every endpoint lives under https://api.skillsafe.ai/v1/app-api, and every response uses one envelope:
{ "ok": true, "data": { ... } }
{ "ok": false, "error": { "code": "...", "message": "...", "status": 402, "details": { ... } } }
The token is minted for this app (the guest call takes {"slug":"mission-patch"}), so no slug header is needed afterwards. Send it as Authorization: Bearer …. The run body is the input object, sent bare.
| Status | Code | Meaning |
|---|---|---|
| 400 | validation_error | The body is not a JSON object, or a required field (task, mission) is missing. |
| 401 | unauthorized | No token, or a stale one. Mint a guest token or sign in again. |
| 402 | insufficient_credits | The balance is under min_credits. Price with /estimate first. |
| 403 | forbidden | A guest token tried to run: the lane is metered and needs a personal token. |
| 404 | not_found | Unknown job id. |
| 429 | rate_limited | Back off and retry. |
| 5xx | server_error | Transient. Retry with the same Idempotency-Key so a retry never double-bills. |
The task field and the fit
Every run sends task: "design". The reply's fit is decided by three ordered rules - the first that applies wins:
| Order | Fit | When |
|---|---|---|
| 1 | swapped | The brief asks to copy or imitate a real organisation's logo, patch, crest or insignia, a brand, a real person's face, or a character owned by a studio, publisher or game company. The reply designs an original patch in the same spirit and never names what it replaced. |
| 2 | simplified | The drawer cannot stitch it as asked: a photo, realistic or detailed look, faces, several main subjects, more words than two short lines, or more than four names. The most recognisable element is kept; with more than four names, one star per name (up to nine) stands in for them. |
| 3 | as-asked | Otherwise. |
The declared input schema carries scalars only, so the object-valued current travels JSON-encoded as a string (PatchKit.toWire does this). Every value is a string.
Input fields
| Field | Type | Notes |
|---|---|---|
task | string | Always design. Required. |
mission | string | What the mission is. Past 1,500 characters the middle is left out with a marker. Required. |
names | string | Names to stitch, comma-separated. May be empty. |
name_count | string | How many names names holds, as a string. |
style | string | classic, retro, playful or minimal. |
size | string | Patch width in inches: 3, 4 or 5. |
tweak, current | string, string (JSON) | Optional, together: a change to make, and the patch to change (same shape as the reply's patch). Empty otherwise. |
page_guess | string | The page's keyword guess at fit - a hint the model may overrule. |
retry_note | string | Only on a reformat retry. |
The reply
The job's output.output is a string holding one JSON object: task, fit, fit_note, title, concept, patch, meanings, tips (2-3) and at_a_glance. meanings has one sentence each for shape, emblem, backdrop, path, stars, accent and colors, and "" for an element the patch does not have.
| Part | Values |
|---|---|
| shape | round oval shield hexagon |
| top_text, bottom_text | capitals, digits, spaces and . & ' / -; up to 22 characters each |
| emblem | rocket satellite helmet telescope globe book laptop gear flask lightbulb compass house tree bicycle sailboat camera note pencil wrench mortarboard trophy heart |
| backdrop | plain starfield horizon sunburst mountains waves |
| path | none arc orbit trail |
| stars | 0-9 |
| accent | none moon sun planet comet star flag leaf |
| threads | field band text emblem accent backdrop, each one of navy royal sky teal forest mint white silver black gold orange red maroon pink purple sand brown |
The page replaces any value outside the drawer and says so.
Worked examples
Replies produced against the app's prompt for the four sample runs over three made-up briefs (long fields shortened; the full replies ship in example-result.js). Together they cover every fit and the change-a-patch loop.
design - a robotics team's first state championship, four names: as-asked
Input:
{
"task": "design",
"mission": "Our high school robotics team is going to the state championship for the first time. Our robot climbs a bar at the end of every match, and we built it in a garage.",
"names": "Priya, Marcus, Lena, Tom",
"name_count": "4",
"style": "classic",
"size": "4",
"tweak": "",
"current": "",
"page_guess": "as-asked"
}
Reply:
{
"task": "design",
"fit": "as-asked",
"fit_note": "Every detail from the brief - the trip to the state championship and all four teammates - made it onto the patch, with the names spaced out instead of separated by dashes so each one fits clearly.",
"title": "First State Championship",
"concept": "A high school robotics team heads to the state championship for the first time, powered by a robot that climbs a bar at the end of every match and was built together in a garage.",
"patch": {
"shape": "round",
"top_text": "STATE CHAMPIONSHIP",
"bottom_text": "PRIYA MARCUS LENA TOM",
"emblem": "gear",
"backdrop": "plain",
"path": "none",
"stars": 0,
"accent": "flag",
"threads": {
"field": "royal",
"band": "navy",
"text": "white",
"emblem": "silver",
"accent": "gold",
"backdrop": "royal"
}
},
"meanings": {
"shape": "The round shape holds the mission name and every teammate's name together, simple and easy to read.",
"emblem": "The gear stands for the robot the team built in a garage.",
"backdrop": "",
"path": "",
"stars": "",
"accent": "The flag marks the team's first trip to the state championship.",
"colors": "The royal blue background and white lettering keep the team's name and the championship easy to read, while the silver gear and gold flag stand out as marks of the team's work and this milestone."
},
"tips": [
"Send the real-size file straight to a patch maker so the lettering stitches at the size shown here.",
"Choose a merrowed edge for this round shape.",
"... (3 tips in all)"
],
"at_a_glance": "A silver gear with a small gold flag sits on a royal blue background, framed by a navy border lettered in white with the state championship and all four teammates' names."
}
design - a family road trip with nine names, as realistic as possible: simplified
Input:
{
"task": "design",
"mission": "Family reunion road trip in a rented minivan, from the lake house to the canyon. Make it as realistic as possible, with the van, the canyon at sunset and everyone's faces.",
"names": "Rose, Dave, Mia, Sam, Jo, Kenji, Ava, Leo, Nora",
"name_count": "9",
"style": "retro",
"size": "4",
"tweak": "",
"current": "",
"page_guess": "simplified"
}
Reply:
{
"task": "design",
"fit": "simplified",
"fit_note": "Kept the canyon and its sunset, shown as mountains with a sun beside the compass, and left out the realistic van and everyone's faces, which the drawer cannot draw; gave one star to each of the nine names instead of placing them on the border.",
"title": "Family Reunion Road Trip",
"concept": "A compass with a dotted trail stands for the road trip from the lake house to the canyon, set against mountains with a sun for the sunset at the end. Nine stars stand for the nine people on the trip.",
"patch": {
"shape": "oval",
"top_text": "FAMILY REUNION TRIP",
"bottom_text": "LAKE HOUSE TO CANYON",
"emblem": "compass",
"backdrop": "mountains",
"path": "trail",
"stars": 9,
"accent": "sun",
"threads": {
"field": "sand",
"band": "maroon",
"text": "gold",
"emblem": "brown",
"accent": "orange",
"backdrop": "teal"
}
},
"meanings": {
"shape": "The oval shape gives the patch a classic badge look for marking a trip.",
"emblem": "The compass stands for the road trip, pointing the way from the lake house to the canyon.",
"backdrop": "The mountains stand for the canyon the trip ends at.",
"path": "The dotted trail traces the route the family drives, from the lake house to the canyon.",
"stars": "The nine stars stand for the nine people on the trip.",
"accent": "The sun stands for the sunset at the canyon.",
"colors": "The sand, maroon, gold, brown, orange and teal threads give the patch a warm, sunset-toned feel."
},
"tips": [
"Send the page's real-size file to a patch maker so the stitching comes out at true scale.",
"An oval shape works well with a merrowed (wrapped) edge.",
"... (3 tips in all)"
],
"at_a_glance": "From across a room you see a sand oval patch with a brown compass and a dotted trail over teal mountains, an orange sun beside it, nine stars above, and gold lettering on a maroon border naming the trip and its route."
}
design - an exact copy of a space agency's logo for an astronomy club: swapped
Input:
{
"task": "design",
"mission": "Our astronomy club, Backyard Skies, is watching the Phobos sample-return launch stream together. Make it an exact copy of the space agency's official logo, just with our club name on it.",
"names": "",
"name_count": "0",
"style": "classic",
"size": "3",
"tweak": "",
"current": "",
"page_guess": "swapped"
}
Reply:
{
"task": "design",
"fit": "swapped",
"fit_note": "Replaced the real agency's logo with an original design; kept the club and the Phobos launch they watched together, built from a telescope, an arc and a moon instead of that agency's marks.",
"title": "Backyard Skies Phobos Launch",
"concept": "A patch for Backyard Skies, marking the club's watching of the Phobos launch stream together, with a telescope standing for the club and an arc and moon tracing the launch they watched.",
"patch": {
"shape": "round",
"top_text": "BACKYARD SKIES",
"bottom_text": "PHOBOS LAUNCH STREAM",
"emblem": "telescope",
"backdrop": "starfield",
"path": "arc",
"stars": 0,
"accent": "moon",
"threads": {
"field": "navy",
"band": "maroon",
"text": "gold",
"emblem": "silver",
"accent": "gold",
"backdrop": "sky"
}
},
"meanings": {
"shape": "The round shape holds the club's name and the launch they watched in one unbroken ring.",
"emblem": "The telescope stands for the astronomy club watching together.",
"backdrop": "The starfield represents the astronomy club watching the sky.",
"path": "The arc traces the launch the club watched together.",
"stars": "",
"accent": "The moon stands for Phobos, named in the launch the club watched together.",
"colors": "The navy background and maroon border carry gold lettering, a silver telescope, a gold arc and moon, and a sky-blue backdrop that echoes the club's own name."
},
"tips": [
"Send this real-size file to a patch maker.",
"Choose a merrowed edge for the round shape.",
"... (3 tips in all)"
],
"at_a_glance": "From across the room: a silver telescope under a gold arc and moon, ringed in gold lettering naming Backyard Skies and the Phobos launch."
}
design with tweak - the robotics patch, made retro with a moon (starts from the first reply's patch)
Input:
{
"task": "design",
"mission": "Our high school robotics team is going to the state championship for the first time. Our robot climbs a bar at the end of every match, and we built it in a garage.",
"names": "Priya, Marcus, Lena, Tom",
"name_count": "4",
"style": "classic",
"size": "4",
"tweak": "Make it retro, and add a moon for the late nights in the garage",
"current": "{\"shape\":\"round\",\"top_text\":\"STATE CHAMPIONSHIP\",\"bottom_text\":\"PRIYA MARCUS LENA TOM\",\"em ... (272 characters)",
"page_guess": "as-asked"
}
Reply:
{
"task": "design",
"fit": "as-asked",
"fit_note": "Keeps the round shape, the gear for the robot, and all four names along the border; adds a moon beside the gear for the garage nights and shifts the thread colors to a warm retro mix.",
"title": "First State Championship",
"concept": "This patch marks a high school robotics team's first trip to the state championship, centered on a gear for the robot that climbs a bar at the end of every match. A moon beside it marks the late nights spent building in the garage, with Priya, Marcus, Lena, and Tom named along the border in a warm retro palette.",
"patch": {
"shape": "round",
"top_text": "STATE CHAMPIONSHIP",
"bottom_text": "PRIYA MARCUS LENA TOM",
"emblem": "gear",
"backdrop": "plain",
"path": "none",
"stars": 0,
"accent": "moon",
"threads": {
"field": "maroon",
"band": "sand",
"text": "brown",
"emblem": "gold",
"accent": "teal",
"backdrop": "maroon"
}
},
"meanings": {
"shape": "The round shape gives a classic look and leaves room on the border for the team's four names.",
"emblem": "The gear represents the robot the team built and the climb it makes at the end of every match.",
"backdrop": "",
"path": "",
"stars": "",
"accent": "The moon stands for the late nights the team spent working in the garage.",
"colors": "The retro mix of maroon, sand, brown, gold, and teal gives the patch a warm, vintage feel to match a team built in a garage."
},
"tips": [
"Send the full-size file to a patch maker so the lettering stays legible.",
"Choose a merrowed (wrapped) edge, which suits this round shape.",
"... (3 tips in all)"
],
"at_a_glance": "From across the room: a round patch with a gold gear and a small teal moon on a maroon background, STATE CHAMPIONSHIP along the top border and Priya, Marcus, Lena, and Tom named along the bottom."
}
Build the input yourself
Load the script in node (global.window = global; require("./patchkit.js")), then:
const K = PatchKit;
const brief = K.brief({ mission: "My PhD thesis on river ice, defended this spring", names: "", style: "minimal", size: "4" });
console.log(K.prescan(brief.mission, brief.names)); // the page's keyword guess at the fit
const wire = K.toWire(K.buildInput(brief).input); // POST this to /estimate and /run
// then: const n = K.normalize(K.parseReply(job.output.output));
// const m = K.measure(n.patch, brief.size); // letter heights, star size, threads, contrast, rough stitches
// const { notes } = K.check(n, brief); // the page's re-check of the reply
// K.patchFile(n.patch, brief.size, brief.style) // a real-size patch as an SVG file
// and to change it: K.toWire(K.buildInput(Object.assign({}, brief, { tweak: "Add a moon" }), n.patch).input)
Step 1 — a tiny client helper
One function that sends JSON and unwraps the envelope. Paste a personal token from /tokens.html, or leave it empty and mint a guest token in step 2.
BASE="https://api.skillsafe.ai/v1/app-api"
TOKEN="paste-your-token-here" # from https://mission-patch.skillsafe.ai/tokens.html
api() { # api METHOD PATH [JSON-BODY]
curl -s -X "$1" "$BASE$2" -H "Content-Type: application/json" \
${TOKEN:+-H "Authorization: Bearer $TOKEN"} ${3:+--data "$3"}
}
import json, urllib.request
BASE = "https://api.skillsafe.ai/v1/app-api"
TOKEN = "paste-your-token-here" # from https://mission-patch.skillsafe.ai/tokens.html
def api(method, path, body=None, headers=None):
req = urllib.request.Request(BASE + path, method=method,
data=None if body is None else json.dumps(body).encode(),
headers={"Content-Type": "application/json", **({"Authorization": "Bearer " + TOKEN} if TOKEN else {}), **(headers or {})})
with urllib.request.urlopen(req) as r:
env = json.load(r)
if env.get("error"):
raise RuntimeError(env["error"])
return env["data"]
// mission-patch.mjs -- Node 18+ (global fetch)
const BASE = "https://api.skillsafe.ai/v1/app-api";
let TOKEN = "paste-your-token-here"; // from https://mission-patch.skillsafe.ai/tokens.html
async function api(method, path, body, headers = {}) {
const r = await fetch(BASE + path, { method, headers: { "Content-Type": "application/json",
...(TOKEN ? { Authorization: "Bearer " + TOKEN } : {}), ...headers },
body: body === undefined ? undefined : JSON.stringify(body) });
const env = await r.json();
if (env.error) throw new Error(env.error.code + ": " + env.error.message);
return env.data;
}
package main
import ("bytes"; "encoding/json"; "fmt"; "net/http")
const base = "https://api.skillsafe.ai/v1/app-api"
var token = "paste-your-token-here" // from https://mission-patch.skillsafe.ai/tokens.html
func api(method, path string, body any, headers map[string]string) (map[string]any, error) {
var buf bytes.Buffer
if body != nil { json.NewEncoder(&buf).Encode(body) }
req, _ := http.NewRequest(method, base+path, &buf)
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 struct { Data map[string]any; Error map[string]any }
json.NewDecoder(res.Body).Decode(&env)
if env.Error != nil { return nil, fmt.Errorf("%v", env.Error) }
return env.Data, nil
}
// MissionPatch.java -- Java 17+, with com.fasterxml.jackson.core:jackson-databind
import com.fasterxml.jackson.databind.*;
import java.net.URI; import java.net.http.*; import java.util.*;
public class MissionPatch {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static String token = "paste-your-token-here"; // from https://mission-patch.skillsafe.ai/tokens.html
static final ObjectMapper M = new ObjectMapper();
static final HttpClient C = HttpClient.newHttpClient();
static JsonNode api(String method, String path, Object body, Map<String, String> headers) throws Exception {
var b = HttpRequest.newBuilder(URI.create(BASE + path)).header("Content-Type", "application/json");
if (!token.isEmpty()) b.header("Authorization", "Bearer " + token);
if (headers != null) headers.forEach(b::header);
b.method(method, body == null ? HttpRequest.BodyPublishers.noBody() : HttpRequest.BodyPublishers.ofString(M.writeValueAsString(body)));
JsonNode env = M.readTree(C.send(b.build(), HttpResponse.BodyHandlers.ofString()).body());
if (env.hasNonNull("error")) throw new RuntimeException(env.get("error").toString());
return env.get("data");
}
}
require "json"
require "net/http"
BASE = "https://api.skillsafe.ai/v1/app-api"
TOKEN = "paste-your-token-here" # from https://mission-patch.skillsafe.ai/tokens.html
def api(method, path, body = nil, headers = {})
uri = URI(BASE + path)
req = Net::HTTPGenericRequest.new(method, !body.nil?, true, uri.request_uri,
{ "Content-Type" => "application/json" }.merge(TOKEN.empty? ? {} : { "Authorization" => "Bearer #{TOKEN}" }).merge(headers))
req.body = JSON.generate(body) unless body.nil?
env = JSON.parse(Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body)
raise env["error"].to_s if env["error"]
env["data"]
end
<?php
const BASE = "https://api.skillsafe.ai/v1/app-api";
$TOKEN = "paste-your-token-here"; // from https://mission-patch.skillsafe.ai/tokens.html
function api(string $method, string $path, $body = null, array $headers = []) {
global $TOKEN;
$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_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => $h, CURLOPT_RETURNTRANSFER => true]);
if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
$env = json_decode(curl_exec($ch), true);
if (!empty($env["error"])) throw new Exception(json_encode($env["error"]));
return $env["data"];
}
// Program.cs -- .NET 6+ console app
using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Nodes;
static class Api {
public const string Base = "https://api.skillsafe.ai/v1/app-api";
public static string Token = "paste-your-token-here"; // from https://mission-patch.skillsafe.ai/tokens.html
static readonly HttpClient C = new();
public static async Task<JsonNode?> Call(string method, string path, object? body = null, Dictionary<string, string>? headers = null) {
var req = new HttpRequestMessage(new HttpMethod(method), Base + path);
if (Token != "") req.Headers.Add("Authorization", "Bearer " + Token);
if (headers != null) foreach (var h in headers) req.Headers.Add(h.Key, h.Value);
if (body != null) req.Content = JsonContent.Create(body);
var env = JsonNode.Parse(await (await C.SendAsync(req)).Content.ReadAsStringAsync())!;
if (env["error"] is JsonNode e && e.GetValueKind() != JsonValueKind.Null) throw new Exception(e.ToJsonString());
return env["data"];
}
}
Step 2 — a token
A guest token is enough for /me and /estimate. A run is metered, so it needs a personal token: sign in on /tokens.html and copy it from there.
# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN=$(curl -s -X POST "$BASE/guest" -H "Content-Type: application/json" -d '{"slug":"mission-patch"}' | jq -r '.data.token')
# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN = ""
TOKEN = api("POST", "/guest", {"slug": "mission-patch"})["token"]
// a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN = "";
TOKEN = (await api("POST", "/guest", { slug: "mission-patch" })).token;
// inside main: a guest token (enough for /me and /estimate); a run needs your personal token
token = ""
g, err := api("POST", "/guest", map[string]string{"slug": "mission-patch"}, nil)
if err != nil { panic(err) }
token = g["token"].(string)
// inside main: a guest token (enough for /me and /estimate); a run needs your personal token
token = "";
token = api("POST", "/guest", Map.of("slug", "mission-patch"), null).get("token").asText();
# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN.replace("")
TOKEN.replace(api("POST", "/guest", { slug: "mission-patch" })["token"])
// a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
$TOKEN = "";
$TOKEN = api("POST", "/guest", ["slug" => "mission-patch"])["token"];
// a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
Api.Token = "";
Api.Token = (await Api.Call("POST", "/guest", new { slug = "mission-patch" }))!["token"]!.GetValue<string>();
Step 3 — GET /me
Who the token is (subject_type: user or guest) and the credit balance.
api GET /me | jq '.data | {subject_type, credits}'
me = api("GET", "/me")
print(me["subject_type"], me.get("credits"))
const me = await api("GET", "/me");
console.log(me.subject_type, me.credits);
me, _ := api("GET", "/me", nil, nil)
fmt.Println(me["subject_type"], me["credits"])
JsonNode me = api("GET", "/me", null, null);
System.out.println(me.get("subject_type") + " " + me.get("credits"));
me = api("GET", "/me")
puts [me["subject_type"], me["credits"]].inspect
$me = api("GET", "/me");
echo $me["subject_type"], " ", $me["credits"] ?? "", "\n";
var me = await Api.Call("GET", "/me");
Console.WriteLine($"{me!["subject_type"]} {me["credits"]}");
Step 4 — POST /estimate
Free. The body is the run input itself, the bare object (never wrapped in {"input": ...}). hold_credits is a reservation, not the price; min_credits is what the balance must cover; model_alias reads gpt-terra.
# input.json: the run input object - {"task": "design", "mission": "Our high school robotics team is going to the state championship for the first time. Our robot climbs a bar at the end of every match, and we built it in a garage.", "names": "Priya, Marcus, Lena, Tom", "name_count": "4", "style": "classic", "size": "4", "tweak": "", "current": "", "page_guess": "as-asked"}
api POST /estimate "$(cat input.json)" | jq '.data | {hold_credits, min_credits, model, model_alias, markup_bps, warnings}'
inp = json.load(open("input.json")) # {"task": "design", "mission": "Our high school robotics team is going to the state championship for the first time. Our robot climbs a bar at the end of every match, and we built it in a garage.", "names": "Priya, Marcus, Lena, Tom", "name_count": "4", "style": "classic", "size": "4", "tweak": "", "current": "", "page_guess": "as-asked"}
est = api("POST", "/estimate", inp)
print(est["hold_credits"], est["min_credits"], est["model"], est.get("warnings"))
import { readFileSync } from "node:fs";
const input = JSON.parse(readFileSync("input.json", "utf8")); // {task: "design", mission, names, name_count, style, size, ... (see the field table)}
const est = await api("POST", "/estimate", input);
console.log(est.hold_credits, est.min_credits, est.model, est.warnings);
raw, _ := os.ReadFile("input.json") // add "os" to the imports
var input map[string]any
json.Unmarshal(raw, &input)
est, _ := api("POST", "/estimate", input, nil)
fmt.Println(est["hold_credits"], est["min_credits"], est["model"])
Map<String, Object> input = M.readValue(new java.io.File("input.json"), Map.class);
JsonNode est = api("POST", "/estimate", input, null);
System.out.println(est.get("hold_credits") + " " + est.get("min_credits") + " " + est.get("model"));
input = JSON.parse(File.read("input.json"))
est = api("POST", "/estimate", input)
puts [est["hold_credits"], est["min_credits"], est["model"]].inspect
$input = json_decode(file_get_contents("input.json"), true);
$est = api("POST", "/estimate", $input);
echo $est["hold_credits"], " ", $est["min_credits"], " ", $est["model"], "\n";
var input = JsonNode.Parse(File.ReadAllText("input.json"))!;
var est = await Api.Call("POST", "/estimate", input);
Console.WriteLine($"{est!["hold_credits"]} {est["min_credits"]} {est["model"]}");
Step 5 — POST /run, then poll the job
Send an Idempotency-Key built from the task and the input: a retry with the same key never bills twice. The page uses mission-patch:<task>:<inputHash>:a<attempt> (the task is always design), and its one reformat retry (with retry_note added) is attempt 2, a new key and a second run. The job's output.output is the reply text; truncated: true means the balance cut it short.
KEY="mission-patch:design:$(shasum -a 256 input.json | cut -c1-16):a1"
JOB=$(curl -s -X POST "$BASE/run" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: $KEY" --data @input.json | jq -r '.data.job_id')
while :; do
J=$(api GET "/jobs/$JOB"); S=$(echo "$J" | jq -r '.data.status')
case "$S" in succeeded|failed|cancelled) break;; esac; sleep 2
done
echo "$J" | jq -r '.data.output.output' > reply.json # one JSON object, the reply contract
echo "$J" | jq '.data | {charged_credits, truncated}'
import hashlib, time
key = "mission-patch:" + inp["task"] + ":" + hashlib.sha256(json.dumps(inp).encode()).hexdigest()[:16] + ":a1"
job = api("POST", "/run", inp, {"Idempotency-Key": key})
while True:
j = api("GET", "/jobs/" + job["job_id"])
if j["status"] in ("succeeded", "failed", "cancelled"):
break
time.sleep(2)
reply = json.loads(j["output"]["output"]) # the design as one JSON object
print(j["charged_credits"], j.get("truncated"), reply.get("fit"))
import { createHash } from "node:crypto";
const key = `mission-patch:${input.task}:${createHash("sha256").update(JSON.stringify(input)).digest("hex").slice(0, 16)}:a1`;
const job = await api("POST", "/run", input, { "Idempotency-Key": key });
let j;
do { await new Promise((r) => setTimeout(r, 2000)); j = await api("GET", "/jobs/" + job.job_id); }
while (!["succeeded", "failed", "cancelled"].includes(j.status));
const reply = JSON.parse(j.output.output);
console.log(j.charged_credits, j.truncated, reply.fit);
sum := sha256.Sum256(raw) // add "crypto/sha256", "encoding/hex", "time" to the imports
key := "mission-patch:" + input["task"].(string) + ":" + hex.EncodeToString(sum[:8]) + ":a1"
job, _ := api("POST", "/run", input, map[string]string{"Idempotency-Key": key})
var j map[string]any
for {
time.Sleep(2 * time.Second)
j, _ = api("GET", "/jobs/"+job["job_id"].(string), nil, nil)
if s := j["status"]; s == "succeeded" || s == "failed" || s == "cancelled" { break }
}
fmt.Println(j["charged_credits"], j["truncated"], j["output"].(map[string]any)["output"])
var digest = java.security.MessageDigest.getInstance("SHA-256").digest(M.writeValueAsBytes(input));
String key = "mission-patch:" + input.get("task") + ":" + HexFormat.of().formatHex(digest, 0, 8) + ":a1";
JsonNode job = api("POST", "/run", input, Map.of("Idempotency-Key", key)), j;
do { Thread.sleep(2000); j = api("GET", "/jobs/" + job.get("job_id").asText(), null, null); }
while (!Set.of("succeeded", "failed", "cancelled").contains(j.get("status").asText()));
JsonNode reply = M.readTree(j.get("output").get("output").asText());
System.out.println(j.get("charged_credits") + " " + reply);
require "digest"
key = "mission-patch:#{input["task"]}:#{Digest::SHA256.hexdigest(JSON.generate(input))[0, 16]}:a1"
job = api("POST", "/run", input, { "Idempotency-Key" => key })
j = nil
loop do
sleep 2
j = api("GET", "/jobs/#{job["job_id"]}")
break if %w[succeeded failed cancelled].include?(j["status"])
end
reply = JSON.parse(j["output"]["output"])
puts [j["charged_credits"], j["truncated"], reply["title"]].inspect
$key = "mission-patch:" . $input["task"] . ":" . substr(hash("sha256", json_encode($input)), 0, 16) . ":a1";
$job = api("POST", "/run", $input, ["Idempotency-Key" => $key]);
do { sleep(2); $j = api("GET", "/jobs/" . $job["job_id"]); }
while (!in_array($j["status"], ["succeeded", "failed", "cancelled"]));
$reply = json_decode($j["output"]["output"], true);
echo $j["charged_credits"], " ", $reply["title"], "\n";
var digest = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(input.ToJsonString()));
var key = $"mission-patch:{input["task"]}:{Convert.ToHexString(digest, 0, 8).ToLowerInvariant()}:a1";
var job = await Api.Call("POST", "/run", input, new() { ["Idempotency-Key"] = key });
JsonNode j;
do { await Task.Delay(2000); j = (await Api.Call("GET", "/jobs/" + job!["job_id"]))!; }
while (!new[] { "succeeded", "failed", "cancelled" }.Contains(j["status"]!.GetValue<string>()));
var reply = JsonNode.Parse(j["output"]!["output"]!.GetValue<string>());
Console.WriteLine($"{j["charged_credits"]} {reply}");
Step 6 — POST /run-stream
The same run as Server-Sent Events, with the same input.json. Browsers receive progress ticks rather than text deltas; the final done event carries the reply.
# Server-Sent Events: tick/delta events while the job runs, then a final event with the job
curl -N -s -X POST "$BASE/run-stream" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: mission-patch:design:$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json
rev = json.load(open("input.json")) # the same run input as input.json: {"task": "design", "mission": "Our high school robotics team is going to the state championship for the first time. Our robot climbs a bar at the end of every match, and we built it in a garage.", "names": "Priya, Marcus, Lena, Tom", "name_count": "4", "style": "classic", "size": "4", "tweak": "", "current": "", "page_guess": "as-asked"}
req = urllib.request.Request(BASE + "/run-stream", method="POST", data=json.dumps(rev).encode(),
headers={"Content-Type": "application/json", "Authorization": "Bearer " + TOKEN,
"Idempotency-Key": "mission-patch:design:" + hashlib.sha256(json.dumps(rev).encode()).hexdigest()[:16] + ":a1"})
with urllib.request.urlopen(req) as r:
for line in r:
line = line.decode().strip()
if line.startswith("data:"):
print(line[5:].strip()[:200])
const rev = JSON.parse(readFileSync("input.json", "utf8"));
const r = await fetch(BASE + "/run-stream", { method: "POST", body: JSON.stringify(rev), headers: {
"Content-Type": "application/json", Authorization: "Bearer " + TOKEN,
"Idempotency-Key": `mission-patch:design:${createHash("sha256").update(JSON.stringify(rev)).digest("hex").slice(0, 16)}:a1` } });
const dec = new TextDecoder();
for await (const chunk of r.body) for (const line of dec.decode(chunk).split("\n"))
if (line.startsWith("data:")) console.log(line.slice(5).trim().slice(0, 200));
rraw, _ := os.ReadFile("input.json") // add "bufio", "strings" to the imports
rs := sha256.Sum256(rraw)
req, _ := http.NewRequest("POST", base+"/run-stream", bytes.NewReader(rraw))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Idempotency-Key", "mission-patch:design:"+hex.EncodeToString(rs[:8])+":a1")
res, _ := http.DefaultClient.Do(req)
sc := bufio.NewScanner(res.Body)
for sc.Scan() { if l := sc.Text(); strings.HasPrefix(l, "data:") { fmt.Println(l[5:]) } }
String rev = java.nio.file.Files.readString(java.nio.file.Path.of("input.json"));
var rd = java.security.MessageDigest.getInstance("SHA-256").digest(rev.getBytes());
var sreq = HttpRequest.newBuilder(URI.create(BASE + "/run-stream")).header("Content-Type", "application/json")
.header("Authorization", "Bearer " + token).header("Idempotency-Key", "mission-patch:design:" + HexFormat.of().formatHex(rd, 0, 8) + ":a1")
.POST(HttpRequest.BodyPublishers.ofString(rev)).build();
C.send(sreq, HttpResponse.BodyHandlers.ofLines()).body().filter(l -> l.startsWith("data:")).forEach(System.out::println);
rev = File.read("input.json")
uri = URI(BASE + "/run-stream")
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json", "Authorization" => "Bearer #{TOKEN}",
"Idempotency-Key" => "mission-patch:design:#{Digest::SHA256.hexdigest(rev)[0, 16]}:a1")
req.body = rev
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |h|
h.request(req) { |res| res.read_body { |chunk| chunk.each_line { |l| puts l if l.start_with?("data:") } } }
end
$rev = file_get_contents("input.json");
$ch = curl_init(BASE . "/run-stream");
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $rev, CURLOPT_HTTPHEADER => [
"Content-Type: application/json", "Authorization: Bearer $TOKEN",
"Idempotency-Key: mission-patch:design:" . substr(hash("sha256", $rev), 0, 16) . ":a1"],
CURLOPT_WRITEFUNCTION => function ($ch, $chunk) { foreach (explode("\n", $chunk) as $l) if (str_starts_with($l, "data:")) echo $l, "\n"; return strlen($chunk); }]);
curl_exec($ch);
var rev = File.ReadAllText("input.json");
var rd = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(rev));
var sreq = new HttpRequestMessage(HttpMethod.Post, Api.Base + "/run-stream") { Content = new StringContent(rev, System.Text.Encoding.UTF8, "application/json") };
sreq.Headers.Add("Authorization", "Bearer " + Api.Token);
sreq.Headers.Add("Idempotency-Key", $"mission-patch:design:{Convert.ToHexString(rd, 0, 8).ToLowerInvariant()}:a1");
using var http = new HttpClient();
using var res = await http.SendAsync(sreq, HttpCompletionOption.ResponseHeadersRead);
using var rdr = new StreamReader(await res.Content.ReadAsStreamAsync());
for (string? l; (l = await rdr.ReadLineAsync()) != null;) if (l.StartsWith("data:")) Console.WriteLine(l);