← Chrome & Grime
API

Drive Chrome & Grime from your own code

The one metered task, read-moments, is available over HTTP. It takes 1 to 6 ordinary moments, each with a name, an optional line on what happened, and an answer (No, A little or Clearly) for each of six cues - three for chrome (high tech) and three for grime (low life). It returns a plain-English reading per moment, the flip that would move it into or out of full neon, and a short sign lettered from the moment's own words.

The division of labour first: the page decides; the model explains. The page adds up each moment's chrome (0-6) and grime (0-6), picks its verdict, its strongest cue, its flip and the brightest sign before the run, and sends them in facts. The model never changes them, and the page re-checks the reply against them.

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":"chrome-and-grime"}), 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, moments, facts) 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: the reasons are metered and need 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: read-moments (the one task is read-moments); a missing or different one is still answered as read-moments. All fields are strings.

FieldRequiredWhat it holds
taskyesAlways read-moments.
momentsyesOne numbered line per moment (1 to 6): name | what happened: ... | A screen or scanner was in the middle of it: ... | A system made the call: ... | Tech rode on your body: ... | Something was broken or makeshift: ... | You worked round the rules: ... | It was cheap, crowded or after dark: ..., each answer No, A little or Clearly.
factsyesThe page's lines: per moment its chrome and grime, its verdict, its strongest cue and its flip (cues moved from one answer to another, and the verdict it would land in); then the verdict counts and the brightest sign.
retry_notenoOnly on the page's one reformat retry.

Each answer is worth No 0, A little 1, Clearly 2. The verdict, first match wins: chrome 3+ and grime 3+ is neon (full neon); chrome 3+ is gloss (chrome gloss); grime 3+ is alley (back alley); otherwise unlit. The strongest cue is the highest-scoring cue (ties to the earlier cue); the brightest sign is the full-neon moment with the most points in all (ties to the moment higher on the list), or none.

The reply

