← Head, Heart or Hands
API

Drive Head, Heart or Hands from your own code

The one metered task, read, is available over HTTP. It takes 1 to 6 everyday moments as the page's lines - what happened, which part was driving (head, heart, hands or not sure), the habit that showed, when it was noticed and a note - plus the totals, the verdict, the moment to look at first and the practice. It returns, for every moment, what it shows and a small cue to catch it next time, then a headline, a way to look again at the first moment, how to try the practice and a closing.

The division of labour first: a fixed rule on the page counts the moments, decides the verdict, picks the moment to look at first and picks the practice; the model only writes the words. After a run the page checks that the reply copied every one of those and kept to the house rules. A light self-observation exercise, not therapy or professional advice.

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":"head-heart-or-hands"}), 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, log) 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 written reading is 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 (the one task is read); a missing or different one is still answered as read. Presence per moment: in the moment 2, a bit later 1, only while writing 0. The verdict, checked in order: at least half the moments noticed in the moment - often-awake; moments noticed in the moment or a bit later make at least half - caught-later; otherwise - mostly-adrift. The moment to look at first is the lowest presence among moments whose habit is not thinking of what the other person needed, the earliest on a tie, none when every moment is that. The practice comes from the habit that shows most often (not counting that outward habit; the earliest on a tie): lost in it - stop-and-see; worried how I came across - turn-outward; played a scene in my head - come-back; let off steam - let-it-pass; none of these - feel-the-hand; every moment outward - feet-and-ears.

FieldRequiredWhat it holds
taskyesAlways read.
logyesThe page's lines: a house rules: line, a totals: line (counts, verdict, look first, practice), then 1 line per moment such as moment 2 | scrolled my phone through lunch | driving: hands (habit or body just did it) | habit: lost in it | noticed: a bit later | note: none.
retry_notenoOnly on the page's single reformat retry.

The reply is one JSON object:

{"task": "read", "verdict": "often-awake", "practice": "let-it-pass", "headline": "...", "items": [{"item": "1", "seen": "...", "cue": "..."}], "first": {"item": "2", "step": "..."}, "how": "...", "closing": "..."}
FieldWhat it holds
verdict, practiceCopied from the totals line.
headlineThe verdict in plain words with how many moments were noticed in the moment out of how many, at most 16 words.
items1 per moment in log order: item (its number), seen (who was driving, the habit and when it was noticed, at most 22 words) and cue (a small signal to catch it next time, at most 16 words).
firstitem (copied from the totals line, or empty) and step (a calm way to look at that moment again, at most 18 words).
howHow to try the practice, tied to a moment from the log, at most 26 words.
closingOne light sentence that agrees with the verdict, at most 20 words.

