← Load-Bearing Icing
API

Drive Load-Bearing Icing from your own code

The one metered task, redteam, is available over HTTP. It takes 1 gingerbread house plan - the page's lines (the fixed numbers, the house line with every size and choice and the verdict, and 6 ranked assumption lines, each with its risk, score and evidence) and the person's note - and returns the verdict, a headline, 1 attack per assumption at medium or high risk (the case for it, the attack, the cheapest test, a give-up line and the fix) and a closing line.

The division of labour first: the page does the geometry, a fixed rule rates each assumption and decides the verdict, and the score fixes the rank; the model makes the case, attacks and writes the tests. After a run the page checks that the verdict follows the rule, that every risky assumption is attacked once in rank order with its own risk, that every number in the prose is in the plan, that the glue named matches the plan, that word limits hold, and that no ids leak into the prose.

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": "..." } }

The token is minted for this app (the guest call takes {"slug":"load-bearing-icing"}), so no slug header is needed afterwards. Send it as Authorization: Bearer …. The run body is the input object, sent bare.

StatusCodeMeaning
400validation_errorThe body is not a JSON object, or a required field (task, plan, notes) is missing.
401unauthorizedNo token, or a stale one. Mint a guest token or sign in again.
402insufficient_creditsThe balance is under min_credits. Price with /estimate first.
403forbiddenA guest token tried to run: an attack is metered and needs a personal sign-in.
404not_foundUnknown job id.
429rate_limitedBack off and retry.
5xxserver_errorTransient. Retry with the same Idempotency-Key so a retry never double-bills.

The task field, the input and the verdicts

Every run sends task: redteam (the one task is redteam); a missing or different one is still answered as redteam. The 6 assumptions are walls-stand, roof-stays, glue-holds, roof-load, survives-display and pieces-fit, each rated low, medium or high by the rules on the page and scored impact x likelihood x cheap to test. The verdict, checked in order: a high risk on walls, roof or glue, redesign; any medium or high risk, test-first; otherwise build-it.

FieldRequiredWhat it holds
taskyesAlways redteam.
planyesThe page's lines: a house numbers: line, a line such as house | Candy tower | front 12 cm wide | sides 10 cm deep | walls 18 cm tall | peak 12 cm above the walls | dough 5 mm | glue: soft frosting | walls set 1 h before the roof | roof candy: heavy | on show 5 days in a steamy room | verdict: redesign, then 6 lines such as rank 1 | glue-holds | risk: high | impact 3 x likelihood 3 x cheap to test 3 = 27 | soft frosting, walls set 1 h before the roof goes on.
notesyesThe person's note, or none.
retry_notenoOnly on the page's single reformat retry.

The reply is one JSON object:

{"task": "redteam", "verdict": "redesign", "headline": "...", "attacks": [{"assumption": "glue-holds", "risk": "high", "steelman": "...", "attack": "...", "cheapest_test": "...", "give_up_if": "If ...", "fix": "..."}, ...], "closing": "..."}
FieldWhat it holds
verdictCopied from the house line.
headlineThe verdict, how many assumptions are under attack and the top-ranked one, at most 20 words.
attacks1 per assumption at medium or high risk, in rank order (empty for build-it): assumption and risk (copied), steelman (at most 16 words), attack (22), cheapest_test (24), give_up_if (starts with "If", 20) and fix (22).
closingThe first test to run, or that the plan is ready to build, at most 22 words.

The page then checks the reply (IcingKit.check) and lists any disagreement under "The page's check".

Worked examples - every verdict

These are the four free samples on the page: the exact input the page sends and the reply that replays for free.

Sideboard cottage - test first, a creeping roof and a long display

Input:

{
 "task": "redteam",
 "plan": "house numbers: tray 38 x 28 cm, 3 or more trays is a long bake | each roof panel overhangs the walls by 1 cm | walls wobble past 22 times taller than thick and fall past 30 | roofs creep from 40 degrees and slide from 50 | a roof slant past 40 times the dough thickness sags | royal icing needs 2 h before the roof | soft frosting never sets hard | heavy candy needs 6 mm dough | gingerbread softens past 3 days in a steamy room, past 7 in an ordinary room, past 28 in a dry room | a test runs 1 h | hold each joint 10 min\nhouse | Sideboard cottage | front 18 cm wide | sides 12 cm deep | walls 10 cm tall | peak 6 cm above the walls | dough 6 mm | glue: royal icing | walls set 3 h before the roof | roof candy: light | on show 10 days in an ordinary room | verdict: test-first\nrank 1 | roof-stays | risk: medium | impact 3 x likelihood 2 x cheap to test 2 = 12 | roof at 45 degrees, each panel 8.5 cm from eave to peak, 14 times the dough thickness\nrank 2 | glue-holds | risk: low | impact 3 x likelihood 1 x cheap to test 3 = 9 | royal icing, walls set 3 h before the roof goes on\nrank 3 | walls-stand | risk: low | impact 3 x likelihood 1 x cheap to test 2 = 6 | 10 cm walls on 6 mm dough stand 17 times taller than thick\nrank 4 | roof-load | risk: low | impact 2 x likelihood 1 x cheap to test 2 = 4 | light candy on the roof, 6 mm dough\nrank 5 | survives-display | risk: medium | impact 2 x likelihood 2 x cheap to test 1 = 4 | 10 days on show in an ordinary room\nrank 6 | pieces-fit | risk: low | impact 1 x likelihood 1 x cheap to test 3 = 3 | 6 panels on 2 trays",
 "notes": "my first gingerbread house; it will stand on the sideboard by the front door"
}

Reply:

{
 "task": "redteam",
 "verdict": "test-first",
 "headline": "Test first: 2 parts of the plan are under attack, starting with whether the roof stays put.",
 "attacks": [
  {
   "assumption": "roof-stays",
   "risk": "medium",
   "steelman": "The panels are only 14 times the dough thickness, short of the slant that causes sagging.",
   "attack": "At 45 degrees, the roof is past the 40-degree point where roofs start to creep, nearing the 50-degree slide.",
   "cheapest_test": "Before baking the roof, glue a spare panel with royal icing onto a board tilted to 45 degrees and watch for 1 h.",
   "give_up_if": "If the panel slips on the tilted board within the hour, lower the roof peak below 40 degrees.",
   "fix": "Lower the peak below 40 degrees, and hold each roof panel in place for 10 min before letting it stand propped."
  },
  {
   "assumption": "survives-display",
   "risk": "medium",
   "steelman": "Nothing about the sideboard by the front door says why it would last that long.",
   "attack": "On show 10 days in an ordinary room, past the 7-day point where ordinary rooms start to soften.",
   "cheapest_test": "Before assembling, leave a spare scrap of gingerbread on the sideboard by the front door and check it the next morning.",
   "give_up_if": "If the scrap softens by morning, choose a drier spot or fewer days on show instead.",
   "fix": "Move the house to a drier spot, or shorten how many days it stays on show."
  }
 ],
 "closing": "Try gluing a spare panel onto a board tilted to 45 degrees for 1 h before you bake the real roof."
}

Tall tower by the kettle - redesign, 5 assumptions under attack

Input:

{
 "task": "redteam",
 "plan": "house numbers: tray 38 x 28 cm, 3 or more trays is a long bake | each roof panel overhangs the walls by 1 cm | walls wobble past 22 times taller than thick and fall past 30 | roofs creep from 40 degrees and slide from 50 | a roof slant past 40 times the dough thickness sags | royal icing needs 2 h before the roof | soft frosting never sets hard | heavy candy needs 6 mm dough | gingerbread softens past 3 days in a steamy room, past 7 in an ordinary room, past 28 in a dry room | a test runs 1 h | hold each joint 10 min\nhouse | Candy tower | front 12 cm wide | sides 10 cm deep | walls 18 cm tall | peak 12 cm above the walls | dough 5 mm | glue: soft frosting | walls set 1 h before the roof | roof candy: heavy | on show 5 days in a steamy room | verdict: redesign\nrank 1 | glue-holds | risk: high | impact 3 x likelihood 3 x cheap to test 3 = 27 | soft frosting, walls set 1 h before the roof goes on\nrank 2 | walls-stand | risk: high | impact 3 x likelihood 3 x cheap to test 2 = 18 | 18 cm walls on 5 mm dough stand 36 times taller than thick\nrank 3 | roof-stays | risk: high | impact 3 x likelihood 3 x cheap to test 2 = 18 | roof at 67 degrees, each panel 13 cm from eave to peak, 26 times the dough thickness\nrank 4 | roof-load | risk: high | impact 2 x likelihood 3 x cheap to test 2 = 12 | heavy candy on the roof, 5 mm dough\nrank 5 | survives-display | risk: high | impact 2 x likelihood 3 x cheap to test 1 = 6 | 5 days on show in a steamy room\nrank 6 | pieces-fit | risk: low | impact 1 x likelihood 1 x cheap to test 3 = 3 | 6 panels on 2 trays",
 "notes": "a tall narrow tower with a steep roof, on the counter next to the kettle"
}

Reply:

{
 "task": "redteam",
 "verdict": "redesign",
 "headline": "This plan needs a redesign: 5 assumptions are under attack, starting with the glue.",
 "attacks": [
  {
   "assumption": "glue-holds",
   "risk": "high",
   "steelman": "The plan states no fact yet that the frosting would hold firm.",
   "attack": "Soft frosting never sets hard, yet the walls get only 1 h before the roof goes on.",
   "cheapest_test": "Glue 2 scraps together with the frosting, stand the joint on its end, and check firmness after 1 h.",
   "give_up_if": "If the frosting stays soft or breaks apart, switch the glue to royal icing instead.",
   "fix": "Replace the soft frosting with royal icing so the glue actually sets before the roof goes on."
  },
  {
   "assumption": "walls-stand",
   "risk": "high",
   "steelman": "Nothing in the plan yet gives a reason the walls would stay upright.",
   "attack": "At 36 times taller than thick, these walls pass the 22 wobble point and the 30 fall point already.",
   "cheapest_test": "Cut a spare strip at 5 mm thick and 18 cm tall, stand it on edge, and watch for 1 h.",
   "give_up_if": "If the strip wobbles or buckles, use thicker dough or lower the walls under 22 times taller than thick.",
   "fix": "Make the dough thicker or build the walls lower, keeping them under 22 times taller than thick."
  },
  {
   "assumption": "roof-stays",
   "risk": "high",
   "steelman": "No fact in the plan yet explains why the roof would stay put.",
   "attack": "At 67 degrees, this roof is already past the 40 degree creep point and the 50 degree slide point.",
   "cheapest_test": "Glue a spare roof panel to a board tilted to the roof's angle, then wait 1 h.",
   "give_up_if": "If the panel slides on the glue, lower the peak under 40 degrees and prop each panel until set.",
   "fix": "Lower the peak under 40 degrees, hold each roof panel 10 min, and prop it until set."
  },
  {
   "assumption": "roof-load",
   "risk": "high",
   "steelman": "The plan offers no stated fact yet that the roof could carry this candy.",
   "attack": "Heavy candy sits on only 5 mm roof dough, short of the 6 mm this candy needs.",
   "cheapest_test": "Set the planned heavy candy on a spare panel tilted to the roof angle and wait 1 h.",
   "give_up_if": "If the panel sags under the candy, move heavy pieces to the board or walls, or thicken to 6 mm.",
   "fix": "Put fewer heavy pieces on the roof, set the rest on the board or walls, or use 6 mm dough."
  },
  {
   "assumption": "survives-display",
   "risk": "high",
   "steelman": "Nothing stated yet explains why the house would survive the display days.",
   "attack": "5 days on show in a steamy room already passes the 3 day steamy room softening point.",
   "cheapest_test": "Leave a spare scrap of gingerbread where the house will stand, and check it the next morning.",
   "give_up_if": "If the scrap softens by morning, move the house away from the steam or cut the display days.",
   "fix": "Move the house to a drier spot away from the steam, or cut back the display days."
  }
 ],
 "closing": "Start by joining 2 dough scraps with the frosting and checking their firmness after 1 h."
}