{"task": "read-moments", "headline": "...", "moments": [{"name": "...", "verdict": "gloss", "reading": "...", "flip": "...", "sign": "..."}], "closing": "..."}
FieldWhat it holds
headlineAt most 22 words summing up the street; counts match facts.
moments[]One per input moment, same order and exact names: verdict copied from facts, reading (at most 50 words, from that moment's own answers, naming its strongest cue), flip (one "If ... would" sentence for the page's flip, ending with the verdict it lands in) and sign (two to five words from the moment's own nouns).
closingOne sentence naming the brightest sign (or saying no moment reached full neon).

The page then checks the reply (GrimeKit.check) - each verdict against its own, every moment present, a flip and a sign for each, time, season or weather words you did not give, contact details, dark warnings, and named books, films or games - and lists anything it finds under "The page's check".

Worked examples - all four verdicts

These are the 3 free samples on the page: the exact input the page sends and the reply that replays for free. Together they cover all four verdicts.

The commute - verdicts: full neon, chrome gloss, back alley, unlit street

Input:

{
 "task": "read-moments",
 "moments": "1. Tapped through the ticket gate with my phone | what happened: the gate beeped twice before it opened | A screen or scanner was in the middle of it: Clearly | A system made the call: A little | Tech rode on your body: No | Something was broken or makeshift: No | You worked round the rules: No | It was cheap, crowded or after dark: A little\n2. Fixed my bike chain with a hair tie | what happened: it held for the rest of the ride | A screen or scanner was in the middle of it: No | A system made the call: No | Tech rode on your body: No | Something was broken or makeshift: Clearly | You worked round the rules: A little | It was cheap, crowded or after dark: No\n3. Late bus with earbuds in | what happened: the timetable screen was blank so I asked the driver | A screen or scanner was in the middle of it: A little | A system made the call: No | Tech rode on your body: Clearly | Something was broken or makeshift: A little | You worked round the rules: Clearly | It was cheap, crowded or after dark: Clearly\n4. Walked past the bakery | what happened: not given | A screen or scanner was in the middle of it: No | A system made the call: No | Tech rode on your body: No | Something was broken or makeshift: No | You worked round the rules: No | It was cheap, crowded or after dark: No",
 "facts": "1. Tapped through the ticket gate with my phone: chrome 3 of 6; grime 1 of 6; verdict gloss; strongest cue: A screen or scanner was in the middle of it; into neon: It was cheap, crowded or after dark from A little to Clearly and Something was broken or makeshift from No to A little (2 points more), landing in neon\n2. Fixed my bike chain with a hair tie: chrome 0 of 6; grime 3 of 6; verdict alley; strongest cue: Something was broken or makeshift; into neon: A screen or scanner was in the middle of it from No to Clearly and A system made the call from No to A little (3 points more), landing in neon\n3. Late bus with earbuds in: chrome 3 of 6; grime 5 of 6; verdict neon; strongest cue: Tech rode on your body; out of neon: A screen or scanner was in the middle of it from A little to No (1 point fewer), landing in alley\n4. Walked past the bakery: chrome 0 of 6; grime 0 of 6; verdict unlit; strongest cue: none; into neon: A screen or scanner was in the middle of it from No to Clearly and A system made the call from No to A little and Something was broken or makeshift from No to Clearly and You worked round the rules from No to A little (6 points more), landing in neon\ncounts: neon 1, gloss 1, alley 1, unlit 1 (of 4 moments)\nbrightest sign: Late bus with earbuds in"
}

Reply:

{
 "task": "read-moments",
 "headline": "This street shows one full neon moment, one chrome gloss, one back alley and one unlit, each its own shade.",
 "moments": [
  {
   "name": "Tapped through the ticket gate with my phone",
   "verdict": "gloss",
   "reading": "You answered that a screen or scanner was clearly in the middle of it, the gate beeping before it opened, and that it was only a little cheap, crowded or after dark, so the chrome shines while the grime stays faint for chrome gloss.",
   "flip": "If it had been clearly cheap, crowded or after dark, and something had been a little broken or makeshift, this would land in full neon.",
   "sign": "Ticket gate phone"
  },
  {
   "name": "Fixed my bike chain with a hair tie",
   "verdict": "alley",
   "reading": "You answered that something was clearly broken or makeshift, and it held for the rest of the ride, while a screen or scanner was no part of it, so the grit carries this one to back alley.",
   "flip": "If a screen or scanner had clearly been in the middle of it, and a system had made the call a little, this moment would land in full neon.",
   "sign": "Bike chain hair tie"
  },
  {
   "name": "Late bus with earbuds in",
   "verdict": "neon",
   "reading": "You answered that tech clearly rode on your body, and that you clearly worked round the rules by asking the driver once the timetable screen was blank, so both sides came through strong for full neon.",
   "flip": "If a screen or scanner had not been in the middle of it at all, this moment would land in back alley.",
   "sign": "Bus and earbuds"
  },
  {
   "name": "Walked past the bakery",
   "verdict": "unlit",
   "reading": "You answered that a screen or scanner was no part of it, and that it was not cheap, crowded or after dark either, so this moment kept both sides quiet and stayed unlit.",
   "flip": "If a screen or scanner had been clearly in the middle of it, a system had slightly made the call, something had been clearly broken or makeshift, and you had slightly worked round the rules, this would be full neon.",
   "sign": "Bakery walk"
  }
 ],
 "closing": "Late bus with earbuds in lights up the brightest on this street."
}

Errands - verdicts: full neon, chrome gloss, unlit street

Input:

{
 "task": "read-moments",
 "moments": "1. Self-checkout flagged my apples | what happened: an assistant came over and cleared it | A screen or scanner was in the middle of it: Clearly | A system made the call: Clearly | Tech rode on your body: No | Something was broken or makeshift: No | You worked round the rules: A little | It was cheap, crowded or after dark: No\n2. Paid for noodles with my watch | what happened: the card reader at the stall was taped together | A screen or scanner was in the middle of it: Clearly | A system made the call: No | Tech rode on your body: Clearly | Something was broken or makeshift: Clearly | You worked round the rules: No | It was cheap, crowded or after dark: Clearly\n3. Posted a parcel at the counter | what happened: the clerk weighed it by hand | A screen or scanner was in the middle of it: No | A system made the call: No | Tech rode on your body: No | Something was broken or makeshift: No | You worked round the rules: No | It was cheap, crowded or after dark: A little",
 "facts": "1. Self-checkout flagged my apples: chrome 4 of 6; grime 1 of 6; verdict gloss; strongest cue: A screen or scanner was in the middle of it; into neon: You worked round the rules from A little to Clearly and Something was broken or makeshift from No to A little (2 points more), landing in neon\n2. Paid for noodles with my watch: chrome 4 of 6; grime 4 of 6; verdict neon; strongest cue: A screen or scanner was in the middle of it; out of neon: A screen or scanner was in the middle of it from Clearly to No (2 points fewer), landing in alley\n3. Posted a parcel at the counter: chrome 0 of 6; grime 1 of 6; verdict unlit; strongest cue: It was cheap, crowded or after dark; into neon: A screen or scanner was in the middle of it from No to Clearly and A system made the call from No to A little and It was cheap, crowded or after dark from A little to Clearly and Something was broken or makeshift from No to A little (5 points more), landing in neon\ncounts: neon 1, gloss 1, alley 0, unlit 1 (of 3 moments)\nbrightest sign: Paid for noodles with my watch"
}

Reply:

{
 "task": "read-moments",
 "headline": "This handful of moments shows full neon, chrome gloss and unlit, each striking its own balance.",
 "moments": [
  {
   "name": "Self-checkout flagged my apples",
   "verdict": "gloss",
   "reading": "You get chrome gloss here: a screen or scanner was clearly in the middle of it, and something being broken or makeshift was a no, so the tech side leads while the scrappy side stays quiet.",
   "flip": "If you had clearly worked round the rules and something had been a little broken or makeshift, this moment would land in full neon.",
   "sign": "Self-checkout apples assistant"
  },
  {
   "name": "Paid for noodles with my watch",
   "verdict": "neon",
   "reading": "You answered clearly that a screen or scanner was in the middle of it, and the taped-together card reader at the stall shows something was clearly broken or makeshift, so both the tech and the scrappy side shine here in full neon.",
   "flip": "If a screen or scanner had not been at all in the middle of it, this moment would land in back alley.",
   "sign": "Noodles watch card reader"
  },
  {
   "name": "Posted a parcel at the counter",
   "verdict": "unlit",
   "reading": "This one stays unlit: a screen or scanner was not in the middle of it, and the only touch on the scrappy side was it being a little cheap, crowded or after dark, too little to tip the balance.",
   "flip": "If a screen or scanner had clearly scanned it, a system had decided a little, it had clearly been cheap, crowded or after dark, and something had been a little broken or makeshift, this would land in full neon.",
   "sign": "Parcel at the counter"
  }
 ],
 "closing": "Paid for noodles with my watch shines brightest among them."
}

A night out - verdicts: full neon, chrome gloss, back alley

Input:

{
 "task": "read-moments",
 "moments": "1. Queue for the club | what happened: a bouncer scanned my ticket on a cracked phone | A screen or scanner was in the middle of it: Clearly | A system made the call: A little | Tech rode on your body: No | Something was broken or makeshift: Clearly | You worked round the rules: No | It was cheap, crowded or after dark: Clearly\n2. Kebab van after midnight | what happened: cash only, so I hunted for the one cash machine that worked | A screen or scanner was in the middle of it: No | A system made the call: No | Tech rode on your body: No | Something was broken or makeshift: A little | You worked round the rules: Clearly | It was cheap, crowded or after dark: Clearly\n3. Ride home booked on an app | what happened: the app picked the route and the price | A screen or scanner was in the middle of it: Clearly | A system made the call: Clearly | Tech rode on your body: No | Something was broken or makeshift: No | You worked round the rules: No | It was cheap, crowded or after dark: A little",
 "facts": "1. Queue for the club: chrome 3 of 6; grime 4 of 6; verdict neon; strongest cue: A screen or scanner was in the middle of it; out of neon: A system made the call from A little to No (1 point fewer), landing in alley\n2. Kebab van after midnight: chrome 0 of 6; grime 5 of 6; verdict alley; strongest cue: You worked round the rules; into neon: A screen or scanner was in the middle of it from No to Clearly and A system made the call from No to A little (3 points more), landing in neon\n3. Ride home booked on an app: chrome 4 of 6; grime 1 of 6; verdict gloss; strongest cue: A screen or scanner was in the middle of it; into neon: It was cheap, crowded or after dark from A little to Clearly and Something was broken or makeshift from No to A little (2 points more), landing in neon\ncounts: neon 1, gloss 1, alley 1, unlit 0 (of 3 moments)\nbrightest sign: Queue for the club"
}

Reply:

{
 "task": "read-moments",
 "headline": "This street flickers between full neon, chrome gloss and back alley scenes.",
 "moments": [
  {
   "name": "Queue for the club",
   "verdict": "neon",
   "reading": "Here it's full neon: you answered clearly that a screen or scanner was in the middle of it, as the bouncer scanned your ticket, and you answered clearly that something was broken or makeshift, with that cracked phone keeping things scrappy too.",
   "flip": "If a system had not made the call at all, this would land in back alley.",
   "sign": "Club queue ticket"
  },
  {
   "name": "Kebab van after midnight",
   "verdict": "alley",
   "reading": "Here it's back alley: you answered clearly that you worked round the rules, hunting down a cash machine when the van was cash only, and you answered no to any screen or scanner being part of it, so no chrome crept in.",
   "flip": "If a screen or scanner had clearly been in the middle of it, and a system had made the call a little, this would land in full neon.",
   "sign": "Midnight kebab van"
  },
  {
   "name": "Ride home booked on an app",
   "verdict": "gloss",
   "reading": "Here it's chrome gloss: you answered clearly that a screen or scanner was in the middle of it, and that a system made the call, as the app picked your route and price, while it was only a little cheap, crowded or after dark, keeping the grime faint.",
   "flip": "If it had clearly been cheap, crowded or after dark, and something had been a little broken or makeshift, this would land in full neon.",
   "sign": "Ride home app"
  }
 ],
 "closing": "Queue for the club shines brightest along this street."
}

Build the input yourself

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

const K = GrimeKit;
const form = K.form({ moments: [
  { name: "Charged my phone at a cafe", note: "the socket was held in with tape", screen: "some", system: "no", worn: "no", patched: "yes", workaround: "no", street: "some" },
  { name: "Lift with a touch screen", note: "", screen: "yes", system: "some", worn: "no", patched: "no", workaround: "no", street: "no" }] });
const built = K.buildInput(form);       // built.facts holds each moment's chrome, grime, verdict, strongest cue and flip
const wire = K.toWire(built.input);     // POST this to /estimate and /run
// then: const reply = K.normalize(K.parseReply(job.output.output));
// K.check(reply, built).notes          // the page's re-check of the reply
// K.md(built, reply, K.title(form))    // the street as Markdown

Each cue answer is "no", "some" (A little) or "yes" (Clearly); the cue keys are screen, system, worn, patched, workaround and street.

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://chrome-and-grime.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":"chrome-and-grime"}' | 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": "read-moments", "moments": "1. Paid for noodles with my watch | what happened: ... | ...", "facts": "...", ...}
# - 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 chrome-and-grime:<task>:<inputHash>:a<attempt> (the one task is read-moments), 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="chrome-and-grime:read-moments:$(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: chrome-and-grime:read-moments:$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json