← Aura Print
API

Drive Aura Print from your own code

The one metered task, read-the-aura, is available over HTTP. It takes one everyday moment, what the person did, what they were going for and five cue ratings (each strong, present, missing, forced or not sure), and returns a rating and a note for every cue, a reading, a caption for the print and one quieter move for next time.

The division of labour first: the points are fixed (strong 30, present 10, missing 0, forced -20) and the reading follows a fixed order from the reply's own ratings. The run keeps every rating you set and chooses one only for cues marked not sure.

The token is minted for this app (the guest call takes {"slug":"aura-print"}), 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, moment, move, aim, cues) 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 reading 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 readings

Every run sends task: read-the-aura (the one task is read-the-aura); a missing or different one is still answered as read-the-aura. All fields are strings.

FieldRequiredWhat it holds
taskyesAlways read-the-aura.
momentyesWhat was going on, in the person's words (the page sends at most 280 characters, cut at a word break).
moveyesWhat the person did or said (at most 240 characters).
aimyesWhat they were going for, or not given.
cuesyesFive numbered lines, always in this order: 1. read-the-room | rating: not sure, then own-voice, kind-edge, fresh-material, timing. The rating is strong, present, missing, forced or not sure.
retry_notenoOnly for a reformat retry: why the previous reply was unusable.

The reply is one JSON object:

{"verdict": "aura-gained", "headline": "...", "cues": [{"cue": "read-the-room", "rating": "strong", "points": 30, "note": "..."}, ...], "quiet_move": "...", "caption": "...", "last_word": "..."}
FieldWhat it holds
verdictFirst that applies: aura-debt (total below 0), aura-farmed (total 100 or more and no cue forced), aura-gained (total 40 or more), aura-even. The total is the sum of the five cues' points.
headlineA plain reading of the move with the signed total in digits (at most 26 words).
cuesFive objects in the input's order: cue (the id), rating (a rating you set is copied; for not sure it is judged from the words, and the lower rating wins a tie), points, note (at most 18 words).
quiet_moveOne quieter, kinder or quicker way to handle a similar moment, aimed at the cue with the fewest points (a tie goes to the earlier cue), at most 30 words.
captionA title for the print from the input's own nouns (at most 8 words).
last_wordOne line that agrees with the reading (at most 18 words).

The page then checks the reply (AuraKit.check) and lists any disagreement under "The page's check". Worked examples, one per reading:

The dropped soup - +150 aura, 3 ratings set by you: aura farmed

Input:

{
 "task": "read-the-aura",
 "moment": "Lunch rush in the office canteen. My tray slipped and a bowl of soup hit the floor right beside the queue for the till.",
 "move": "I set the tray down, said \"well, that's my soup tasted\", fetched paper towels from the counter and wiped it up before anyone else had to.",
 "aim": "Nothing much - just getting it cleaned up.",
 "cues": "1. read-the-room | rating: strong\n2. own-voice | rating: not sure\n3. kind-edge | rating: strong\n4. fresh-material | rating: not sure\n5. timing | rating: strong"
}

Reply:

{
 "verdict": "aura-farmed",
 "headline": "Spilling the soup and clearing it up fast, with a light touch throughout, lands a strong +150.",
 "cues": [
  {
   "cue": "read-the-room",
   "rating": "strong",
   "points": 30,
   "note": "Soup spilled beside the till queue during the lunch rush, and clearing it up matched that exactly."
  },
  {
   "cue": "own-voice",
   "rating": "strong",
   "points": 30,
   "note": "Setting the tray down and clearing up stayed plain and understated, with nothing suggesting a performance."
  },
  {
   "cue": "kind-edge",
   "rating": "strong",
   "points": 30,
   "note": "Wiping up the spill before anyone else had to help kept the joke only on themself."
  },
  {
   "cue": "fresh-material",
   "rating": "strong",
   "points": 30,
   "note": "The quip names the soup itself, a specific detail from what had just happened, not a stock line."
  },
  {
   "cue": "timing",
   "rating": "strong",
   "points": 30,
   "note": "The spill was dealt with right away, with paper towels fetched and everything wiped up without delay."
  }
 ],
 "quiet_move": "Pause to glance at how busy the queue is before clearing a spill, keeping your reading the room sharp.",
 "caption": "Tray, Soup, and the Lunch Rush Queue",
 "last_word": "The whole clean-up came across as effortless, nothing about it looked like hard work."
}

The forgotten snacks - +80 aura, 3 ratings set by you: aura gained

Input:

{
 "task": "read-the-aura",
 "moment": "Book club at Dana's flat. Dana realised she had forgotten to buy snacks and kept apologising while we took our coats off.",
 "move": "I said I'd been meaning to try the corner shop's crisps anyway, popped out to get some, and asked who had finished the book.",
 "aim": "Help Dana stop feeling bad.",
 "cues": "1. read-the-room | rating: not sure\n2. own-voice | rating: present\n3. kind-edge | rating: not sure\n4. fresh-material | rating: present\n5. timing | rating: missing"
}

Reply:

{
 "verdict": "aura-gained",
 "headline": "Fetching snacks and asking about the book redirected Dana's apology into something easy and generous. +80.",
 "cues": [
  {
   "cue": "read-the-room",
   "rating": "strong",
   "points": 30,
   "note": "Dana had forgotten snacks and kept apologising; getting crisps answered exactly what was happening."
  },
  {
   "cue": "own-voice",
   "rating": "present",
   "points": 10,
   "note": "Saying you'd been meaning to try the crisps anyway sounds like an ordinary, natural remark."
  },
  {
   "cue": "kind-edge",
   "rating": "strong",
   "points": 30,
   "note": "Saying you'd been meaning to try the crisps covered for Dana while you fetched snacks."
  },
  {
   "cue": "fresh-material",
   "rating": "present",
   "points": 10,
   "note": "Buying crisps from the corner shop is a natural choice, not pulled from a stock line."
  },
  {
   "cue": "timing",
   "rating": "missing",
   "points": 0,
   "note": "The words don't say how quickly you left or returned, or how long it took."
  }
 ],
 "quiet_move": "Keep the errand brief and mention it in passing so the timing stays tight next time.",
 "caption": "Dana's Snacks and Corner Shop Crisps",
 "last_word": "This finished net positive and didn't cost Dana anything in the moment."
}

The fun-fact circle - +20 aura, 3 ratings set by you: aura even

Input:

{
 "task": "read-the-aura",
 "moment": "First evening of a pottery class. The teacher asked each of us to share a fun fact about ourselves.",
 "move": "I gave the fact I always use at work introductions, about owning too many houseplants, and rushed it because I was nervous.",
 "aim": "Just to get through it.",
 "cues": "1. read-the-room | rating: present\n2. own-voice | rating: present\n3. kind-edge | rating: not sure\n4. fresh-material | rating: forced\n5. timing | rating: not sure"
}

Reply:

{
 "verdict": "aura-even",
 "headline": "Reusing your usual work fact under nerves nets +20: fits the moment fine but leans on an old line instead of something new.",
 "cues": [
  {
   "cue": "read-the-room",
   "rating": "present",
   "points": 10,
   "note": "Sharing a fun fact when the teacher asked fits what the moment called for."
  },
  {
   "cue": "own-voice",
   "rating": "present",
   "points": 10,
   "note": "Giving the fact you always use at work sounds like your own easy answer, not a performance."
  },
  {
   "cue": "kind-edge",
   "rating": "present",
   "points": 10,
   "note": "Sharing a houseplant fact about yourself doesn't take anything away from anyone else in the class."
  },
  {
   "cue": "fresh-material",
   "rating": "forced",
   "points": -20,
   "note": "The houseplant fact is the one you always use at work, not made for this class."
  },
  {
   "cue": "timing",
   "rating": "present",
   "points": 10,
   "note": "Giving the fact right when asked, though rushed from nerves, still answered in time."
  }
 ],
 "quiet_move": "Notice one small, real detail from the pottery class itself, and offer that as your fresh material next time instead of your usual work fact.",
 "caption": "Pottery Class Houseplant Fact",
 "last_word": "This one roughly balanced out: it fit the moment but leaned on a fact used elsewhere while nervous."
}

The leaving-do roast - -100 aura, 1 rating set by you: aura debt

Input:

{
 "task": "read-the-aura",
 "moment": "Leaving drinks for Sam at the pub after work. People were taking turns to say a few kind words.",
 "move": "I read a roast from my phone that I had written the night before, mostly about the spreadsheet mistake Priya made last spring, and kept going after the table went quiet.",
 "aim": "To be the funny one everyone talks about afterwards.",
 "cues": "1. read-the-room | rating: not sure\n2. own-voice | rating: not sure\n3. kind-edge | rating: forced\n4. fresh-material | rating: not sure\n5. timing | rating: not sure"
}

Reply:

{
 "verdict": "aura-debt",
 "headline": "Reading a prepared roast about Priya's old spreadsheet mistake during Sam's leaving drinks, then continuing after the table went quiet, totals -100.",
 "cues": [
  {
   "cue": "read-the-room",
   "rating": "forced",
   "points": -20,
   "note": "People were saying kind words, but the roast kept going after the table went quiet."
  },
  {
   "cue": "own-voice",
   "rating": "forced",
   "points": -20,
   "note": "Written the night before and aimed at being the funny one afterwards, not a spontaneous voice."
  },
  {
   "cue": "kind-edge",
   "rating": "forced",
   "points": -20,
   "note": "The roast singled out Priya's old mistake instead of helping or crediting anyone at the table."
  },
  {
   "cue": "fresh-material",
   "rating": "forced",
   "points": -20,
   "note": "The roast was written the night before, a prepared line rather than material made for this event."
  },
  {
   "cue": "timing",
   "rating": "forced",
   "points": -20,
   "note": "The roast kept going after the table went quiet, instead of stopping once that happened."
  }
 ],
 "quiet_move": "Read the room before you start, and offer Sam a few kind words instead of a prepared story about someone else's old mistake.",
 "caption": "The Roast That Replaced the Kind Words",
 "last_word": "This one cost more aura than it earned, plain and simple."
}

Build the input yourself

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

const K = AuraKit;
const built = K.buildInput(K.form({
  moment: "The printer jammed during the team meeting and the room went quiet.",
  move: "I opened the tray, pulled the page out and handed it round.",
  aim: "", ratings: { timing: "strong" } }));
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, "my moment")      // 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://aura-print.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":"aura-print"}' | 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-the-aura", "moment": "...", "move": "...", "aim": "not given", "cues": "1. read-the-room | rating: not sure\n2. ..."}
# - 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 aura-print:<task>:<inputHash>:a<attempt> (the one task is read-the-aura), 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="aura-print:read-the-aura:$(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: aura-print:read-the-aura:$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json