Small starter house - build it, no attacks

Input:

{
 "task": "redteam",
 "plan": "house numbers: tray 38 x 28 cm, 3 or more trays is a long bake | each roof panel overhangs the walls by 1 cm | walls wobble past 22 times taller than thick and fall past 30 | roofs creep from 40 degrees and slide from 50 | a roof slant past 40 times the dough thickness sags | royal icing needs 2 h before the roof | soft frosting never sets hard | heavy candy needs 6 mm dough | gingerbread softens past 3 days in a steamy room, past 7 in an ordinary room, past 28 in a dry room | a test runs 1 h | hold each joint 10 min\nhouse | Little starter | front 14 cm wide | sides 10 cm deep | walls 8 cm tall | peak 4 cm above the walls | dough 6 mm | glue: royal icing | walls set 4 h before the roof | roof candy: none | on show 5 days in a dry room | verdict: build-it\nrank 1 | glue-holds | risk: low | impact 3 x likelihood 1 x cheap to test 3 = 9 | royal icing, walls set 4 h before the roof goes on\nrank 2 | walls-stand | risk: low | impact 3 x likelihood 1 x cheap to test 2 = 6 | 8 cm walls on 6 mm dough stand 13 times taller than thick\nrank 3 | roof-stays | risk: low | impact 3 x likelihood 1 x cheap to test 2 = 6 | roof at 39 degrees, each panel 6.5 cm from eave to peak, 11 times the dough thickness\nrank 4 | roof-load | risk: low | impact 2 x likelihood 1 x cheap to test 2 = 4 | no candy on the roof, 6 mm dough\nrank 5 | pieces-fit | risk: low | impact 1 x likelihood 1 x cheap to test 3 = 3 | 6 panels on 2 trays\nrank 6 | survives-display | risk: low | impact 2 x likelihood 1 x cheap to test 1 = 2 | 5 days on show in a dry room",
 "notes": "none"
}

Reply:

{
 "task": "redteam",
 "verdict": "build-it",
 "headline": "Build it - no part of the plan is at medium or high risk.",
 "attacks": [],
 "closing": "The plan is ready to build whenever you want to start your gingerbread house."
}

Big party centrepiece - test first, roof panels too big for the tray

Input:

{
 "task": "redteam",
 "plan": "house numbers: tray 38 x 28 cm, 3 or more trays is a long bake | each roof panel overhangs the walls by 1 cm | walls wobble past 22 times taller than thick and fall past 30 | roofs creep from 40 degrees and slide from 50 | a roof slant past 40 times the dough thickness sags | royal icing needs 2 h before the roof | soft frosting never sets hard | heavy candy needs 6 mm dough | gingerbread softens past 3 days in a steamy room, past 7 in an ordinary room, past 28 in a dry room | a test runs 1 h | hold each joint 10 min\nhouse | Party hall | front 38 cm wide | sides 24 cm deep | walls 14 cm tall | peak 10 cm above the walls | dough 8 mm | glue: melted sugar | walls set 0 h before the roof | roof candy: light | on show 21 days in a dry room | verdict: test-first\nrank 1 | roof-stays | risk: medium | impact 3 x likelihood 2 x cheap to test 2 = 12 | roof at 40 degrees, each panel 15.5 cm from eave to peak, 19 times the dough thickness\nrank 2 | glue-holds | risk: low | impact 3 x likelihood 1 x cheap to test 3 = 9 | melted sugar, walls set 0 h before the roof goes on\nrank 3 | pieces-fit | risk: high | impact 1 x likelihood 3 x cheap to test 3 = 9 | roof left, roof right bigger than the tray (40 x 16.5 cm)\nrank 4 | walls-stand | risk: low | impact 3 x likelihood 1 x cheap to test 2 = 6 | 14 cm walls on 8 mm dough stand 18 times taller than thick\nrank 5 | roof-load | risk: low | impact 2 x likelihood 1 x cheap to test 2 = 4 | light candy on the roof, 8 mm dough\nrank 6 | survives-display | risk: low | impact 2 x likelihood 1 x cheap to test 1 = 2 | 21 days on show in a dry room",
 "notes": "a wide centrepiece for the party table, joints glued with melted sugar"
}