The page then checks the reply (DriveKit.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.

Snapped at the printer - often awake, a tie for the practice

Input:

{
 "task": "read",
 "log": "house rules: presence - in the moment 2, a bit later 1, only while writing this 0 | verdict checked in order: often-awake when at least half the moments were noticed in the moment, caught-later when moments noticed in the moment or a bit later make at least half, otherwise mostly-adrift | look first: the lowest presence among moments whose habit is not thinking of what the other person needed, the earliest on a tie, none when every moment is that | practice: from the habit that shows most often (not counting thinking of what the other person needed), the earliest on a tie - lost in it: stop-and-see, worried how I came across: turn-outward, played a scene in my head: come-back, let off steam: let-it-pass, none of these: feel-the-hand, every moment outward: feet-and-ears\ntotals: 4 moments | noticed in the moment: 3 | a bit later: 1 | only while writing this: 0 | verdict: often-awake | look first: moment 2 | practice: let-it-pass (when the urge to let off steam rises, notice it and let a breath pass before you speak)\nmoment 1 | snapped at the slow printer | driving: heart (a feeling took over) | habit: let off steam | noticed: in the moment | note: said it out loud to nobody\nmoment 2 | scrolled my phone through lunch | driving: hands (habit or body just did it) | habit: lost in it | noticed: a bit later | note: none\nmoment 3 | held the lift for a neighbour with boxes | driving: heart (a feeling took over) | habit: thought of what the other person needed | noticed: in the moment | note: none\nmoment 4 | rehearsed what to say to my landlord | driving: head (thinking it over) | habit: played a scene in my head | noticed: in the moment | note: same lines over and over"
}

Reply:

{
 "task": "read",
 "verdict": "often-awake",
 "practice": "let-it-pass",
 "headline": "Often awake: 3 out of 4 moments noticed in the moment.",
 "items": [
  {
   "item": "1",
   "seen": "Heart led the printer moment — you snapped at the slow printer, said it out loud to nobody, noticed in the moment.",
   "cue": "Notice heat rising in your chest before the words come out."
  },
  {
   "item": "2",
   "seen": "Hands were driving — scrolling your phone through lunch pulled you in, and you only noticed a bit later.",
   "cue": "Feel your thumb move on the screen before the scroll becomes automatic."
  },
  {
   "item": "3",
   "seen": "Heart led you to hold the lift for a neighbour with boxes, sensing what the neighbour needed, noticed in the moment.",
   "cue": "Notice the instant you spot someone nearby who might want help."
  },
  {
   "item": "4",
   "seen": "Head led this — rehearsing what to say to your landlord, the same lines running over and over, noticed in the moment.",
   "cue": "Notice your lips moving silently as the imagined conversation starts again."
  }
 ],
 "first": {
  "item": "2",
  "step": "Picture yourself scrolling your phone through lunch again, calmly noticing when attention slipped away."
 },
 "how": "Next time snapping at a slow printer stirs, catch the urge and hold still until the heat settles before speaking.",
 "closing": "Your attention was awake for most of these moments — let that warm noticing keep meeting whatever comes next."
}

Nodded through a meeting - caught it later

Input:

{
 "task": "read",
 "log": "house rules: presence - in the moment 2, a bit later 1, only while writing this 0 | verdict checked in order: often-awake when at least half the moments were noticed in the moment, caught-later when moments noticed in the moment or a bit later make at least half, otherwise mostly-adrift | look first: the lowest presence among moments whose habit is not thinking of what the other person needed, the earliest on a tie, none when every moment is that | practice: from the habit that shows most often (not counting thinking of what the other person needed), the earliest on a tie - lost in it: stop-and-see, worried how I came across: turn-outward, played a scene in my head: come-back, let off steam: let-it-pass, none of these: feel-the-hand, every moment outward: feet-and-ears\ntotals: 4 moments | noticed in the moment: 1 | a bit later: 2 | only while writing this: 1 | verdict: caught-later | look first: moment 3 | practice: stop-and-see (pause in the middle of something and notice how you are standing or sitting)\nmoment 1 | nodded along in the meeting without listening | driving: hands (habit or body just did it) | habit: lost in it | noticed: a bit later | note: none\nmoment 2 | replayed a joke that fell flat | driving: head (thinking it over) | habit: worried how I came across | noticed: a bit later | note: kept wincing on the bus\nmoment 3 | ate biscuits standing at the counter | driving: hands (habit or body just did it) | habit: none of these | noticed: only while writing this | note: none\nmoment 4 | asked a friend how the new job is going | driving: heart (a feeling took over) | habit: thought of what the other person needed | noticed: in the moment | note: none"
}

Reply:

{
 "task": "read",
 "verdict": "caught-later",
 "practice": "stop-and-see",
 "headline": "Caught it later - 1 of 4 moments noticed in the moment.",
 "items": [
  {
   "item": "1",
   "seen": "Hands did the nodding in the meeting, attention wasn't there, and you only noticed it a bit later, carried along without listening.",
   "cue": "Feel your chin start to dip before you've actually heard a word."
  },
  {
   "item": "2",
   "seen": "Head kept replaying a joke that fell flat, turning over how it looked, noticed a bit later, still wincing on the bus.",
   "cue": "Catch the replay starting, a conversation looping back uninvited in your mind."
  },
  {
   "item": "3",
   "seen": "Hands took over - you ate biscuits standing at the counter, noticed only while writing the log, nothing more to note.",
   "cue": "Feel a biscuit reach your mouth before you've decided to take it."
  },
  {
   "item": "4",
   "seen": "Heart led you to ask a friend about the new job, attention on what might be needed, noticed in the moment.",
   "cue": "Notice that quiet pause when curiosity about a friend rises before you speak."
  }
 ],
 "first": {
  "item": "3",
  "step": "Picture standing at the counter with the biscuits again, calmly noticing what your hands were doing."
 },
 "how": "Next time you catch yourself nodding along without listening, pause right there and notice how you are sitting or standing.",
 "closing": "A later catch still counts for something; there's no need to rush yourself next time."
}

A day on autopilot, a not-sure driver and a tie for the moment to look at first - mostly adrift

Input:

{
 "task": "read",
 "log": "house rules: presence - in the moment 2, a bit later 1, only while writing this 0 | verdict checked in order: often-awake when at least half the moments were noticed in the moment, caught-later when moments noticed in the moment or a bit later make at least half, otherwise mostly-adrift | look first: the lowest presence among moments whose habit is not thinking of what the other person needed, the earliest on a tie, none when every moment is that | practice: from the habit that shows most often (not counting thinking of what the other person needed), the earliest on a tie - lost in it: stop-and-see, worried how I came across: turn-outward, played a scene in my head: come-back, let off steam: let-it-pass, none of these: feel-the-hand, every moment outward: feet-and-ears\ntotals: 5 moments | noticed in the moment: 1 | a bit later: 1 | only while writing this: 3 | verdict: mostly-adrift | look first: moment 1 | practice: come-back (when a scene starts playing in your head, notice what you can see and hear around you)\nmoment 1 | planned an argument in the shower | driving: head (thinking it over) | habit: played a scene in my head | noticed: only while writing this | note: won it, too\nmoment 2 | checked my phone at every pause in the queue | driving: hands (habit or body just did it) | habit: none of these | noticed: only while writing this | note: none\nmoment 3 | felt stung when the group chat went quiet | driving: heart (a feeling took over) | habit: worried how I came across | noticed: a bit later | note: none\nmoment 4 | lost the thread of the film in a daydream | driving: not sure | habit: played a scene in my head | noticed: only while writing this | note: cannot say what happened in it\nmoment 5 | stopped to watch rain on the window | driving: heart (a feeling took over) | habit: none of these | noticed: in the moment | note: none"
}

Reply:

{
 "task": "read",
 "verdict": "mostly-adrift",
 "practice": "come-back",
 "headline": "Mostly adrift, 1 of 5 moments noticed in the moment.",
 "items": [
  {
   "item": "1",
   "seen": "Head was driving as you planned an argument in the shower, replaying it and winning there too; noticed only while writing this.",
   "cue": "Notice your eyes settle on nothing as the imagined conversation starts running."
  },
  {
   "item": "2",
   "seen": "Hands were driving as you checked your phone at every pause in the queue, noticed only while writing this.",
   "cue": "Feel your hand reach for your phone before you've decided to look."
  },
  {
   "item": "3",
   "seen": "Heart was driving as you felt stung when the group chat went quiet, then worried how you seemed; noticed a bit later.",
   "cue": "Notice a tightness in your chest right as the chat goes quiet."
  },
  {
   "item": "4",
   "seen": "Driver unclear, you lost the film's thread in a daydream, picturing a scene you can't recall, noticed only while writing this.",
   "cue": "Notice your gaze drift as a daydream starts pulling you away from the film."
  },
  {
   "item": "5",
   "seen": "Heart was driving as you stopped to watch rain on the window, noticed in the moment.",
   "cue": "Feel your shoulders ease as rain on the window catches your eye."
  }
 ],
 "first": {
  "item": "1",
  "step": "Picture the shower argument again, calmly noticing how the imagined scene played out."
 },
 "how": "Next time an imagined argument starts running, like the shower scene, pause and tune into the real sounds and sights around you.",
 "closing": "This round landed mostly adrift, and that's okay - simply noticing any of this is a start."
}

Listening well - every moment outward, so no moment to look at first

Input:

{
 "task": "read",
 "log": "house rules: presence - in the moment 2, a bit later 1, only while writing this 0 | verdict checked in order: often-awake when at least half the moments were noticed in the moment, caught-later when moments noticed in the moment or a bit later make at least half, otherwise mostly-adrift | look first: the lowest presence among moments whose habit is not thinking of what the other person needed, the earliest on a tie, none when every moment is that | practice: from the habit that shows most often (not counting thinking of what the other person needed), the earliest on a tie - lost in it: stop-and-see, worried how I came across: turn-outward, played a scene in my head: come-back, let off steam: let-it-pass, none of these: feel-the-hand, every moment outward: feet-and-ears\ntotals: 2 moments | noticed in the moment: 1 | a bit later: 1 | only while writing this: 0 | verdict: often-awake | look first: none | practice: feet-and-ears (while you listen to someone, keep a little attention on your feet)\nmoment 1 | let my neighbour tell a long story | driving: heart (a feeling took over) | habit: thought of what the other person needed | noticed: in the moment | note: did not check the time\nmoment 2 | let a colleague finish before answering | driving: not sure | habit: thought of what the other person needed | noticed: a bit later | note: none"
}

Reply:

{
 "task": "read",
 "verdict": "often-awake",
 "practice": "feet-and-ears",
 "headline": "Often awake: 1 of 2 moments caught right in the moment.",
 "items": [
  {
   "item": "1",
   "seen": "Heart drove it: letting your neighbour tell a long story, attention on what was needed, in the moment, time unchecked.",
   "cue": "Feel your shoulders ease toward whoever is speaking."
  },
  {
   "item": "2",
   "seen": "Which part was driving stays unclear: letting a colleague finish before answering, someone else's needs kept in mind, noticed a bit later.",
   "cue": "Notice the urge to jump in before the finish."
  }
 ],
 "first": {
  "item": "",
  "step": ""
 },
 "how": "When a story like your neighbour's is unfolding, try keeping some attention on your feet while you listen.",
 "closing": "Lovely to catch yourself like this, keep meeting these small moments with the same light curiosity."
}

Build the input yourself

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

const K = DriveKit;
const built = K.buildInput(K.form({ items: [
  { text: "skipped the queue chat and stared at my phone", driver: "hands", habit: "lost", noticed: "later", note: "" },
  { text: "bristled at a curt email", driver: "heart", habit: "steam", noticed: "now", note: "typed a reply, then deleted it" }] }));
built.input.log;                        // the page's lines: rules, totals and one line per moment
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, "my log")         // the reading 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://head-heart-or-hands.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":"head-heart-or-hands"}' | 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", "log": "house rules: presence - in the moment 2, a bit later 1, only while writing this 0 ...
totals: 4 moments | noticed in the moment: 1 | a bit later: 2 | only while writing this: 1 | verdict: caught-later | look first: moment 3 | practice: stop-and-see (...)
moment 1 | nodded along in the meeting without listening | driving: hands (habit or body just did it) | habit: lost in it | noticed: a bit later | note: none
..."}
# - 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 head-heart-or-hands:<task>:<inputHash>:a<attempt> (the one task is read), 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="head-heart-or-hands:read:$(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: head-heart-or-hands:read:$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json