Run Cash Trough from your own code
Both metered lanes are available over HTTP and each returns one JSON object. The division of labour: the page splits the lines, costs the three tiers with a break-even each, schedules the cash and finds its lowest point, and prices the cut order - all free, in budgetkit.js. The run writes the budget or the shortfall plan; the page then holds the reply to its own numbers (budgetreply.js).
Base URL and the response shape
Every endpoint lives under https://api.skillsafe.ai/v1/app-api, and every response has one shape:
{ "ok": true, "data": { ... } }
{ "ok": false, "error": { "code": "...", "message": "..." } }
The token is minted for this app (the guest call takes {"slug":"cash-trough"}), 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 key (task, event, lines, facts) 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: both lanes are metered and need a personal sign-in. |
| 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 comes first
Two lanes - budget (the model to validate) and shortfall (the cut order and the note to sponsors; needs a plan at Better or Best). A missing or unknown task is answered as budget. Both lanes send the same four fields.
The input
| Field | Required | What |
|---|---|---|
task | yes | budget or shortfall. If it is missing or neither, do budget and set "task": "budget". |
event | yes | the event, its format and cadence, the tier planned, the head count and its basis, speakers, ticket price and comps, what comes off the top, the sponsor revenue per tier, the cash dates, the contingency and reserve inputs, and the organizers' answers. |
lines | yes | every cost line: tier, character, unit amount, total at the head count, its quote date or placeholder label, the date it leaves and the date it locks. |
facts | yes | the page's posture, deductions, targets, the three tiers, the contingency stack, the reserve and outflow posture, the cash schedule with its lowest point, the cut order, the locked lines, the tax question, the gate, the signals to record and every finding tagged [fix], [check] or [info]. |
retry_note | no | optional; if present, your previous reply was unusable - follow it. |
The rules the model applies
- Consume prices - ticket, sponsor, venue, vendor - never derive them. Every number is a dated quote or a labelled placeholder ("no quote yet", carried as a range). Never invent a price to balance the model, and never re-derive a ticket or sponsor price to close a gap: change the target and hand it back.
- The axis that changes the work is whether the format has a ticket line at all. Ticketed: a two-line revenue mix, and break-even is an attendee count at a price. Free-by-construction: sponsorship is close to the whole funding source, break-even collapses to a fundraising target, and the only lever left is spend per head.
- Postures: sponsor-led (default for a community conference; registrations cover per-attendee cost), registration-led (only when the audience attends on employer training budgets the program already clears), annual-funding-led (default for a recurring series such as a monthly meetup - not for one edition of a yearly event; deleted on a one-off edition), sponsorship-by-construction (a free format). On a free event the ticket postures are deleted - a ticket price reappearing as the fix for a shortfall is a funding-model reversal. Membership tied to ticket discounts couples income to single events; decouple it.
- Cut every line into fixed, per-head and per-speaker; the head count is the expected show-up number, never the registration count. Speaker travel and hotel is its own per-speaker line, separate from the speakers' dinner. Take what comes off the top first: the fiscal-host share and processing fees on both rails.
- Three costed tiers - Good runs at all, Better adds the expected niceties, Best has real headroom - each with its own break-even, and sponsor revenue re-estimated per tier because sponsors price on reach.
- Contingency is a stack, not a ranking: a floor (one absolute amount), a pad on the estimates (the one available figure is a convention carried over from startup financial modelling, not event data - say so every time), and the downgrade ladder (the only tool that absorbs a whole-event shortfall). Neither the floor nor the pad is an event-industry convention.
- The reserve protects the organization across editions: a self-set target against its own fixed yearly costs, a per-edition contribution as its own line, and permitted uses written before they are needed (typically: absorbing a failed edition's deficit, deposits for the next edition before sponsor money lands, seeding a new chapter from a surplus). Break-even every time is legitimate only when chosen.
- Read the lowest cash point, not the bottom line: deposits leave before sponsor money lands, so a profitable budget can still fail. Hold each commitment to its latest safe date; staged commitment needs a threshold owed regardless of turnout and someone tracking sales; paying early with no early-payment discount spends recoverability and buys nothing.
- The cut order is the ladder: discretionary extras first, per-head quantities second (only before their guarantee locks), fixed structural lines last (attrition and minimums are owed on the commitment, so a late cut can cost more than it saves). Every cut is priced net of the sponsorship it forfeits; a cut that saves less than it loses deepens the shortfall. Keep a staff-free digital visibility surface - it is what still sells after a downgrade.
- Never state a tax rate, threshold or rule, a contingency percentage as a convention, a venue or vendor price benchmark, or a named platform or fiscal host as a recommendation. The tax treatment of tickets and sponsorship is a question for the fiscal host, a local accountant or counsel, with an owner and a date.
House rules
- Use only the input and the rules above. The page's picks, names, amounts and dates are exact - copy them. Every number in a sentence must appear in the input.
- Label anything that is not one of the rules above, and is not in the input, as your own design.
- Never invent a cost, a quote, a price, a percentage, a date, a vendor or a person; where someone must be named and is not, give a role. Refer to people by name or as they - never infer anyone's gender from a name.
- Never use backticks, never refer to this input's fields by their key names in a sentence, and never copy this prompt's narrative wording into a reply.
- Reply with ONLY one JSON object - no prose before or after, no code fences.
The replies
budget
summary- one sentence, at most 35 words: the format and posture first, the lowest cash point, and the first thing to settle.posture-format(ticketedorfree),pick(sponsor,registration,annualorconstruction, the page's pick),why(one sentence, including any posture the page deleted).lines- one object per cost line, in the page's order:line(exactly),side(fixed,headorspeaker),total(the page's total as a number, or null when there is no amount),basis(the page's label:quote YYYY-MM-DD,placeholder, orno label).deductions- one sentence: what comes off the top, as the page's shares.targets-sponsorship(ticketed: the page's sponsorship target; free: the page's fundraising target),ticket(the page's ticket target, or null when there is no ticket line),math(one sentence: how the page reached them).tiers- exactly 3 objects, Good, Better, Best:tier(good,better,best),expenses(the page's figure),sponsor(the page's figure),breakeven(the page's paying-attendee break-even, or null),target(the page's fundraising target, or null when ticketed),note(one sentence: what the tier adds and what it means).contingency-floor(the page's floor as a number, or null),pad_pct(the page's pad percentage, or null),ladder(one sentence on the downgrade ladder),label(one sentence saying the floor and the pad are self-set, not event-industry conventions).reserve- one sentence: the policy, this edition's contribution or draw, and what the reserve may fund (when the input names no uses, the rules' typical uses, labelled as typical).cash-lowest(the page's lowest cash point as a number),date(its date),note(one sentence: what causes it and the move that lifts it).tax_question- the open tax question, written as a question ending with "?", with its owner from the input or "owner to be named".signals- 2 or 3 short strings, the page's signals to record, each with what it would change next edition (your design).caveats- at most 4 short strings: every[fix]finding first, then the most important[check]findings while there is room.[]when there are none of either.
{
"task": "budget",
"summary": "one sentence",
"posture": { "format": "ticketed", "pick": "sponsor", "why": "one sentence" },
"lines": [ { "line": "Venue", "side": "fixed", "total": 9000, "basis": "quote 2026-01-15" } ],
"deductions": "one sentence",
"targets": { "sponsorship": 20000, "ticket": 12000, "math": "one sentence" },
"tiers": [ { "tier": "good", "expenses": 25000, "sponsor": 16000, "breakeven": 60, "target": null, "note": "one sentence" } ],
"contingency": { "floor": 1500, "pad_pct": null, "ladder": "one sentence", "label": "one sentence" },
"reserve": "one sentence",
"cash": { "lowest": -500, "date": "YYYY-MM-DD", "note": "one sentence" },
"tax_question": "Who confirms ...?",
"signals": [ "what to record and what it changes" ],
"caveats": [ "a caveat" ]
}
The page holds it to its own numbers: the posture, every line's side, total and label, the targets, all three tiers' expenses, sponsorship and break-even or target, the floor and pad, and the lowest cash point with its date; the tax question must be a question and no number may appear that the input does not hold.
shortfall
summary- one sentence, at most 35 words: the first cut, what it nets after the sponsorship it loses, and the date it must be decided by.rungs- one object per rung, in the page's order:fromandto(tier names),saving,sponsor_lostandnet(the page's figures),by(the page's last recoverable date, or ""),what(short: the lines that go). Say plainly inwhatwhen a rung's net is zero or negative.locked- the page's per-head lines already locked by the checkpoint, as short strings;[]when none.last- one sentence: the fixed structural line or lines the page names go last, and why.checkpoints- 1 to 3 objects in date order:date(only the page's checkpoint date or a rung's last recoverable date),decide(one sentence: what is decided that day, against what revenue actually received).sponsor_note- a few sentences to the sponsors if the event steps down a tier: what changes, what they keep (the staff-free digital visibility), no new prices and no numbers the input does not hold.team_note- one sentence for the organizers: what to settle before the first checkpoint.caveats- at most 4 short strings: every[fix]finding first, then the most important[check]findings.[]when there are none.
{
"task": "shortfall",
"summary": "one sentence",
"rungs": [ { "from": "better", "to": "good", "saving": 5000, "sponsor_lost": 2000, "net": 3200, "by": "YYYY-MM-DD", "what": "short" } ],
"locked": [ "a line" ],
"last": "one sentence",
"checkpoints": [ { "date": "YYYY-MM-DD", "decide": "one sentence" } ],
"sponsor_note": "a few sentences",
"team_note": "one sentence",
"caveats": [ "a caveat" ]
}
The page requires each rung's saving, sponsorship lost, net and last recoverable date, the locked lines, checkpoints only on its own dates, a rung that deepens the shortfall named as such, and a sponsor note that keeps digital visibility and offers no price or discount.
Worked examples - every lane, every sample
These are the page's three free samples (fictional events, vendors and people). Each input is exactly what the page sends; each reply is the saved reply the sample replays, shipped as the validating model wrote it; the last line is the page's own check. Every sample has both lanes.
Lakeshore Infra Days (250 people, ticketed) - budget
Sponsor-led; planned at better, expenses $57,965, break-even 111; lowest cash point -$1,900 on 2027-02-01; the gate fails.
Input:
{
"task": "budget",
"event": "Event: Lakeshore Infra Days; conference; ticketed; one edition of a recurring event (last edition's tiers reusable; not a series); planned at the better tier; event date 2027-06-10\nHead count: 250 (the expected show-up number); speakers 12; ticket price $180; comps from agreements held 30; paid capacity 220\nOff the top: fiscal-host share 7%; processing 3%\nSponsor revenue expected per tier: Good $30,000, Better $38,000, Best $41,000\nCash: in hand now $5,000; sponsorship lands 2027-03-15; ticket money lands 2027-05-20; first go/no-go checkpoint 2027-03-20\nContingency entered: floor $2,000; pad on estimates 10%\nReserve policy: surplus on large events; this edition's contribution $3,000; balance $8,000; organization's fixed yearly costs $6,000\nAnswers: audience on employer training budgets no; membership tied to ticket discounts no; a genuine early-payment discount no; someone tracks sales through the window yes; second approver for major spend yes; tax question owner the treasurer, by 2027-01-31",
"lines": "Cost lines (amount is the unit cost, the top of any range; total at the head count):\nVenue deposit | good | fixed | $6,000 | total $6,000 | quote 2026-10-20 | leaves 2026-12-01 | locks 2026-12-01\nVenue balance | good | fixed | $12,000 | total $12,000 | quote 2026-10-20 | leaves 2027-05-20 | locks 2027-04-01\nCatering | good | per head | $45 x 250 | total $11,250 | quote 2026-11-02 | leaves 2027-05-27 | locks 2027-05-27\nInternet | good | fixed | $1,200-$1,500 | total $1,500 | placeholder | leaves 2027-05-01\nBadges and lanyards | good | per head | $4 x 250 | total $1,000 | quote 2026-11-10 | leaves 2027-04-20 | locks 2027-04-20\nInsurance | good | fixed | $900 | total $900 | quote 2026-11-05 | leaves 2027-02-01\nSpeaker travel and hotel | better | per speaker | $600 x 12 | total $7,200 | placeholder | leaves 2027-05-15\nRecording and captioning | better | fixed | $6,500 | total $6,500 | quote 2026-11-12 | leaves 2027-06-10 | locks 2027-04-15\nSpeakers' dinner and gifts | better | fixed | $1,800 | total $1,800 | NO LABEL | leaves 2027-06-09\nEvening event | best | fixed | $7,000 | total $7,000 | quote 2026-11-15 | leaves 2027-03-20 | locks 2027-03-20\nSwag upgrade | best | per head | $12 x 250 | total $3,000 | placeholder | leaves 2027-04-10",
"facts": "Format: ticketed; revenue posture: sponsor - Sponsor-led\nOff the top: 10% of every inflow (host 7% + processing 3%)\nTargets: sponsorship target $38,000 (the better tier's estimate, handed to sponsor pricing); ticket target $26,406 (expenses $57,965 grossed up for fees, minus sponsorship) over a paid capacity of 220\nTiers (cumulative; expenses = lines + pad + floor + reserve contribution):\ngood | 6 lines | lines $32,650 | pad $3,265 | expenses $40,915 | sponsor $30,000 | break-even 20 paying attendees, result at paid capacity $21,725 | cost per head $164\nbetter | 9 lines | lines $48,150 | pad $4,815 | expenses $57,965 | sponsor $38,000 | break-even 111 paying attendees, result at paid capacity $11,875 | cost per head $232\nbest | 11 lines | lines $58,150 | pad $5,815 | expenses $68,965 | sponsor $41,000 | break-even 183 paying attendees, result at paid capacity $3,575 | cost per head $276\nContingency stack (self-set, never an industry convention): floor $2,000; pad 10% ($4,815 at better; a convention from startup financial modelling, not event data); the downgrade ladder below\nReserve: surplus; contribution line $3,000; outflow posture staged - Staged against a tracked signal; earliest-date-allowed deleted (no early-payment discount)\nCash schedule at the better tier (start $5,000):\n2026-12-01 | Venue deposit | -$6,000 | balance -$1,000\n2027-02-01 | Insurance | -$900 | balance -$1,900\n2027-03-15 | sponsorship, net of fees | +$34,200 | balance $32,300\n2027-04-20 | Badges and lanyards | -$1,000 | balance $31,300\n2027-05-01 | Internet | -$1,500 | balance $29,800\n2027-05-15 | Speaker travel and hotel | -$7,200 | balance $22,600\n2027-05-20 | Venue balance | -$12,000 | balance $10,600\n2027-05-20 | ticket revenue at paid capacity, net of fees | +$35,640 | balance $46,240\n2027-05-27 | Catering | -$11,250 | balance $34,990\n2027-06-09 | Speakers' dinner and gifts | -$1,800 | balance $33,190\n2027-06-10 | Recording and captioning | -$6,500 | balance $26,690\n2027-06-10 | reserve contribution | -$3,000 | balance $23,690\nLowest cash point: -$1,900 on 2027-02-01 (Insurance); end $23,690\nCut order (pre-priced, net of sponsorship lost after fees):\nbetter to good (the expected niceties) | saves $17,050 | sponsorship lost $8,000 | net $9,850 | last recoverable 2027-04-15 | Speaker travel and hotel, Recording and captioning, Speakers' dinner and gifts\nPer-head lines locked by the checkpoint: none; fixed structural lines cut last: Venue balance, Venue deposit, Internet\nTax question (never answered here): who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when? Owner: the treasurer, by 2027-01-31\nGate (fails; 4 of 5): Every line a dated quote or an explicit placeholder MISSING; Every line carries the date it leaves settled; A computed lowest cash point settled; Fees off the top entered settled; A reserve policy written down settled\nSignals to record: Variance per line, planned against actual - per line, never one total (self-set); Lowest projected cash point (-$1,900 on 2027-02-01) against the lowest actual (self-set); Which tier the event actually ran at, against better as planned, and the sponsor revenue at that tier (self-set)\nFindings (6):\n[check] silent (lines): 1 line carries neither a dated quote nor a placeholder label (Speakers' dinner and gifts) - a silent placeholder means the budget has not been built yet.\n[info] placeholders (lines): 3 lines still say no quote yet (Internet, Speaker travel and hotel, Swag upgrade) - carried at the top of any range entered.\n[fix] trough (cash): The cash schedule goes to -$1,900 on 2027-02-01 (Insurance) - a budget that is profitable and still fails: money leaves before it lands. Move the commitment to its latest safe date, or bring the inflow forward.\n[info] staged (cash): Staged against a tracked signal is promoted: a per-head guarantee carries a threshold owed regardless of turnout, and someone holds the tracking job through the sale window.\n[info] reserve_months (reserve): The reserve of $8,000 covers about 16 months of the organization's fixed yearly costs ($6,000) - the target is self-set.\n[check] gate (readiness): The gate: not yet - every line needs a dated quote or an explicit placeholder (4 of 5 settled)."
}
Reply:
{
"task": "budget",
"summary": "Ticketed, sponsor-led budget for Lakeshore Infra Days at the better tier; cash falls to -$1,900 on 2027-02-01; first settle: a label for the speakers' dinner and gifts line.",
"posture": {
"format": "ticketed",
"pick": "sponsor",
"why": "Sponsor-led is the default for this conference, with registrations covering per-attendee cost, registration-led deleted because the audience does not attend on employer training budgets, and annual-funding-led deleted because this is one edition of a recurring event rather than a series."
},
"lines": [
{
"line": "Venue deposit",
"side": "fixed",
"total": 6000,
"basis": "quote 2026-10-20"
},
{
"line": "Venue balance",
"side": "fixed",
"total": 12000,
"basis": "quote 2026-10-20"
},
{
"line": "Catering",
"side": "head",
"total": 11250,
"basis": "quote 2026-11-02"
},
{
"line": "Internet",
"side": "fixed",
"total": 1500,
"basis": "placeholder"
},
{
"line": "Badges and lanyards",
"side": "head",
"total": 1000,
"basis": "quote 2026-11-10"
},
{
"line": "Insurance",
"side": "fixed",
"total": 900,
"basis": "quote 2026-11-05"
},
{
"line": "Speaker travel and hotel",
"side": "speaker",
"total": 7200,
"basis": "placeholder"
},
{
"line": "Recording and captioning",
"side": "fixed",
"total": 6500,
"basis": "quote 2026-11-12"
},
{
"line": "Speakers' dinner and gifts",
"side": "fixed",
"total": 1800,
"basis": "no label"
},
{
"line": "Evening event",
"side": "fixed",
"total": 7000,
"basis": "quote 2026-11-15"
},
{
"line": "Swag upgrade",
"side": "head",
"total": 3000,
"basis": "placeholder"
}
],
"deductions": "Off the top of every inflow come the fiscal-host share of 7% and processing of 3%, together 10%.",
"targets": {
"sponsorship": 38000,
"ticket": 26406,
"math": "The sponsorship target is the better tier's $38,000 estimate handed to sponsor pricing, and the ticket target of $26,406 is the better tier's $57,965 expenses grossed up for fees minus that sponsorship, over a paid capacity of 220."
},
"tiers": [
{
"tier": "good",
"expenses": 40915,
"sponsor": 30000,
"breakeven": 20,
"target": null,
"note": "Good runs at all with its 6 lines, expenses of $40,915 and sponsor revenue of $30,000, breaking even at 20 paying attendees and clearing $21,725 at the 220 paid capacity."
},
{
"tier": "better",
"expenses": 57965,
"sponsor": 38000,
"breakeven": 111,
"target": null,
"note": "Better adds speaker travel and hotel, recording and captioning, and the speakers' dinner and gifts to Good's lines for 9 lines total, expenses $57,965 and sponsor revenue $38,000, breaking even at 111 paying attendees and clearing $11,875 at paid capacity."
},
{
"tier": "best",
"expenses": 68965,
"sponsor": 41000,
"breakeven": 183,
"target": null,
"note": "Best adds the evening event and a swag upgrade for real headroom, 11 lines total, expenses $68,965 and sponsor revenue $41,000, breaking even at 183 paying attendees and clearing just $3,575 at paid capacity."
}
],
"contingency": {
"floor": 2000,
"pad_pct": 10,
"ladder": "The downgrade ladder steps from better to good, cutting speaker travel and hotel, recording and captioning, and the speakers' dinner and gifts.",
"label": "The $2,000 floor and the 10% pad are self-set figures, not event-industry conventions; the pad is carried over from startup financial modelling, not event data."
},
"reserve": "The reserve policy takes a surplus on large events, with this edition contributing $3,000 toward a current balance of $8,000 against $6,000 of the organization's fixed yearly costs; since the input names no uses, reserves typically fund absorbing a failed edition's deficit, deposits for the next edition before sponsor money lands, or seeding a new chapter from a surplus (typical uses, not input-specified).",
"cash": {
"lowest": -1900,
"date": "2027-02-01",
"note": "The trough comes from the Insurance payment leaving on 2027-02-01, before sponsorship lands on 2027-03-15; moving that commitment to its latest safe date or pulling an inflow forward would lift it."
},
"tax_question": "Who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when - owner the treasurer, due 2027-01-31?",
"signals": [
"Record variance per line, planned against actual, never one total - this would tighten next edition's per-line quotes where variance ran highest this time.",
"Record the lowest actual cash point against the projected -$1,900 on 2027-02-01 - this would refine how far ahead commitments need to move next edition.",
"Record which tier the event actually ran at against better as planned, and the sponsor revenue realized at that tier - this would recalibrate next edition's sponsor-revenue-per-tier estimates."
],
"caveats": [
"[fix] The cash schedule goes to -$1,900 on 2027-02-01 (Insurance) - move the commitment to its latest safe date or bring the inflow forward.",
"[check] Speakers' dinner and gifts carries no label, neither a dated quote nor a placeholder - the budget has not been fully built yet.",
"[check] The gate is not yet passed (4 of 5): every line needs a dated quote or an explicit placeholder, and the lowest cash point must be computed."
]
}
The page's check: kept - nothing to flag
Lakeshore Infra Days (250 people, ticketed) - shortfall
Sponsor-led; planned at better, expenses $57,965, break-even 111; lowest cash point -$1,900 on 2027-02-01; the gate fails.
Input:
{
"task": "shortfall",
"event": "Event: Lakeshore Infra Days; conference; ticketed; one edition of a recurring event (last edition's tiers reusable; not a series); planned at the better tier; event date 2027-06-10\nHead count: 250 (the expected show-up number); speakers 12; ticket price $180; comps from agreements held 30; paid capacity 220\nOff the top: fiscal-host share 7%; processing 3%\nSponsor revenue expected per tier: Good $30,000, Better $38,000, Best $41,000\nCash: in hand now $5,000; sponsorship lands 2027-03-15; ticket money lands 2027-05-20; first go/no-go checkpoint 2027-03-20\nContingency entered: floor $2,000; pad on estimates 10%\nReserve policy: surplus on large events; this edition's contribution $3,000; balance $8,000; organization's fixed yearly costs $6,000\nAnswers: audience on employer training budgets no; membership tied to ticket discounts no; a genuine early-payment discount no; someone tracks sales through the window yes; second approver for major spend yes; tax question owner the treasurer, by 2027-01-31",
"lines": "Cost lines (amount is the unit cost, the top of any range; total at the head count):\nVenue deposit | good | fixed | $6,000 | total $6,000 | quote 2026-10-20 | leaves 2026-12-01 | locks 2026-12-01\nVenue balance | good | fixed | $12,000 | total $12,000 | quote 2026-10-20 | leaves 2027-05-20 | locks 2027-04-01\nCatering | good | per head | $45 x 250 | total $11,250 | quote 2026-11-02 | leaves 2027-05-27 | locks 2027-05-27\nInternet | good | fixed | $1,200-$1,500 | total $1,500 | placeholder | leaves 2027-05-01\nBadges and lanyards | good | per head | $4 x 250 | total $1,000 | quote 2026-11-10 | leaves 2027-04-20 | locks 2027-04-20\nInsurance | good | fixed | $900 | total $900 | quote 2026-11-05 | leaves 2027-02-01\nSpeaker travel and hotel | better | per speaker | $600 x 12 | total $7,200 | placeholder | leaves 2027-05-15\nRecording and captioning | better | fixed | $6,500 | total $6,500 | quote 2026-11-12 | leaves 2027-06-10 | locks 2027-04-15\nSpeakers' dinner and gifts | better | fixed | $1,800 | total $1,800 | NO LABEL | leaves 2027-06-09\nEvening event | best | fixed | $7,000 | total $7,000 | quote 2026-11-15 | leaves 2027-03-20 | locks 2027-03-20\nSwag upgrade | best | per head | $12 x 250 | total $3,000 | placeholder | leaves 2027-04-10",
"facts": "Format: ticketed; revenue posture: sponsor - Sponsor-led\nOff the top: 10% of every inflow (host 7% + processing 3%)\nTargets: sponsorship target $38,000 (the better tier's estimate, handed to sponsor pricing); ticket target $26,406 (expenses $57,965 grossed up for fees, minus sponsorship) over a paid capacity of 220\nTiers (cumulative; expenses = lines + pad + floor + reserve contribution):\ngood | 6 lines | lines $32,650 | pad $3,265 | expenses $40,915 | sponsor $30,000 | break-even 20 paying attendees, result at paid capacity $21,725 | cost per head $164\nbetter | 9 lines | lines $48,150 | pad $4,815 | expenses $57,965 | sponsor $38,000 | break-even 111 paying attendees, result at paid capacity $11,875 | cost per head $232\nbest | 11 lines | lines $58,150 | pad $5,815 | expenses $68,965 | sponsor $41,000 | break-even 183 paying attendees, result at paid capacity $3,575 | cost per head $276\nContingency stack (self-set, never an industry convention): floor $2,000; pad 10% ($4,815 at better; a convention from startup financial modelling, not event data); the downgrade ladder below\nReserve: surplus; contribution line $3,000; outflow posture staged - Staged against a tracked signal; earliest-date-allowed deleted (no early-payment discount)\nCash schedule at the better tier (start $5,000):\n2026-12-01 | Venue deposit | -$6,000 | balance -$1,000\n2027-02-01 | Insurance | -$900 | balance -$1,900\n2027-03-15 | sponsorship, net of fees | +$34,200 | balance $32,300\n2027-04-20 | Badges and lanyards | -$1,000 | balance $31,300\n2027-05-01 | Internet | -$1,500 | balance $29,800\n2027-05-15 | Speaker travel and hotel | -$7,200 | balance $22,600\n2027-05-20 | Venue balance | -$12,000 | balance $10,600\n2027-05-20 | ticket revenue at paid capacity, net of fees | +$35,640 | balance $46,240\n2027-05-27 | Catering | -$11,250 | balance $34,990\n2027-06-09 | Speakers' dinner and gifts | -$1,800 | balance $33,190\n2027-06-10 | Recording and captioning | -$6,500 | balance $26,690\n2027-06-10 | reserve contribution | -$3,000 | balance $23,690\nLowest cash point: -$1,900 on 2027-02-01 (Insurance); end $23,690\nCut order (pre-priced, net of sponsorship lost after fees):\nbetter to good (the expected niceties) | saves $17,050 | sponsorship lost $8,000 | net $9,850 | last recoverable 2027-04-15 | Speaker travel and hotel, Recording and captioning, Speakers' dinner and gifts\nPer-head lines locked by the checkpoint: none; fixed structural lines cut last: Venue balance, Venue deposit, Internet\nTax question (never answered here): who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when? Owner: the treasurer, by 2027-01-31\nGate (fails; 4 of 5): Every line a dated quote or an explicit placeholder MISSING; Every line carries the date it leaves settled; A computed lowest cash point settled; Fees off the top entered settled; A reserve policy written down settled\nSignals to record: Variance per line, planned against actual - per line, never one total (self-set); Lowest projected cash point (-$1,900 on 2027-02-01) against the lowest actual (self-set); Which tier the event actually ran at, against better as planned, and the sponsor revenue at that tier (self-set)\nFindings (6):\n[check] silent (lines): 1 line carries neither a dated quote nor a placeholder label (Speakers' dinner and gifts) - a silent placeholder means the budget has not been built yet.\n[info] placeholders (lines): 3 lines still say no quote yet (Internet, Speaker travel and hotel, Swag upgrade) - carried at the top of any range entered.\n[fix] trough (cash): The cash schedule goes to -$1,900 on 2027-02-01 (Insurance) - a budget that is profitable and still fails: money leaves before it lands. Move the commitment to its latest safe date, or bring the inflow forward.\n[info] staged (cash): Staged against a tracked signal is promoted: a per-head guarantee carries a threshold owed regardless of turnout, and someone holds the tracking job through the sale window.\n[info] reserve_months (reserve): The reserve of $8,000 covers about 16 months of the organization's fixed yearly costs ($6,000) - the target is self-set.\n[check] gate (readiness): The gate: not yet - every line needs a dated quote or an explicit placeholder (4 of 5 settled)."
}
Reply:
{
"task": "shortfall",
"summary": "The first cut is better to good, saving $17,050 and netting $9,850 after $8,000 of lost sponsorship, decided by 2027-04-15.",
"rungs": [
{
"from": "better",
"to": "good",
"saving": 17050,
"sponsor_lost": 8000,
"net": 9850,
"by": "2027-04-15",
"what": "Drops Speaker travel and hotel, Recording and captioning, and Speakers' dinner and gifts."
}
],
"locked": [],
"last": "Venue balance, Venue deposit and Internet are cut last because they are fixed structural commitments - cancelling them late can trigger attrition charges or minimums that cost more than the cut saves.",
"checkpoints": [
{
"date": "2027-03-20",
"decide": "The first go/no-go, against the sponsorship that landed on 2027-03-15."
},
{
"date": "2027-04-15",
"decide": "Whether to step down from better to good - the final date this cut can still be made - against ticket and sponsorship revenue tracked to date."
}
],
"sponsor_note": "If the event steps down from better to good, the lines that go are speaker travel and hotel, recording and captioning, and the speakers' dinner and gifts, saving $17,050 and netting $9,850 after $8,000 of sponsorship repriced for the smaller tier. What stays the same is the sponsor's always-on presence across our digital channels, which needs no event staff to keep running and keeps generating visibility regardless of tier. No new prices are set by this change.",
"team_note": "Before the 2027-03-20 checkpoint, get a quote or placeholder label onto Speakers' dinner and gifts and resolve the 2027-02-01 Insurance cash trough, so the gate can close ahead of the first go/no-go.",
"caveats": [
"The cash schedule goes to -$1,900 on 2027-02-01 (Insurance) before sponsorship lands - move the commitment to its latest safe date or bring the inflow forward.",
"Speakers' dinner and gifts carries neither a dated quote nor a placeholder label - a silent placeholder means the budget is not yet built.",
"The gate reads not yet, 4 of 5 settled - every line still needs a dated quote or an explicit placeholder."
]
}
The page's check: kept - nothing to flag
Northgate Student Hack (free, 150) - budget
Sponsorship-by-construction; planned at better, expenses $12,250, raise $13,172; lowest cash point -$6,750 on 2027-02-10; the gate passes.
Input:
{
"task": "budget",
"event": "Event: Northgate Student Hack; hackathon; free to attend - no ticket line; a one-off edition; planned at the better tier; event date 2027-03-06\nHead count: 150 (a REGISTRATION count); speakers 0\nOff the top: fiscal-host share 7%; processing 0%\nSponsor revenue expected per tier: Good $14,000, Better $17,000, Best $19,000\nCash: in hand now $1,500; sponsorship lands 2027-02-20; first go/no-go checkpoint 2027-01-31\nContingency entered: floor $1,000; pad on estimates none\nReserve policy: break-even each edition; this edition's contribution $0; balance not given; organization's fixed yearly costs not given\nAnswers: audience on employer training budgets no; membership tied to ticket discounts no; a genuine early-payment discount no; someone tracks sales through the window no; second approver for major spend no; tax question owner not named",
"lines": "Cost lines (amount is the unit cost, the top of any range; total at the head count):\nVenue (campus hall) | good | fixed | $0 | total $0 | quote 2026-09-15 | leaves 2027-03-06\nMeals (four) | good | per head | $36 x 150 | total $5,400 | quote 2026-12-01 | leaves 2027-02-10 | locks 2027-02-24\nSnacks and drinks | good | per head | $10 x 150 | total $1,500 | quote 2026-12-01 | leaves 2027-02-10 | locks 2027-02-24\nHosting and tooling | good | fixed | $300 | total $300 | placeholder | leaves 2027-02-01\nT-shirts | better | per head | $7 x 150 | total $1,050 | quote 2026-12-05 | leaves 2027-01-25 | locks 2027-01-25\nPrizes | better | fixed | $3,000 | total $3,000 | quote 2026-12-10 | leaves 2027-03-06\nBus from the station | best | fixed | $3,500 | total $3,500 | quote 2026-12-12 | leaves 2027-02-15 | locks 2027-02-15\nPlatform upgrade | best | fixed | $400 | total $400 | placeholder | leaves 2027-02-01",
"facts": "Format: free-by-construction; revenue posture: construction - Sponsorship-by-construction; annual-funding-led deleted (a one-off edition); sponsor-led and registration-led deleted (no ticket line)\nOff the top: 7% of every inflow (host 7% + processing 0%)\nTargets: fundraising target at the better tier $13,172 (expenses $12,250 grossed up for fees); sponsorship entered $17,000; gap -$3,828\nTiers (cumulative; expenses = lines + pad + floor + reserve contribution):\ngood | 4 lines | lines $7,200 | pad $0 | expenses $8,200 | sponsor $14,000 | target $8,817, gap -$5,183 | cost per head $55\nbetter | 6 lines | lines $11,250 | pad $0 | expenses $12,250 | sponsor $17,000 | target $13,172, gap -$3,828 | cost per head $82\nbest | 8 lines | lines $15,150 | pad $0 | expenses $16,150 | sponsor $19,000 | target $17,366, gap -$1,634 | cost per head $108\nContingency stack (self-set, never an industry convention): floor $1,000; pad none; the downgrade ladder below\nReserve: breakeven; contribution line $0; outflow posture latest - Latest safe date; earliest-date-allowed deleted (no early-payment discount)\nCash schedule at the better tier (start $1,500):\n2027-01-25 | T-shirts | -$1,050 | balance $450\n2027-02-01 | Hosting and tooling | -$300 | balance $150\n2027-02-10 | Meals (four) | -$5,400 | balance -$5,250\n2027-02-10 | Snacks and drinks | -$1,500 | balance -$6,750\n2027-02-20 | sponsorship, net of fees | +$15,810 | balance $9,060\n2027-03-06 | Prizes | -$3,000 | balance $6,060\nLowest cash point: -$6,750 on 2027-02-10 (Snacks and drinks); end $6,060\nCut order (pre-priced, net of sponsorship lost after fees):\nbetter to good (the expected niceties) | saves $4,050 | sponsorship lost $3,000 | net $1,260 | last recoverable 2027-01-25 | T-shirts, Prizes\nPer-head lines locked by the checkpoint: T-shirts; fixed structural lines cut last: Hosting and tooling\nTax question (never answered here): who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when? Owner: not named\nGate (passes; 5 of 5): Every line a dated quote or an explicit placeholder settled; Every line carries the date it leaves settled; A computed lowest cash point settled; Fees off the top entered settled; A reserve policy written down settled\nSignals to record: Variance per line, planned against actual - per line, never one total (self-set); Lowest projected cash point (-$6,750 on 2027-02-10) against the lowest actual (self-set); Which tier the event actually ran at, against better as planned, and the sponsor revenue at that tier (self-set)\nFindings (9):\n[fix] reg_count (head count): The head count is a registration count - per-head lines budgeted off it are over-ordered by construction. Use the expected show-up number.\n[info] placeholders (lines): 2 lines still say no quote yet (Hosting and tooling, Platform upgrade) - carried at the top of any range entered.\n[info] no_pad (contingency): No pad on the estimates. The one figure available (20%) is a convention carried over from startup financial modelling, not measured against event data - if you use one, label it that way.\n[fix] trough (cash): The cash schedule goes to -$6,750 on 2027-02-10 (Snacks and drinks) - a budget that is profitable and still fails: money leaves before it lands. Move the commitment to its latest safe date, or bring the inflow forward.\n[info] locked (cuts): Past their lock date by the first checkpoint: T-shirts - the per-head rung is gone for these; decide the cut order against the cash schedule, not in the abstract.\n[check] pays_early (cash): Meals (four), Snacks and drinks leave before their lock date with no early-payment discount on the other side - paying early spends recoverability and buys nothing.\n[check] approver (approval): Major spend needs a second approver who is not the spender - never self-approved.\n[check] tax_owner (tax): Name who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when - this model never states a tax rule.\n[info] gate (readiness): The gate: passes - every line is a dated quote or a labelled placeholder, and the lowest cash point is computed (5 of 5 settled)."
}
Reply:
{
"task": "budget",
"summary": "Free hackathon funded by Sponsorship-by-construction; cash falls to -$6,750 on 2027-02-10 before sponsorship lands; settle first that the 150 head count is a registration count, not the expected show-up number.",
"posture": {
"format": "free",
"pick": "construction",
"why": "The event is free to attend with no ticket line, so Sponsorship-by-construction applies; annual-funding-led is deleted because this is a one-off edition, and sponsor-led and registration-led are deleted because there is no ticket line."
},
"lines": [
{
"line": "Venue (campus hall)",
"side": "fixed",
"total": 0,
"basis": "quote 2026-09-15"
},
{
"line": "Meals (four)",
"side": "head",
"total": 5400,
"basis": "quote 2026-12-01"
},
{
"line": "Snacks and drinks",
"side": "head",
"total": 1500,
"basis": "quote 2026-12-01"
},
{
"line": "Hosting and tooling",
"side": "fixed",
"total": 300,
"basis": "placeholder"
},
{
"line": "T-shirts",
"side": "head",
"total": 1050,
"basis": "quote 2026-12-05"
},
{
"line": "Prizes",
"side": "fixed",
"total": 3000,
"basis": "quote 2026-12-10"
},
{
"line": "Bus from the station",
"side": "fixed",
"total": 3500,
"basis": "quote 2026-12-12"
},
{
"line": "Platform upgrade",
"side": "fixed",
"total": 400,
"basis": "placeholder"
}
],
"deductions": "The fiscal host takes 7% and processing takes 0% off the top of every inflow.",
"targets": {
"sponsorship": 13172,
"ticket": null,
"math": "The $13,172 fundraising target is the better tier's $12,250 in expenses grossed up for the 7% taken off the top."
},
"tiers": [
{
"tier": "good",
"expenses": 8200,
"sponsor": 14000,
"breakeven": null,
"target": 8817,
"note": "Good covers the venue, meals, snacks and drinks, and hosting and tooling at $55 cost per head, against an $8,817 fundraising target and $14,000 in sponsor revenue entered for that tier."
},
{
"tier": "better",
"expenses": 12250,
"sponsor": 17000,
"breakeven": null,
"target": 13172,
"note": "Better is the planned tier, adding T-shirts and Prizes for $82 cost per head, against a $13,172 fundraising target and $17,000 in sponsor revenue entered for that tier."
},
{
"tier": "best",
"expenses": 16150,
"sponsor": 19000,
"breakeven": null,
"target": 17366,
"note": "Best adds the bus from the station and the platform upgrade for $108 cost per head, against a $17,366 fundraising target and $19,000 in sponsor revenue entered for that tier."
}
],
"contingency": {
"floor": 1000,
"pad_pct": null,
"ladder": "The only rung is better to good, dropping T-shirts and Prizes to save $4,050, a net $1,260 after the $3,000 in sponsor revenue lost, decided by 2027-01-25.",
"label": "The $1,000 floor is self-set, not an event-industry convention, and no pad on the estimates was entered for this edition."
},
"reserve": "The reserve policy is break-even each edition with a $0 contribution for this one, and since no uses are named, it would typically fund a failed edition's deficit, deposits for the next edition before sponsor money lands, or seeding a new chapter from a surplus.",
"cash": {
"lowest": -6750,
"date": "2027-02-10",
"note": "The lowest point is -$6,750 on 2027-02-10, caused by Snacks and drinks leaving before the 2027-02-20 sponsorship lands; moving that commitment to its latest safe date or bringing the sponsorship inflow forward would lift it."
},
"tax_question": "Who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when, owner to be named?",
"signals": [
"Variance per line, planned against actual, per line never one total - sets next edition's per-line pad and quote confidence.",
"Lowest projected cash point against the lowest actual - shows whether next edition needs an earlier inflow or a later outflow.",
"Which tier the event actually ran at against better as planned, and the sponsor revenue at that tier - recalibrates next edition's planned-tier sponsor target."
],
"caveats": [
"The head count (150) is a registration count, not the expected show-up number - per-head lines are over-ordered by construction until it is corrected.",
"The cash schedule goes to -$6,750 on 2027-02-10 (Snacks and drinks), a profitable budget that still fails because money leaves before the 2027-02-20 sponsorship lands; move the commitment to its latest safe date or bring the inflow forward.",
"Meals (four) and Snacks and drinks leave on 2027-02-10, before their 2027-02-24 lock date, with no early-payment discount - paying early spends recoverability and buys nothing.",
"Major spend needs a second approver who is not the spender - none is named, so spend is currently self-approved."
]
}
The page's check: kept - nothing to flag
Northgate Student Hack (free, 150) - shortfall
Sponsorship-by-construction; planned at better, expenses $12,250, raise $13,172; lowest cash point -$6,750 on 2027-02-10; the gate passes.
Input:
{
"task": "shortfall",
"event": "Event: Northgate Student Hack; hackathon; free to attend - no ticket line; a one-off edition; planned at the better tier; event date 2027-03-06\nHead count: 150 (a REGISTRATION count); speakers 0\nOff the top: fiscal-host share 7%; processing 0%\nSponsor revenue expected per tier: Good $14,000, Better $17,000, Best $19,000\nCash: in hand now $1,500; sponsorship lands 2027-02-20; first go/no-go checkpoint 2027-01-31\nContingency entered: floor $1,000; pad on estimates none\nReserve policy: break-even each edition; this edition's contribution $0; balance not given; organization's fixed yearly costs not given\nAnswers: audience on employer training budgets no; membership tied to ticket discounts no; a genuine early-payment discount no; someone tracks sales through the window no; second approver for major spend no; tax question owner not named",
"lines": "Cost lines (amount is the unit cost, the top of any range; total at the head count):\nVenue (campus hall) | good | fixed | $0 | total $0 | quote 2026-09-15 | leaves 2027-03-06\nMeals (four) | good | per head | $36 x 150 | total $5,400 | quote 2026-12-01 | leaves 2027-02-10 | locks 2027-02-24\nSnacks and drinks | good | per head | $10 x 150 | total $1,500 | quote 2026-12-01 | leaves 2027-02-10 | locks 2027-02-24\nHosting and tooling | good | fixed | $300 | total $300 | placeholder | leaves 2027-02-01\nT-shirts | better | per head | $7 x 150 | total $1,050 | quote 2026-12-05 | leaves 2027-01-25 | locks 2027-01-25\nPrizes | better | fixed | $3,000 | total $3,000 | quote 2026-12-10 | leaves 2027-03-06\nBus from the station | best | fixed | $3,500 | total $3,500 | quote 2026-12-12 | leaves 2027-02-15 | locks 2027-02-15\nPlatform upgrade | best | fixed | $400 | total $400 | placeholder | leaves 2027-02-01",
"facts": "Format: free-by-construction; revenue posture: construction - Sponsorship-by-construction; annual-funding-led deleted (a one-off edition); sponsor-led and registration-led deleted (no ticket line)\nOff the top: 7% of every inflow (host 7% + processing 0%)\nTargets: fundraising target at the better tier $13,172 (expenses $12,250 grossed up for fees); sponsorship entered $17,000; gap -$3,828\nTiers (cumulative; expenses = lines + pad + floor + reserve contribution):\ngood | 4 lines | lines $7,200 | pad $0 | expenses $8,200 | sponsor $14,000 | target $8,817, gap -$5,183 | cost per head $55\nbetter | 6 lines | lines $11,250 | pad $0 | expenses $12,250 | sponsor $17,000 | target $13,172, gap -$3,828 | cost per head $82\nbest | 8 lines | lines $15,150 | pad $0 | expenses $16,150 | sponsor $19,000 | target $17,366, gap -$1,634 | cost per head $108\nContingency stack (self-set, never an industry convention): floor $1,000; pad none; the downgrade ladder below\nReserve: breakeven; contribution line $0; outflow posture latest - Latest safe date; earliest-date-allowed deleted (no early-payment discount)\nCash schedule at the better tier (start $1,500):\n2027-01-25 | T-shirts | -$1,050 | balance $450\n2027-02-01 | Hosting and tooling | -$300 | balance $150\n2027-02-10 | Meals (four) | -$5,400 | balance -$5,250\n2027-02-10 | Snacks and drinks | -$1,500 | balance -$6,750\n2027-02-20 | sponsorship, net of fees | +$15,810 | balance $9,060\n2027-03-06 | Prizes | -$3,000 | balance $6,060\nLowest cash point: -$6,750 on 2027-02-10 (Snacks and drinks); end $6,060\nCut order (pre-priced, net of sponsorship lost after fees):\nbetter to good (the expected niceties) | saves $4,050 | sponsorship lost $3,000 | net $1,260 | last recoverable 2027-01-25 | T-shirts, Prizes\nPer-head lines locked by the checkpoint: T-shirts; fixed structural lines cut last: Hosting and tooling\nTax question (never answered here): who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when? Owner: not named\nGate (passes; 5 of 5): Every line a dated quote or an explicit placeholder settled; Every line carries the date it leaves settled; A computed lowest cash point settled; Fees off the top entered settled; A reserve policy written down settled\nSignals to record: Variance per line, planned against actual - per line, never one total (self-set); Lowest projected cash point (-$6,750 on 2027-02-10) against the lowest actual (self-set); Which tier the event actually ran at, against better as planned, and the sponsor revenue at that tier (self-set)\nFindings (9):\n[fix] reg_count (head count): The head count is a registration count - per-head lines budgeted off it are over-ordered by construction. Use the expected show-up number.\n[info] placeholders (lines): 2 lines still say no quote yet (Hosting and tooling, Platform upgrade) - carried at the top of any range entered.\n[info] no_pad (contingency): No pad on the estimates. The one figure available (20%) is a convention carried over from startup financial modelling, not measured against event data - if you use one, label it that way.\n[fix] trough (cash): The cash schedule goes to -$6,750 on 2027-02-10 (Snacks and drinks) - a budget that is profitable and still fails: money leaves before it lands. Move the commitment to its latest safe date, or bring the inflow forward.\n[info] locked (cuts): Past their lock date by the first checkpoint: T-shirts - the per-head rung is gone for these; decide the cut order against the cash schedule, not in the abstract.\n[check] pays_early (cash): Meals (four), Snacks and drinks leave before their lock date with no early-payment discount on the other side - paying early spends recoverability and buys nothing.\n[check] approver (approval): Major spend needs a second approver who is not the spender - never self-approved.\n[check] tax_owner (tax): Name who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when - this model never states a tax rule.\n[info] gate (readiness): The gate: passes - every line is a dated quote or a labelled placeholder, and the lowest cash point is computed (5 of 5 settled)."
}
Reply:
{
"task": "shortfall",
"summary": "The first cut is better to good, saving $4,050 but losing $3,000 of sponsorship for a net of $1,260, and must be decided by 2027-01-25.",
"rungs": [
{
"from": "better",
"to": "good",
"saving": 4050,
"sponsor_lost": 3000,
"net": 1260,
"by": "2027-01-25",
"what": "Drops T-shirts and Prizes."
}
],
"locked": [
"T-shirts"
],
"last": "Hosting and tooling is cut last because fixed structural lines are owed on the commitment, so cutting them late can cost more than it saves.",
"checkpoints": [
{
"date": "2027-01-25",
"decide": "Decide whether to step down from better to good - dropping T-shirts and Prizes for a net of $1,260 - against sponsorship revenue actually received by that date."
},
{
"date": "2027-01-31",
"decide": "Make the first go/no-go decision for the event against sponsorship revenue actually received by that date."
}
],
"sponsor_note": "If the event steps down from better to good, the T-shirts and Prizes lines are dropped. Every sponsor keeps the staff-free digital visibility surface, which is what still sells after a downgrade. No sponsor price changes and no new costs are introduced at any tier, and terms already agreed are not altered by the step-down.",
"team_note": "Before 2027-01-25, organizers should name the second approver for major spend, since none is currently named.",
"caveats": [
"[fix] The head count is a registration count; per-head lines are over-ordered by construction - use the expected show-up number.",
"[fix] Cash goes to -$6,750 on 2027-02-10 (Snacks and drinks); a profitable budget can still fail - move the commitment to its latest safe date or bring the inflow forward.",
"[check] Meals (four) and Snacks and drinks leave before their lock date with no early-payment discount - paying early spends recoverability and buys nothing.",
"[check] Major spend needs a second approver who is not the spender - never self-approved."
]
}
The page's check: kept - nothing to flag
Riverside Python Evenings (a free monthly series) - budget
Annual-funding-led; planned at best, expenses $12,380, raise $13,312; lowest cash point -$1,165 on 2027-10-20; the gate passes.
Input:
{
"task": "budget",
"event": "Event: Riverside Python Evenings; meetup; free to attend - no ticket line; a recurring series budgeted for the year; planned at the best tier; event date 2027-01-20\nHead count: 60 (the expected show-up number) per edition, 10 editions in this budget; speakers 0\nOff the top: fiscal-host share 5%; processing 2%\nSponsor revenue expected per tier: Good not given, Better not given, Best not given; annual-funding allocation in $9,500\nCash: in hand now $800; sponsorship lands not given; first go/no-go checkpoint 2027-01-15\nContingency entered: floor $500; pad on estimates 10%\nReserve policy: break-even each edition; this edition's contribution $0; balance $1,200; organization's fixed yearly costs $2,400\nAnswers: audience on employer training budgets no; membership tied to ticket discounts yes; a genuine early-payment discount yes; someone tracks sales through the window no; second approver for major spend yes; tax question owner the fiscal host, by 2026-12-15",
"lines": "Cost lines (amount is the unit cost, the top of any range; total at the head count):\nRoom hire (year) | good | fixed | $2,400 | total $2,400 | quote 2026-11-01 | leaves 2026-12-20 | locks 2026-12-20\nPizza and drinks | good | per head | $9 x 600 | total $5,400 | quote 2026-11-03 | leaves 2027-01-20\nLivestream kit | better | fixed | $1,200 | total $1,200 | placeholder | leaves 2027-01-10\nStickers | better | per head | $0.50 x 600 | total $300 | quote 2026-11-04 | leaves 2027-01-10\nYear-end social | best | fixed | $1,500 | total $1,500 | quote 2026-11-08 | leaves 2027-01-05 | locks 2027-01-05",
"facts": "Format: free-by-construction; revenue posture: annual - Annual-funding-led; sponsor-led and registration-led deleted (no ticket line)\nOff the top: 7% of every inflow (host 5% + processing 2%)\nTargets: fundraising target at the best tier $13,312 (expenses $12,380 grossed up for fees); sponsorship entered $0 + annual $9,500; gap $3,812\nTiers (cumulative; expenses = lines + pad + floor + reserve contribution):\ngood | 2 lines | lines $7,800 | pad $780 | expenses $9,080 | sponsor $0 | target $9,763, gap $263 | cost per head $151\nbetter | 4 lines | lines $9,300 | pad $930 | expenses $10,730 | sponsor $0 | target $11,538, gap $2,038 | cost per head $179\nbest | 5 lines | lines $10,800 | pad $1,080 | expenses $12,380 | sponsor $0 | target $13,312, gap $3,812 | cost per head $206\nContingency stack (self-set, never an industry convention): floor $500; pad 10% ($1,080 at best; a convention from startup financial modelling, not event data); the downgrade ladder below\nReserve: breakeven; contribution line $0; outflow posture latest - Latest safe date\nCash schedule at the best tier (start $9,635):\n2026-12-20 | Room hire (year) | -$2,400 | balance $7,235\n2027-01-05 | Year-end social | -$1,500 | balance $5,735\n2027-01-10 | Livestream kit | -$1,200 | balance $4,535\n2027-01-10 | Stickers (edition 1) | -$30 | balance $4,505\n2027-01-20 | Pizza and drinks (edition 1) | -$540 | balance $3,965\n2027-02-10 | Stickers (edition 2) | -$30 | balance $3,935\n2027-02-20 | Pizza and drinks (edition 2) | -$540 | balance $3,395\n2027-03-10 | Stickers (edition 3) | -$30 | balance $3,365\n2027-03-20 | Pizza and drinks (edition 3) | -$540 | balance $2,825\n2027-04-10 | Stickers (edition 4) | -$30 | balance $2,795\n2027-04-20 | Pizza and drinks (edition 4) | -$540 | balance $2,255\n2027-05-10 | Stickers (edition 5) | -$30 | balance $2,225\n2027-05-20 | Pizza and drinks (edition 5) | -$540 | balance $1,685\n2027-06-10 | Stickers (edition 6) | -$30 | balance $1,655\n2027-06-20 | Pizza and drinks (edition 6) | -$540 | balance $1,115\n2027-07-10 | Stickers (edition 7) | -$30 | balance $1,085\n2027-07-20 | Pizza and drinks (edition 7) | -$540 | balance $545\n2027-08-10 | Stickers (edition 8) | -$30 | balance $515\n2027-08-20 | Pizza and drinks (edition 8) | -$540 | balance -$25\n2027-09-10 | Stickers (edition 9) | -$30 | balance -$55\n2027-09-20 | Pizza and drinks (edition 9) | -$540 | balance -$595\n2027-10-10 | Stickers (edition 10) | -$30 | balance -$625\n2027-10-20 | Pizza and drinks (edition 10) | -$540 | balance -$1,165\nLowest cash point: -$1,165 on 2027-10-20 (Pizza and drinks (edition 10)); end -$1,165\nCut order (pre-priced, net of sponsorship lost after fees):\nbest to better (discretionary extras) | saves $1,650 | sponsorship lost $0 | net $1,650 | last recoverable 2027-01-05 | Year-end social\nbetter to good (the expected niceties) | saves $1,650 | sponsorship lost $0 | net $1,650 | last recoverable no lock date | Livestream kit, Stickers\nPer-head lines locked by the checkpoint: none; fixed structural lines cut last: Room hire (year)\nTax question (never answered here): who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when? Owner: the fiscal host, by 2026-12-15\nGate (passes; 5 of 5): Every line a dated quote or an explicit placeholder settled; Every line carries the date it leaves settled; A computed lowest cash point settled; Fees off the top entered settled; A reserve policy written down settled\nSignals to record: Variance per line, planned against actual - per line, never one total (self-set); Lowest projected cash point (-$1,165 on 2027-10-20) against the lowest actual (self-set); Which tier the event actually ran at, against best as planned, and the sponsor revenue at that tier (self-set)\nFindings (6):\n[info] placeholders (lines): 1 line still says no quote yet (Livestream kit) - carried at the top of any range entered.\n[fix] membership_tied (posture): Membership tied to event ticket discounts - members join just before each event, the count spikes and income stays coupled to single events. Decouple membership and annual sponsorship from any one edition.\n[check] fund_gap (targets): Free-by-construction: the best tier needs $13,312 raised; the sponsor revenue entered leaves $3,812 to find - the only other lever is spend per head.\n[fix] trough (cash): The cash schedule goes to -$1,165 on 2027-10-20 (Pizza and drinks (edition 10)) - the plan does not cover its costs, and this is where the money runs out. Raise the target or take the cut order.\n[info] reserve_months (reserve): The reserve of $1,200 covers about 6 months of the organization's fixed yearly costs ($2,400) - the target is self-set.\n[info] gate (readiness): The gate: passes - every line is a dated quote or a labelled placeholder, and the lowest cash point is computed (5 of 5 settled)."
}
Reply:
{
"task": "budget",
"summary": "The event is free-by-construction, budgeted under an annual-funding-led posture; cash runs to -$1,165 on 2027-10-20, so the first thing to settle is the fiscal host confirming the tax treatment by 2026-12-15.",
"posture": {
"format": "free",
"pick": "annual",
"why": "The event is free-by-construction and is a recurring series budgeted for the year, so it is annual-funding-led; sponsor-led and registration-led are deleted because there is no ticket line."
},
"lines": [
{
"line": "Room hire (year)",
"side": "fixed",
"total": 2400,
"basis": "quote 2026-11-01"
},
{
"line": "Pizza and drinks",
"side": "head",
"total": 5400,
"basis": "quote 2026-11-03"
},
{
"line": "Livestream kit",
"side": "fixed",
"total": 1200,
"basis": "placeholder"
},
{
"line": "Stickers",
"side": "head",
"total": 300,
"basis": "quote 2026-11-04"
},
{
"line": "Year-end social",
"side": "fixed",
"total": 1500,
"basis": "quote 2026-11-08"
}
],
"deductions": "Before any line is funded, 7% of every inflow comes off the top as a fiscal-host share of 5% and processing of 2%.",
"targets": {
"sponsorship": 13312,
"ticket": null,
"math": "The $13,312 fundraising target is the best tier's $12,380 in expenses grossed up for the 7% off the top, which the $0 sponsorship entered plus the $9,500 annual-funding allocation leave $3,812 short of covering."
},
"tiers": [
{
"tier": "good",
"expenses": 9080,
"sponsor": 0,
"breakeven": null,
"target": 9763,
"note": "Good keeps to the 2 baseline lines (Room hire, Pizza and drinks) at $151 cost per head, needing $9,763 raised against $0 sponsor revenue entered, a $263 gap."
},
{
"tier": "better",
"expenses": 10730,
"sponsor": 0,
"breakeven": null,
"target": 11538,
"note": "Better adds the Livestream kit and Stickers for the expected niceties, raising cost per head to $179 and the target to $11,538, a $2,038 gap."
},
{
"tier": "best",
"expenses": 12380,
"sponsor": 0,
"breakeven": null,
"target": 13312,
"note": "Best adds the Year-end social for real headroom, raising cost per head to $206 and the target to $13,312, a $3,812 gap."
}
],
"contingency": {
"floor": 500,
"pad_pct": 10,
"ladder": "The downgrade ladder drops the Year-end social first, stepping from best to better for a $1,650 net saving, then drops the Livestream kit and Stickers, stepping from better to good for another $1,650 net saving, with $0 sponsorship lost at either step.",
"label": "The $500 floor and the 10% pad are both self-set, not event-industry conventions - the pad is a convention carried over from startup financial modelling, not event data."
},
"reserve": "The reserve policy is break-even each edition, with this edition contributing $0 against a $1,200 balance toward the organization's $2,400 fixed yearly costs, and - since the input names no permitted uses - it would typically fund absorbing a failed edition's deficit, deposits for the next edition before sponsor money lands, or seeding a new chapter from a surplus, labelled as typical.",
"cash": {
"lowest": -1165,
"date": "2027-10-20",
"note": "The lowest point comes from edition 10's Pizza and drinks outflow on 2027-10-20, and raising the target or taking the cut order is what lifts it above zero."
},
"tax_question": "Who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when - owner the fiscal host, by 2026-12-15?",
"signals": [
"Variance per line, planned against actual, tracked per line rather than one total - sets next edition's per-line quotes.",
"The lowest actual cash point against the projected -$1,165 on 2027-10-20 - sets next edition's floor and pad.",
"Which tier the event actually ran at against best as planned, and the sponsor revenue at that tier - sets next edition's planned tier."
],
"caveats": [
"Membership is tied to event ticket discounts, coupling income to single editions - decouple membership and annual sponsorship from any one edition.",
"The cash schedule goes to -$1,165 on 2027-10-20 (Pizza and drinks, edition 10) - the plan does not cover its costs there; raise the target or take the cut order.",
"Free-by-construction: the best tier needs $13,312 raised and the sponsor revenue entered leaves a $3,812 gap - the only other lever is spend per head."
]
}
The page's check: kept - nothing to flag
Riverside Python Evenings (a free monthly series) - shortfall
Annual-funding-led; planned at best, expenses $12,380, raise $13,312; lowest cash point -$1,165 on 2027-10-20; the gate passes.
Input:
{
"task": "shortfall",
"event": "Event: Riverside Python Evenings; meetup; free to attend - no ticket line; a recurring series budgeted for the year; planned at the best tier; event date 2027-01-20\nHead count: 60 (the expected show-up number) per edition, 10 editions in this budget; speakers 0\nOff the top: fiscal-host share 5%; processing 2%\nSponsor revenue expected per tier: Good not given, Better not given, Best not given; annual-funding allocation in $9,500\nCash: in hand now $800; sponsorship lands not given; first go/no-go checkpoint 2027-01-15\nContingency entered: floor $500; pad on estimates 10%\nReserve policy: break-even each edition; this edition's contribution $0; balance $1,200; organization's fixed yearly costs $2,400\nAnswers: audience on employer training budgets no; membership tied to ticket discounts yes; a genuine early-payment discount yes; someone tracks sales through the window no; second approver for major spend yes; tax question owner the fiscal host, by 2026-12-15",
"lines": "Cost lines (amount is the unit cost, the top of any range; total at the head count):\nRoom hire (year) | good | fixed | $2,400 | total $2,400 | quote 2026-11-01 | leaves 2026-12-20 | locks 2026-12-20\nPizza and drinks | good | per head | $9 x 600 | total $5,400 | quote 2026-11-03 | leaves 2027-01-20\nLivestream kit | better | fixed | $1,200 | total $1,200 | placeholder | leaves 2027-01-10\nStickers | better | per head | $0.50 x 600 | total $300 | quote 2026-11-04 | leaves 2027-01-10\nYear-end social | best | fixed | $1,500 | total $1,500 | quote 2026-11-08 | leaves 2027-01-05 | locks 2027-01-05",
"facts": "Format: free-by-construction; revenue posture: annual - Annual-funding-led; sponsor-led and registration-led deleted (no ticket line)\nOff the top: 7% of every inflow (host 5% + processing 2%)\nTargets: fundraising target at the best tier $13,312 (expenses $12,380 grossed up for fees); sponsorship entered $0 + annual $9,500; gap $3,812\nTiers (cumulative; expenses = lines + pad + floor + reserve contribution):\ngood | 2 lines | lines $7,800 | pad $780 | expenses $9,080 | sponsor $0 | target $9,763, gap $263 | cost per head $151\nbetter | 4 lines | lines $9,300 | pad $930 | expenses $10,730 | sponsor $0 | target $11,538, gap $2,038 | cost per head $179\nbest | 5 lines | lines $10,800 | pad $1,080 | expenses $12,380 | sponsor $0 | target $13,312, gap $3,812 | cost per head $206\nContingency stack (self-set, never an industry convention): floor $500; pad 10% ($1,080 at best; a convention from startup financial modelling, not event data); the downgrade ladder below\nReserve: breakeven; contribution line $0; outflow posture latest - Latest safe date\nCash schedule at the best tier (start $9,635):\n2026-12-20 | Room hire (year) | -$2,400 | balance $7,235\n2027-01-05 | Year-end social | -$1,500 | balance $5,735\n2027-01-10 | Livestream kit | -$1,200 | balance $4,535\n2027-01-10 | Stickers (edition 1) | -$30 | balance $4,505\n2027-01-20 | Pizza and drinks (edition 1) | -$540 | balance $3,965\n2027-02-10 | Stickers (edition 2) | -$30 | balance $3,935\n2027-02-20 | Pizza and drinks (edition 2) | -$540 | balance $3,395\n2027-03-10 | Stickers (edition 3) | -$30 | balance $3,365\n2027-03-20 | Pizza and drinks (edition 3) | -$540 | balance $2,825\n2027-04-10 | Stickers (edition 4) | -$30 | balance $2,795\n2027-04-20 | Pizza and drinks (edition 4) | -$540 | balance $2,255\n2027-05-10 | Stickers (edition 5) | -$30 | balance $2,225\n2027-05-20 | Pizza and drinks (edition 5) | -$540 | balance $1,685\n2027-06-10 | Stickers (edition 6) | -$30 | balance $1,655\n2027-06-20 | Pizza and drinks (edition 6) | -$540 | balance $1,115\n2027-07-10 | Stickers (edition 7) | -$30 | balance $1,085\n2027-07-20 | Pizza and drinks (edition 7) | -$540 | balance $545\n2027-08-10 | Stickers (edition 8) | -$30 | balance $515\n2027-08-20 | Pizza and drinks (edition 8) | -$540 | balance -$25\n2027-09-10 | Stickers (edition 9) | -$30 | balance -$55\n2027-09-20 | Pizza and drinks (edition 9) | -$540 | balance -$595\n2027-10-10 | Stickers (edition 10) | -$30 | balance -$625\n2027-10-20 | Pizza and drinks (edition 10) | -$540 | balance -$1,165\nLowest cash point: -$1,165 on 2027-10-20 (Pizza and drinks (edition 10)); end -$1,165\nCut order (pre-priced, net of sponsorship lost after fees):\nbest to better (discretionary extras) | saves $1,650 | sponsorship lost $0 | net $1,650 | last recoverable 2027-01-05 | Year-end social\nbetter to good (the expected niceties) | saves $1,650 | sponsorship lost $0 | net $1,650 | last recoverable no lock date | Livestream kit, Stickers\nPer-head lines locked by the checkpoint: none; fixed structural lines cut last: Room hire (year)\nTax question (never answered here): who confirms the treatment of ticket and sponsorship revenue in this jurisdiction, and by when? Owner: the fiscal host, by 2026-12-15\nGate (passes; 5 of 5): Every line a dated quote or an explicit placeholder settled; Every line carries the date it leaves settled; A computed lowest cash point settled; Fees off the top entered settled; A reserve policy written down settled\nSignals to record: Variance per line, planned against actual - per line, never one total (self-set); Lowest projected cash point (-$1,165 on 2027-10-20) against the lowest actual (self-set); Which tier the event actually ran at, against best as planned, and the sponsor revenue at that tier (self-set)\nFindings (6):\n[info] placeholders (lines): 1 line still says no quote yet (Livestream kit) - carried at the top of any range entered.\n[fix] membership_tied (posture): Membership tied to event ticket discounts - members join just before each event, the count spikes and income stays coupled to single events. Decouple membership and annual sponsorship from any one edition.\n[check] fund_gap (targets): Free-by-construction: the best tier needs $13,312 raised; the sponsor revenue entered leaves $3,812 to find - the only other lever is spend per head.\n[fix] trough (cash): The cash schedule goes to -$1,165 on 2027-10-20 (Pizza and drinks (edition 10)) - the plan does not cover its costs, and this is where the money runs out. Raise the target or take the cut order.\n[info] reserve_months (reserve): The reserve of $1,200 covers about 6 months of the organization's fixed yearly costs ($2,400) - the target is self-set.\n[info] gate (readiness): The gate: passes - every line is a dated quote or a labelled placeholder, and the lowest cash point is computed (5 of 5 settled)."
}
Reply:
{
"task": "shortfall",
"summary": "The first cut is best to better - dropping the year-end social - netting $1,650 after $0 sponsorship lost, and it must be decided by 2027-01-05.",
"rungs": [
{
"from": "best",
"to": "better",
"saving": 1650,
"sponsor_lost": 0,
"net": 1650,
"by": "2027-01-05",
"what": "Year-end social"
},
{
"from": "better",
"to": "good",
"saving": 1650,
"sponsor_lost": 0,
"net": 1650,
"by": "",
"what": "Livestream kit, Stickers"
}
],
"locked": [],
"last": "Room hire (year) is cut last because it is a fixed structural line already locked by 2026-12-20, and a late cut on a committed line can cost more than it saves.",
"checkpoints": [
{
"date": "2027-01-05",
"decide": "Decide whether to keep or cut the year-end social (best to better) against the sponsorship and annual funding received by then."
},
{
"date": "2027-01-15",
"decide": "At the first go/no-go checkpoint, decide whether the event proceeds at the planned tier against the revenue actually received by then."
}
],
"sponsor_note": "If Riverside Python Evenings steps down from best to better, the year-end social is dropped; stepping down further to good also drops the livestream kit and stickers. The sponsorship entered against these tiers is $0, so no sponsor revenue is lost at either step. What sponsors keep either way is the event's staff-free digital visibility surface, and no prices change for sponsors at any step.",
"team_note": "Before 2027-01-05, settle how to close the $3,812 gap between the $13,312 best-tier target and the $9,500 annual funding, since no additional sponsor revenue is entered yet.",
"caveats": [
"Membership is tied to event ticket discounts, coupling income to single editions - decouple membership and annual sponsorship from any one edition.",
"The cash schedule goes to -$1,165 on 2027-10-20 (Pizza and drinks, edition 10) - the plan does not cover its costs; raise the target or take the cut order.",
"Free-by-construction: the best tier needs $13,312 raised and the sponsor revenue entered leaves $3,812 to find - the only other lever is spend per head."
]
}
The page's check: kept - nothing to flag
The cash schedule CSV for the first sample
date,what,amount,balance
2026-12-01,Venue deposit,-6000,-1000
2027-02-01,Insurance,-900,-1900
2027-03-15,"sponsorship, net of fees",34200,32300
2027-04-20,Badges and lanyards,-1000,31300
2027-05-01,Internet,-1500,29800
2027-05-15,Speaker travel and hotel,-7200,22600
2027-05-20,Venue balance,-12000,10600
2027-05-20,"ticket revenue at paid capacity, net of fees",35640,46240
2027-05-27,Catering,-11250,34990
2027-06-09,Speakers' dinner and gifts,-1800,33190
2027-06-10,Recording and captioning,-6500,26690
2027-06-10,reserve contribution,-3000,23690
Build the input yourself
Load the engine in node (global.window = global; require("./budgetkit.js"); require("./budgetreply.js")), then:
const K = BudgetKit, R = BudgetReply;
const f = K.form({ name: "My Conf", showup: "250", ticketPrice: "180", hostPct: "7", feePct: "3", floor: "2000", reserve: "breakeven",
sponsorGood: "30000", sponsorBetter: "38000", sponsorBest: "41000", sponsorLands: "2027-03-15", ticketLands: "2027-05-20",
costs: fs.readFileSync("lines.csv", "utf8") }); // line, tier, per, amount, quote, pays, locks
// f.lines (per, total, quote label), f.posture, f.tiers (expenses, sponsor, breakeven or target), f.ticketTarget
// f.schedule, f.trough (the lowest cash point), f.rungs (saving, sponsorLost, net, by), f.locked, f.gate, f.findings
K.scheduleCSV(f) // date, what, amount, balance
const b = K.buildInput(f, "budget"); // or "shortfall" (planned at Better or Best)
const wire = K.toWire(b.input); // POST this to /estimate and /run
// then: const reply = R.normalize(R.parseReply(job.output.output), "budget");
// R.check(reply, b) // {status: kept or flagged, notes}
Get a token for the steps below from /tokens.html.
Step 1 — a tiny client helper
One function that sends JSON and unwraps the response. 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://cash-trough.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://cash-trough.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"]
// cash-trough.mjs -- Node 18+ (global fetch)
const BASE = "https://api.skillsafe.ai/v1/app-api";
let TOKEN = "paste-your-token-here"; // from https://cash-trough.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://cash-trough.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
}
// CashTrough.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 CashTrough {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static String token = "paste-your-token-here"; // from https://cash-trough.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://cash-trough.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://cash-trough.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://cash-trough.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":"cash-trough"}' | 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": "cash-trough"})["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: "cash-trough" })).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": "cash-trough"}, 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", "cash-trough"), 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: "cash-trough" })["token"])
// a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
$TOKEN = "";
$TOKEN = api("POST", "/guest", ["slug" => "cash-trough"])["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 = "cash-trough" }))!["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": "budget", "event": "Event: ...; conference; ticketed ...", "lines": "Cost lines (amount is the unit cost ...): ...", "facts": "Format: ticketed; revenue posture: ..."}
# - see the input table
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": "budget", "event": "Event: ...; conference; ticketed ...", "lines": "Cost lines (amount is the unit cost ...): ...", "facts": "Format: ticketed; revenue posture: ..."}
# - see the input table
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: "budget" or "shortfall", event, lines, facts} (see the input 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 lane and the input: a retry with the same key never bills twice. The page uses cash-trough:<task>:<inputHash>:a<attempt> - the lane is part of the key - and its one automatic reformat retry is a new attempt number, named in the page as one extra run. A budget carries summary, posture, lines, targets, tiers, contingency, reserve, cash, tax_question, signals and caveats; a shortfall plan carries summary, rungs, locked, last, checkpoints, sponsor_note, team_note and caveats. The code below prints the headline parts of either (list items are plain strings).
KEY="cash-trough:budget:$(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}'
jq -r '.summary // .format, .note // .tax_question, ((.signals // .caveats)[] | "\(.signal // .caveat): \(.text // .text)")' reply.json
jq -r '.sponsor_note // .note // empty' reply.json # the note to sponsors (shortfall)
import hashlib, time
key = "cash-trough:" + 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 reply as one JSON object
print(j["charged_credits"], j.get("truncated"), reply.get("summary"))
print(reply.get("note") or reply.get("tax_question", ""))
for s in reply.get("signals") or reply.get("caveats") or []:
print(s.get("signal", s.get("caveat")), "-", s.get("text", s.get("text")))
if reply.get("sponsor_note"):
print("sponsor note:", reply["sponsor_note"])
import { createHash } from "node:crypto";
const key = `cash-trough:${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.summary);
console.log(reply.note ?? reply.tax_question);
for (const s of reply.signals || reply.caveats || []) console.log(s.signal ?? s.caveat, "-", s.text ?? s.text);
if (reply.sponsor_note) console.log("sponsor note:", reply.sponsor_note);
sum := sha256.Sum256(raw) // add "crypto/sha256", "encoding/hex", "time" to the imports
key := "cash-trough:" + 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 }
}
var reply map[string]any
json.Unmarshal([]byte(j["output"].(map[string]any)["output"].(string)), &reply)
items, _ := reply["signals"].([]any) // signals and caveats are plain strings
fmt.Println(j["charged_credits"], j["truncated"], reply["summary"])
fmt.Println(reply["note"], reply["tax_question"])
for _, x := range items {
c := x.(map[string]any)
fmt.Println(c["signal"], "-", c["status"])
}
if fix, ok := reply["sponsor_note"].(string); ok && fix != "" {
fmt.Println("sponsor note:", fix)
}
var digest = java.security.MessageDigest.getInstance("SHA-256").digest(M.writeValueAsBytes(input));
String key = "cash-trough:" + 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.get("summary"));
System.out.println(reply.path("note").asText(reply.path("tax_question").asText()));
for (JsonNode s : reply.has("signals") ? reply.path("signals") : reply.path("caveats")) System.out.println(s.path("signal").asText(s.path("caveat").asText()) + " - " + s.path("status").asText(s.path("text").asText()));
if (!reply.path("sponsor_note").asText().isEmpty()) System.out.println("sponsor note: " + reply.get("sponsor_note").asText());
require "digest"
key = "cash-trough:#{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["summary"]].inspect
puts reply["note"] || reply["tax_question"]
(reply["signals"] || reply["caveats"] || []).each { |s| puts "#{s["signal"] || s["caveat"]} - #{s["text"] || s["text"]}" }
puts "sponsor note: #{reply["sponsor_note"]}" unless reply["sponsor_note"].to_s.empty?
$key = "cash-trough:" . $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["summary"] ?? ""), "\n";
echo $reply["note"] ?? $reply["tax_question"] ?? "", "\n";
foreach ($reply["signals"] ?? $reply["caveats"] ?? [] as $s) echo $s["signal"] ?? $s["caveat"], " - ", $s["text"] ?? $s["text"], "\n";
if (!empty($reply["sponsor_note"])) echo "sponsor note: ", $reply["sponsor_note"], "\n";
var digest = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(input.ToJsonString()));
var key = $"cash-trough:{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!["summary"]}");
Console.WriteLine($"{reply["note"] ?? reply["tax_question"]}");
foreach (var s in (reply["signals"] ?? reply["caveats"]) as JsonArray ?? new JsonArray()) Console.WriteLine($"{s!["signal"] ?? s["caveat"]} - {s["text"] ?? s["text"]}");
if (reply["sponsor_note"] is JsonNode fix && fix.GetValue<string>() != "") Console.WriteLine("sponsor note: " + fix);
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: cash-trough:budget:$(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": "budget", "event": "Event: ...; conference; ticketed ...", "lines": "Cost lines (amount is the unit cost ...): ...", "facts": "Format: ticketed; revenue posture: ..."}
# - see the input table
req = urllib.request.Request(BASE + "/run-stream", method="POST", data=json.dumps(rev).encode(),
headers={"Content-Type": "application/json", "Authorization": "Bearer " + TOKEN,
"Idempotency-Key": "cash-trough:budget:" + 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": `cash-trough:budget:${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", "cash-trough:budget:"+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", "cash-trough:budget:" + 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" => "cash-trough:budget:#{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: cash-trough:budget:" . 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", $"cash-trough:budget:{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);