Reply:

{
 "task": "redteam",
 "verdict": "test-first",
 "headline": "Test first: 2 parts of the plan are at risk, starting with whether the roof stays put.",
 "attacks": [
  {
   "assumption": "roof-stays",
   "risk": "medium",
   "steelman": "The roof carries light candy, not heavy weight pulling panels down the slope.",
   "attack": "At 40 degrees the roof reaches the creep point; its 19 times thickness stays under the 40 sag limit.",
   "cheapest_test": "Glue a spare roof panel onto a board tilted to 40 degrees with the melted sugar, and check it after 1 h.",
   "give_up_if": "If a spare panel slides or sags on the tilted board, lower the peak under 40 degrees instead.",
   "fix": "Lower the peak to under 40 degrees, and hold and prop each roof panel for 10 min before adding it."
  },
  {
   "assumption": "pieces-fit",
   "risk": "high",
   "steelman": "The hall's front panel is exactly 38 cm wide, matching the tray's own width precisely.",
   "attack": "The roof panels measure 40 by 16.5 cm, overshooting the 38 by 28 cm tray on the long side.",
   "cheapest_test": "Cut every panel from paper at its planned size and lay all of them together on the 38 by 28 cm tray.",
   "give_up_if": "If the paper roof panels do not fit the tray together, bake them in parts or build smaller.",
   "fix": "Bake the oversized roof panels in smaller parts and join them with the melted sugar, or build a smaller hall."
  }
 ],
 "closing": "Before anything else, glue a spare roof panel onto a board tilted to 40 degrees and watch it for 1 h."
}

Build the input yourself

Load the engine in node (global.window = global; require("./icingkit.js")), then:

const K = IcingKit;
const built = K.buildInput(K.form({ name: "Cottage", width: 18, depth: 12, height: 10, rise: 6, thick: 6,
  glue: "royal-icing", wait: 3, candy: "light", days: 10, room: "normal", notes: "on the sideboard" }));
built.input.plan;                       // the page's lines: numbers, the house line, 6 ranked assumptions
const wire = K.toWire(built.input);     // POST this to /estimate and /run
// then: const reply = K.normalize(K.parseReply(job.output.output));
// built.facts.verdict                  // the verdict the page's rule gives
// K.check(reply, built).notes          // the page's re-check of the reply
// K.md(built, reply, "Cottage")        // the attack as Markdown

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://load-bearing-icing.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"}
}

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":"load-bearing-icing"}' | jq -r '.data.token')

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}' 

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": "redteam", "plan": "house numbers: tray 38 x 28 cm | ...
house | Candy tower | front 12 cm wide | ... | verdict: redesign
rank 1 | glue-holds | risk: high | ...", "notes": "a tall narrow tower ..."}
# - see the field table
api POST /estimate "$(cat input.json)" | jq '.data | {hold_credits, min_credits, model, model_alias, markup_bps, warnings}' 

Step 5 — POST /run, then poll the job

Send an Idempotency-Key built from the lane and the input: a retry with the same key never bills twice. The page uses load-bearing-icing:<task>:<inputHash>:a<attempt> (the one task is redteam), 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="load-bearing-icing:redteam:$(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}' 

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: load-bearing-icing:redteam:$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json