Run Event Shape from your own code
Both metered lanes are available over HTTP and each returns one JSON object. The division of labour: the page fixes the value axis, ranks the shape ladder with its deleted and starved rungs, picks the program-sourcing posture and floor shape, and holds the sponsor activity layer to coherence and outside the CFP - all free, in shapekit.js. The run writes the brief or the memo; the page then holds the reply to its own picks and rejects any number (shapereply.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":"event-shape"}), 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, company, sponsors, 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 - brief (the shape brief for the team) and memo (the memo to the executive who signs it off, company-run only). A missing or unknown task is answered as brief. Both lanes send the same four fields.
The input
| Field | Required | What |
|---|---|---|
task | yes | brief or memo. If it is missing or neither, do brief and set "task": "brief". |
company | yes | the event, the pole, the intended attendee, the goal and list, the audience evidence, the launch, who is marketed to, what already exists, and the shape and program on the table. |
sponsors | yes | the sponsor segments with what each buys, the planned activities with their placement, and the prospectus claims. |
facts | yes | the value axis, the shape ladder with its pick, candidates in order and rungs, the program-sourcing and floor menus, the activity layer, the gates, the signals and every finding tagged [fix], [check] or [info], most urgent first. |
retry_note | no | optional; if present, your previous reply was unusable - follow it. |
The rules the model applies
- Say company-run and community-run, whatever the user calls it. A community-run event does not get the company ladder; only the sponsor activity layer runs for it.
- The value axis comes first: volume of qualified conversations ranks shapes on reach; penetration of a named account list ranks them on account depth. The two axes are exact inverses, so without the answer the ladder is unordered and any recommendation is really "run the cheapest thing".
- The ladder: an invite-only dinner or roundtable tops account depth and is the only shape committable inside a quarter - deleted when there is no named account list; an executive summit is the step up once the list outgrows one table; a single-city user conference is the default for volume when an audience will travel to you, and the only shape that becomes an owned annual property; a multi-city roadshow leads reach per unit of effort because later stops reuse one deck, crew and run of show, and it needs a per-city stop rule fixed before the first city; a floor-primary event tops reach and effort and wins only when no audience travels to you and the ecosystem is what you sell. A field team already on the road promotes the roadshow; an advisory board already meeting promotes the summit. Small is not low-risk: hosted hospitality lands on the guest's employer's gift policy.
- Program sourcing: a mixed practitioner program with labelled company slots by default; a customer-story program when an advisory board already meets (reference approvals cost real time); open submissions with the company as named host only for an annual property with the political capital to refuse an executive's slot; a company-supplied program is legitimate for customers who came for the roadmap, and deleted when people who are not customers yet are marketed to. What a posture entitles the event to claim is a positioning decision, not this one.
- Floor shape, only for a floor-primary event: an own showcase floor by default; a program-plus-floor event when the ecosystem is the story; presence on a third party's floor when you have no audience of your own - and the expo-floor guidance serves the hall's owner, not an exhibitor, so say so. Layout, tiers and staffing belong to the floor owner.
- The activity layer, beside the program and never inside the CFP: every activity answers a category a sponsor segment buys, and every category the prospectus claims has an activity. A hiring corner when a segment recruits; an opt-in demo space when a segment sells; a hands-on product feedback session when a segment buys feedback and sends product people, with consent before recording; matchmaking only when attendees ask for meetings and someone runs the scheduling (double opt-in at a community-run event, never a list). Do not copy another event's recipe.
- State no headcount, city count, seat count, agenda length, ticket price or budget figure - every such number would be invented. Every gate is set in advance, never read off a benchmark.
House rules
- Use only the input and the rules above. The page's picks, candidates, rungs and names are exact - copy them. Write no digits at all - no counts, prices, dates or percentages - except inside a name the input gives.
- Label anything that is not one of the rules above, and is not in the input, as your own design.
- Never invent a person, a company fact, a figure or a commitment; where someone must be named and is not, write "to be named". Refer to people by name or as they - never infer anyone's gender from a name, possessives included ("their slot", never his or her).
- 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
brief
summary- one sentence, at most 35 words: the recommendation and the first thing to settle.axis-pick(reach,depthor "not applicable", the page's),why(one sentence from the goal).candidates- one object per candidate shape in the page's order:shape(dinner,summit,userconf,roadshoworfloor),tradeoff(one sentence: what it buys and what it costs on this axis).recommendation- the page's pick (dinner,summit,userconf,roadshow,flooror "not applicable").deleted- one object per rung the page marks deleted, on any menu:rung(its name),reason(one sentence).sourcing-pick(open,mixed,customer,companyor "not applicable", the page's),note(one sentence; what it may claim is handed to positioning).floor-pick(programfloor,showcase,thirdpartyor "not applicable", the page's),note(one sentence).activities- one object per activity in the page's activity layer, in order:activity(hiring,demo,feedbackormatchmaking),segments(the page's segments for it, exactly),placement(where it runs beside the program - the planned placement when one exists, otherwise propose one, your design, such as a break area or a separate room; never a CFP session),note(one short sentence, including what must be true first when the page marks it not yet or needing product people).[]when the layer does not apply.flips- 2 to 4 objects:choice(one ofaxis,shape,sourcing,floor,activities),flip(one sentence: the answer that would flip it next year).handoffs- 2 to 4 short strings: the questions handed to goal-and-funding strategy, to format selection (track count, session mix), and to the floor owner when a floor applies.checks- one object per gate in the page's order:check(the page's short name, exactly),pass(true or false, as the page says).caveats- at most 4 short strings:[fix]findings first in the page's order (the first four when there are more), then[check]findings in the page's order.[]when there are none of either.
{
"task": "brief",
"summary": "one sentence",
"axis": { "pick": "reach", "why": "one sentence" },
"candidates": [ { "shape": "userconf", "tradeoff": "one sentence" } ],
"recommendation": "userconf",
"deleted": [ { "rung": "Invite-only dinner or roundtable", "reason": "one sentence" } ],
"sourcing": { "pick": "mixed", "note": "one sentence" },
"floor": { "pick": "not applicable", "note": "one sentence" },
"activities": [ { "activity": "hiring", "segments": "a segment", "placement": "beside the coffee", "note": "one short sentence" } ],
"flips": [ { "choice": "shape", "flip": "one sentence" } ],
"handoffs": [ "a question handed on" ],
"checks": [ { "check": "Value axis", "pass": true } ],
"caveats": [ "a caveat" ]
}
The page holds it to its own picks: the axis, the candidates in order, the recommendation, every deleted rung named, the program and floor, the activity layer beside the program, and the gates; any digit is flagged, since every count or budget would be invented.
memo
to- the executive ("to be named").subject- specific: the shape and the goal it serves.recommendation- one sentence naming the page's shape by its full name and the axis it serves.why- two or three sentences: why this shape moves the chosen axis, and why the alternatives do not.will_not- one or two sentences: what this shape will not do, said plainly.ask- one or two sentences: what the executive must decide or commit (for example the political capital to refuse a slot, a per-city stop rule, a named account list) - only what the page's findings call for.measure- one sentence: the gate written down in advance that will say whether the shape was right.note- one sentence, at most 30 words, for the organizer: what to settle before sending.
{
"task": "memo",
"to": "to be named",
"subject": "a specific subject",
"recommendation": "one sentence",
"why": "two or three sentences",
"will_not": "one or two sentences",
"ask": "one or two sentences",
"measure": "one sentence",
"note": "one sentence"
}
The page requires the shape by its full name, what it will not do, an ask (the per-city stop rule for a roadshow, the program change when a company-supplied program meets prospects) and a gate set in advance, with no digit anywhere.
Worked examples - every lane, every sample
These are the page's three free samples (fictional companies and events; no figures). 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. The community-run sample has no memo: the executive memo is for a company-run shape decision.
Quillmoor Platform Days (marketing wants a user conference) - brief
company-run; axis reach; single-city user conference; 2 sponsor activities; 4 of 6 gates.
Input:
{
"task": "brief",
"company": "Event: Quillmoor Platform Days; company-run; intended attendee existing customers; goal volume of qualified conversations\nAudience that will travel to you: yes; a fixed-date launch to carry yes; marketed to people who are not customers yet yes; a compounding annual property; political capital to refuse an executive's slot no\nAlready exists: field-marketing team on the road no; a venue you control no; partners exhibiting elsewhere yes; a customer advisory board already meeting no; a recorded-content pipeline yes; selling this year: the product\nOn the table: proposed shape Single-city user conference; proposed program Company-supplied program; roadshow stop rule set no",
"sponsors": "Sponsor segments, in order (segment | buys | sends product people | attendees ask for meetings):\n Cloud partners | partnerships | no | yes\n Hiring startups | recruiting | no | no\nPlanned activities, in order (activity | placement | local alternatives already run it):\n Partner demo talks | CFP session | no | INSIDE THE CFP PROGRAM\n Hiring corner | beside the coffee | yes\nProspectus claims: recruiting, selling, partnerships; someone to run meeting scheduling no",
"facts": "Value axis: reach (volume)\nShape: the page's pick is userconf; candidates in order userconf, roadshow, summit; rungs Invite-only dinner or roundtable deleted; Executive summit available; Single-city user conference promoted; Multi-city roadshow available; Floor-primary event starved\nProgram sourcing: the page's pick is mixed; rungs Open submissions with the company as named host starved; Mixed practitioner program with labelled company slots default; Customer-story program available; Company-supplied program deleted\nFloor shape: not applicable - the shape is not floor-primary\nActivity layer: Matchmaking for Cloud partners (not yet); Hiring corner for Hiring startups (default); every activity beside the program, never in the CFP\nChecks (4 of 6): Value axis (The value axis - volume or a named account list - is fixed before the ladder is ranked) passed; Proposed shape (The shape on the table survives the deletions and fits the axis) passed; Honest program (No company-supplied program is marketed to people who are not customers yet) MISSING; Activity coherence (Every activity answers a category a sponsor segment buys, and every category claimed has an activity) MISSING; Outside the CFP (No sponsor activity runs inside the CFP program) passed; Stop rule (A roadshow has its per-city stop rule fixed before the first city) passed\nSignals to record: People reached on the volume axis, against a level written down in advance; Company slots on the program against the plan (sourcing drift); Per activity: sponsors who used it and attendees who opted in, against a level fixed in advance\nFindings (12):\n[fix] company_to_prospects (sourcing): A company-supplied program marketed to people who are not customers yet is deleted - it fails the neutrality test; change the program or the marketing.\n[fix] orphan_activity (activities): No sponsor segment buys: Partner demo talks - delete it; an activity nobody buys becomes staffed empty space.\n[fix] claim_without_activity (activities): The prospectus claims partnerships, and no activity stages it - one activity per category claimed, or renewals fail.\n[check] unplanned (activities): Segments buy what no planned activity delivers: Matchmaking (Cloud partners).\n[check] matchmaking_wait (activities): Matchmaking needs attendees who ask for meetings and someone to run the scheduling - until then it is the starved rung.\n[check] checks (measurement): Shape gates: 4 of 6 (all set in advance, no benchmark).\n[info] dinner_deleted (shape): The invite-only dinner is deleted - with no named account list it buys neither reach nor depth.\n[info] launch (shape): A fixed-date launch must ride on this event - the program posture weighs message control against credibility.\n[info] open_collapse (sourcing): Open submissions need the political capital to tell an executive their slot did not make the program - without it the posture collapses into a company-supplied program with extra administration.\n[info] labelled (sourcing): Mixed program: label the company slots as company slots - never dress them as neutral content; what the posture entitles the event to claim is a positioning decision.\n[info] local_copy (activities): Local alternatives already run Hiring corner - an activity nobody nearby runs differentiates the conference more.\n[info] hiring_data (activities): At a hiring corner, contact details and CVs move only when the attendee hands them over."
}
Reply:
{
"task": "brief",
"summary": "Recommend the single-city user conference for reach; settle first that a company-supplied program cannot be marketed to people who are not customers yet without failing the neutrality test.",
"axis": {
"pick": "reach",
"why": "The goal is volume of qualified conversations, which ranks shapes by reach."
},
"candidates": [
{
"shape": "userconf",
"tradeoff": "Single-city user conference buys reach from an audience that already travels to you and can become an owned annual property, at the cost of the deeper reach per unit of effort a roadshow would gain from reused stops."
},
{
"shape": "roadshow",
"tradeoff": "Multi-city roadshow leads reach per unit of effort by reusing one deck, crew and run of show across stops, but costs the owned annual property a user conference builds, and needs a per-city stop rule fixed before the first city."
},
{
"shape": "summit",
"tradeoff": "Executive summit buys account depth once a named list outgrows one table, but costs the reach this axis asks for since it is sized to a room, not a travelling audience."
}
],
"recommendation": "userconf",
"deleted": [
{
"rung": "Invite-only dinner or roundtable",
"reason": "There is no named account list, and the dinner is deleted without one."
},
{
"rung": "Company-supplied program",
"reason": "People who are not customers yet are marketed to this event, so a company-supplied program is deleted."
}
],
"sourcing": {
"pick": "mixed",
"note": "Company slots must be labelled as company slots, not dressed as neutral content, and what the posture may claim beyond that is a positioning decision."
},
"floor": {
"pick": "not applicable",
"note": "The shape is not floor-primary, so no floor shape applies."
},
"activities": [
{
"activity": "matchmaking",
"segments": "Cloud partners",
"placement": "a dedicated meeting room away from the main program (own design)",
"note": "Attendees already ask for meetings; what is missing first is someone named to run the scheduling."
},
{
"activity": "hiring",
"segments": "Hiring startups",
"placement": "beside the coffee",
"note": "Local alternatives already run a hiring corner, so this one should aim to stand apart."
}
],
"flips": [
{
"choice": "axis",
"flip": "If the goal shifts from volume to penetrating a named account list, the axis would flip from reach to depth."
},
{
"choice": "shape",
"flip": "A field-marketing team going on the road, or an advisory board beginning to meet, would promote the roadshow or the summit over the user conference."
},
{
"choice": "sourcing",
"flip": "Gaining the political capital to refuse an executive's slot would open the door to open submissions as named host."
},
{
"choice": "activities",
"flip": "Naming someone to run meeting scheduling would move matchmaking out of not yet for Cloud partners."
}
],
"handoffs": [
"Goal-and-funding strategy: should marketing narrow to existing customers, or should program sourcing move off company-supplied so prospects can keep being marketed to?",
"Format selection: how many tracks and what session mix the single-city user conference should run."
],
"checks": [
{
"check": "Value axis",
"pass": true
},
{
"check": "Proposed shape",
"pass": true
},
{
"check": "Honest program",
"pass": false
},
{
"check": "Activity coherence",
"pass": false
},
{
"check": "Outside the CFP",
"pass": true
},
{
"check": "Stop rule",
"pass": true
}
],
"caveats": [
"A company-supplied program marketed to people who are not customers yet is deleted for failing the neutrality test; change the program or the marketing.",
"No sponsor segment buys Partner demo talks; delete it before it becomes staffed empty space.",
"The prospectus claims partnerships, and no activity stages it; one activity per category claimed, or renewals fail.",
"Segments buy what no planned activity delivers: Matchmaking for Cloud partners."
]
}
The page's check: kept - nothing to flag
Quillmoor Platform Days (marketing wants a user conference) - memo
company-run; axis reach; single-city user conference; 2 sponsor activities; 4 of 6 gates.
Input:
{
"task": "memo",
"company": "Event: Quillmoor Platform Days; company-run; intended attendee existing customers; goal volume of qualified conversations\nAudience that will travel to you: yes; a fixed-date launch to carry yes; marketed to people who are not customers yet yes; a compounding annual property; political capital to refuse an executive's slot no\nAlready exists: field-marketing team on the road no; a venue you control no; partners exhibiting elsewhere yes; a customer advisory board already meeting no; a recorded-content pipeline yes; selling this year: the product\nOn the table: proposed shape Single-city user conference; proposed program Company-supplied program; roadshow stop rule set no",
"sponsors": "Sponsor segments, in order (segment | buys | sends product people | attendees ask for meetings):\n Cloud partners | partnerships | no | yes\n Hiring startups | recruiting | no | no\nPlanned activities, in order (activity | placement | local alternatives already run it):\n Partner demo talks | CFP session | no | INSIDE THE CFP PROGRAM\n Hiring corner | beside the coffee | yes\nProspectus claims: recruiting, selling, partnerships; someone to run meeting scheduling no",
"facts": "Value axis: reach (volume)\nShape: the page's pick is userconf; candidates in order userconf, roadshow, summit; rungs Invite-only dinner or roundtable deleted; Executive summit available; Single-city user conference promoted; Multi-city roadshow available; Floor-primary event starved\nProgram sourcing: the page's pick is mixed; rungs Open submissions with the company as named host starved; Mixed practitioner program with labelled company slots default; Customer-story program available; Company-supplied program deleted\nFloor shape: not applicable - the shape is not floor-primary\nActivity layer: Matchmaking for Cloud partners (not yet); Hiring corner for Hiring startups (default); every activity beside the program, never in the CFP\nChecks (4 of 6): Value axis (The value axis - volume or a named account list - is fixed before the ladder is ranked) passed; Proposed shape (The shape on the table survives the deletions and fits the axis) passed; Honest program (No company-supplied program is marketed to people who are not customers yet) MISSING; Activity coherence (Every activity answers a category a sponsor segment buys, and every category claimed has an activity) MISSING; Outside the CFP (No sponsor activity runs inside the CFP program) passed; Stop rule (A roadshow has its per-city stop rule fixed before the first city) passed\nSignals to record: People reached on the volume axis, against a level written down in advance; Company slots on the program against the plan (sourcing drift); Per activity: sponsors who used it and attendees who opted in, against a level fixed in advance\nFindings (12):\n[fix] company_to_prospects (sourcing): A company-supplied program marketed to people who are not customers yet is deleted - it fails the neutrality test; change the program or the marketing.\n[fix] orphan_activity (activities): No sponsor segment buys: Partner demo talks - delete it; an activity nobody buys becomes staffed empty space.\n[fix] claim_without_activity (activities): The prospectus claims partnerships, and no activity stages it - one activity per category claimed, or renewals fail.\n[check] unplanned (activities): Segments buy what no planned activity delivers: Matchmaking (Cloud partners).\n[check] matchmaking_wait (activities): Matchmaking needs attendees who ask for meetings and someone to run the scheduling - until then it is the starved rung.\n[check] checks (measurement): Shape gates: 4 of 6 (all set in advance, no benchmark).\n[info] dinner_deleted (shape): The invite-only dinner is deleted - with no named account list it buys neither reach nor depth.\n[info] launch (shape): A fixed-date launch must ride on this event - the program posture weighs message control against credibility.\n[info] open_collapse (sourcing): Open submissions need the political capital to tell an executive their slot did not make the program - without it the posture collapses into a company-supplied program with extra administration.\n[info] labelled (sourcing): Mixed program: label the company slots as company slots - never dress them as neutral content; what the posture entitles the event to claim is a positioning decision.\n[info] local_copy (activities): Local alternatives already run Hiring corner - an activity nobody nearby runs differentiates the conference more.\n[info] hiring_data (activities): At a hiring corner, contact details and CVs move only when the attendee hands them over."
}
Reply:
{
"task": "memo",
"to": "to be named",
"subject": "Sign-off needed: Single-city user conference for Quillmoor Platform Days, to grow the volume of qualified conversations",
"recommendation": "Recommend running Quillmoor Platform Days as a Single-city user conference, the shape that serves the volume-of-qualified-conversations axis this event is built on.",
"why": "A single-city user conference is the default shape for volume when the audience will already travel to you, which this audience will, and it is also the only shape that can become an owned annual property, matching the compounding property this event is meant to be. A multi-city roadshow reaches more people per unit of effort by reusing one deck, crew and run of show across stops, but there is no field-marketing team already on the road to make that the natural move here. An executive summit tops account depth rather than reach, so it does not serve the axis this event is built around.",
"will_not": "This shape will not reach as many people per unit of effort as a multi-city roadshow could, and it will not grow penetration into any named account list the way an invite-only dinner or an executive summit would.",
"ask": "Decide whether to keep marketing to people who are not customers yet, in which case the program must move off the proposed company-supplied content onto the mixed practitioner program with labelled company slots, and commit to naming someone to run partner meeting scheduling if the partnerships claim stays in the prospectus.",
"measure": "Success will be measured by the volume of qualified conversations reached, against a level fixed in advance rather than any benchmark read off after the fact.",
"note": "Settle the program-sourcing conflict with the prospect marketing and give the partnerships claim an activity before sending this memo."
}
The page's check: kept - nothing to flag
Ledgerline (a handful of target accounts, one quarter) - brief
company-run; axis depth; invite-only dinner or roundtable; 0 sponsor activities; 5 of 6 gates.
Input:
{
"task": "brief",
"company": "Event: Ledgerline accounts push; company-run; intended attendee buyers inside a target-account list; goal penetration of a named account list (a handful of named accounts)\nAudience that will travel to you: no; a fixed-date launch to carry no; marketed to people who are not customers yet yes; a one-off bet; political capital to refuse an executive's slot no\nAlready exists: field-marketing team on the road yes; a venue you control no; partners exhibiting elsewhere no; a customer advisory board already meeting no; a recorded-content pipeline no; selling this year: the product\nOn the table: proposed shape Multi-city roadshow; proposed program Mixed practitioner program with labelled company slots; roadshow stop rule set no",
"sponsors": "Sponsors: not a sponsored conference - the activity layer does not apply",
"facts": "Value axis: account depth (named accounts)\nShape: the page's pick is dinner; candidates in order dinner, summit, userconf; rungs Invite-only dinner or roundtable default; Executive summit available; Single-city user conference available; Multi-city roadshow available; Floor-primary event starved\nProgram sourcing: the page's pick is mixed; rungs Open submissions with the company as named host starved; Mixed practitioner program with labelled company slots default; Customer-story program available; Company-supplied program deleted\nFloor shape: not applicable - the shape is not floor-primary\nActivity layer: not applicable\nChecks (5 of 6): Value axis (The value axis - volume or a named account list - is fixed before the ladder is ranked) passed; Proposed shape (The shape on the table survives the deletions and fits the axis) MISSING; Honest program (No company-supplied program is marketed to people who are not customers yet) passed; Activity coherence (Every activity answers a category a sponsor segment buys, and every category claimed has an activity) passed; Outside the CFP (No sponsor activity runs inside the CFP program) passed; Stop rule (A roadshow has its per-city stop rule fixed before the first city) passed\nSignals to record: Named accounts touched - a decision-maker in a real conversation - against a level written down in advance; Company slots on the program against the plan (sourcing drift)\nFindings (4):\n[check] proposed_axis (shape): The proposed multi-city roadshow is not among the candidates for the named-account axis - it scores on the axis not picked.\n[check] checks (measurement): Shape gates: 5 of 6 (all set in advance, no benchmark).\n[info] hospitality (compliance): Hosted hospitality for a named guest lands on that guest's employer's gift policy, and an accepted invitation cannot be quietly withdrawn - small is not low-risk.\n[info] labelled (sourcing): Mixed program: label the company slots as company slots - never dress them as neutral content; what the posture entitles the event to claim is a positioning decision."
}
Reply:
{
"task": "brief",
"summary": "Recommend the invite-only dinner or roundtable for account depth; settle first that the proposed multi-city roadshow scores on reach, not the named-account axis this event needs.",
"axis": {
"pick": "depth",
"why": "The goal is penetration of a named account list, which ranks shapes by account depth."
},
"candidates": [
{
"shape": "dinner",
"tradeoff": "Invite-only dinner or roundtable tops account depth and is the only shape committable inside a quarter, at the cost of reaching only the room it seats."
},
{
"shape": "summit",
"tradeoff": "Executive summit is the step up once the named list outgrows one table, buying more account depth at the cost of the intimacy and quick commitment a dinner offers."
},
{
"shape": "userconf",
"tradeoff": "Single-city user conference is the default for volume when an audience travels to you, buying reach at the cost of the account-level depth this axis asks for."
}
],
"recommendation": "dinner",
"deleted": [
{
"rung": "Company-supplied program",
"reason": "People who are not customers yet are marketed to this event, so a company-supplied program is deleted."
}
],
"sourcing": {
"pick": "mixed",
"note": "Company slots must be labelled as company slots, not dressed as neutral content, and what the posture may claim beyond that is a positioning decision."
},
"floor": {
"pick": "not applicable",
"note": "The shape is not floor-primary, so no floor shape applies."
},
"activities": [],
"flips": [
{
"choice": "axis",
"flip": "If the goal shifts from penetrating a named account list to volume of qualified conversations, the axis would flip from depth to reach."
},
{
"choice": "shape",
"flip": "If the named account list outgrows one table, the shape would step up from the dinner to the executive summit."
},
{
"choice": "shape",
"flip": "Because a field team is already on the road, a shift to the reach axis would make the multi-city roadshow on the table a real candidate."
},
{
"choice": "sourcing",
"flip": "Gaining the political capital to refuse an executive's slot would let open submissions replace the mixed program."
}
],
"handoffs": [
"Goal-and-funding strategy: what level of named-account penetration this one-off bet must reach before it is judged worthwhile.",
"Format selection: how the evening is structured across the named accounts, one table or several, and who hosts each."
],
"checks": [
{
"check": "Value axis",
"pass": true
},
{
"check": "Proposed shape",
"pass": false
},
{
"check": "Honest program",
"pass": true
},
{
"check": "Activity coherence",
"pass": true
},
{
"check": "Outside the CFP",
"pass": true
},
{
"check": "Stop rule",
"pass": true
}
],
"caveats": [
"The proposed multi-city roadshow is not among the candidates for the named-account axis; it scores on the axis not picked.",
"Not every shape gate passes yet, though each was set in advance with no benchmark."
]
}
The page's check: kept - nothing to flag
Ledgerline (a handful of target accounts, one quarter) - memo
company-run; axis depth; invite-only dinner or roundtable; 0 sponsor activities; 5 of 6 gates.
Input:
{
"task": "memo",
"company": "Event: Ledgerline accounts push; company-run; intended attendee buyers inside a target-account list; goal penetration of a named account list (a handful of named accounts)\nAudience that will travel to you: no; a fixed-date launch to carry no; marketed to people who are not customers yet yes; a one-off bet; political capital to refuse an executive's slot no\nAlready exists: field-marketing team on the road yes; a venue you control no; partners exhibiting elsewhere no; a customer advisory board already meeting no; a recorded-content pipeline no; selling this year: the product\nOn the table: proposed shape Multi-city roadshow; proposed program Mixed practitioner program with labelled company slots; roadshow stop rule set no",
"sponsors": "Sponsors: not a sponsored conference - the activity layer does not apply",
"facts": "Value axis: account depth (named accounts)\nShape: the page's pick is dinner; candidates in order dinner, summit, userconf; rungs Invite-only dinner or roundtable default; Executive summit available; Single-city user conference available; Multi-city roadshow available; Floor-primary event starved\nProgram sourcing: the page's pick is mixed; rungs Open submissions with the company as named host starved; Mixed practitioner program with labelled company slots default; Customer-story program available; Company-supplied program deleted\nFloor shape: not applicable - the shape is not floor-primary\nActivity layer: not applicable\nChecks (5 of 6): Value axis (The value axis - volume or a named account list - is fixed before the ladder is ranked) passed; Proposed shape (The shape on the table survives the deletions and fits the axis) MISSING; Honest program (No company-supplied program is marketed to people who are not customers yet) passed; Activity coherence (Every activity answers a category a sponsor segment buys, and every category claimed has an activity) passed; Outside the CFP (No sponsor activity runs inside the CFP program) passed; Stop rule (A roadshow has its per-city stop rule fixed before the first city) passed\nSignals to record: Named accounts touched - a decision-maker in a real conversation - against a level written down in advance; Company slots on the program against the plan (sourcing drift)\nFindings (4):\n[check] proposed_axis (shape): The proposed multi-city roadshow is not among the candidates for the named-account axis - it scores on the axis not picked.\n[check] checks (measurement): Shape gates: 5 of 6 (all set in advance, no benchmark).\n[info] hospitality (compliance): Hosted hospitality for a named guest lands on that guest's employer's gift policy, and an accepted invitation cannot be quietly withdrawn - small is not low-risk.\n[info] labelled (sourcing): Mixed program: label the company slots as company slots - never dress them as neutral content; what the posture entitles the event to claim is a positioning decision."
}
Reply:
{
"task": "memo",
"to": "to be named",
"subject": "Sign-off needed: Invite-only dinner or roundtable for the Ledgerline accounts push, to grow penetration of the named account list",
"recommendation": "Recommend running the Ledgerline accounts push as an Invite-only dinner or roundtable, the shape that serves penetration of the named account list this push is built on.",
"why": "An invite-only dinner or roundtable tops account depth and is the only shape committable within a quarter, which fits a push built around a handful of named accounts. An executive summit and a single-city user conference are both available if the list later grows past what one table can hold, but today's list does not call for either yet. The multi-city roadshow already on the table is not even among the shapes ranked for account depth - it scores on reach, the axis this event does not serve.",
"will_not": "This small-format shape will not reach broad volume audiences, and it will not expand to serve more than a handful of named accounts without stepping up the ladder.",
"ask": "Decide to drop the proposed multi-city roadshow in favor of the invite-only dinner or roundtable, since the roadshow does not rank among the shapes for the account-depth axis this push is built on, and commit to checking each named account's employer gift policy before any hosted invitation goes out, since an accepted invitation cannot be quietly withdrawn.",
"measure": "Success will be measured by how many named accounts reach a real conversation with a decision-maker, against a level fixed before the event rather than a benchmark discovered afterward.",
"note": "Settle the shape mismatch on the roadshow proposal and confirm gift-policy clearance for each named account before sending this memo."
}
The page's check: kept - nothing to flag
Fennowick DevConf (a community conference, sponsors ask what they get) - brief
community-run; axis none; no company shape; 3 sponsor activities; 4 of 6 gates.
Input:
{
"task": "brief",
"company": "Event: Fennowick DevConf; community-run\nCompany ladder: not applicable - community-run",
"sponsors": "Sponsor segments, in order (segment | buys | sends product people | attendees ask for meetings):\n Scale-ups hiring | recruiting | no | no\n Dev tool vendors | feedback | no | no\n Cloud vendors | selling | no | no\nPlanned activities, in order (activity | placement | local alternatives already run it):\n Demo booth row | expo hall | yes\n Sponsor lightning talks | CFP talk slot | no | INSIDE THE CFP PROGRAM\nProspectus claims: recruiting, feedback, selling; someone to run meeting scheduling no",
"facts": "Activity layer: Hiring corner for Scale-ups hiring (default); Hands-on product feedback session for Dev tool vendors (needs product people); Opt-in demo space for Cloud vendors (default); every activity beside the program, never in the CFP\nChecks (4 of 6): Value axis (The value axis - volume or a named account list - is fixed before the ladder is ranked) passed; Proposed shape (The shape on the table survives the deletions and fits the axis) passed; Honest program (No company-supplied program is marketed to people who are not customers yet) passed; Activity coherence (Every activity answers a category a sponsor segment buys, and every category claimed has an activity) MISSING; Outside the CFP (No sponsor activity runs inside the CFP program) MISSING; Stop rule (A roadshow has its per-city stop rule fixed before the first city) passed\nSignals to record: People reached on the volume axis, against a level written down in advance; Company slots on the program against the plan (sourcing drift); Per activity: sponsors who used it and attendees who opted in, against a level fixed in advance\nFindings (7):\n[fix] in_cfp (activities): Sponsor lightning talks is scheduled inside the CFP program - a sold program slot whatever it is called; move it beside the program (breaks, a separate room, a side slot).\n[fix] claim_without_activity (activities): The prospectus claims recruiting and feedback, and no activity stages them - one activity per category claimed, or renewals fail.\n[check] unplanned (activities): Segments buy what no planned activity delivers: Hiring corner (Scale-ups hiring); Hands-on product feedback session (Dev tool vendors).\n[check] feedback_sales (activities): A product-feedback session for Dev tool vendors needs the sponsor's product people, not sales - and consent before recording anything.\n[check] checks (measurement): Shape gates: 4 of 6 (all set in advance, no benchmark).\n[info] community_layer (pole): A community-run conference: the company-run ladder is routed away; only the sponsor activity layer below runs, with meetings attendee-initiated or double opt-in only.\n[info] local_copy (activities): Local alternatives already run Demo booth row - an activity nobody nearby runs differentiates the conference more."
}
Reply:
{
"task": "brief",
"summary": "For this community-run event only the sponsor activity layer applies; settle first that sponsor lightning talks must move out of the CFP program before any sponsor claim is honest.",
"axis": {
"pick": "not applicable",
"why": "This is a community-run event, so the company ladder's value axis does not apply; only the sponsor activity layer runs."
},
"candidates": [],
"recommendation": "not applicable",
"deleted": [],
"sourcing": {
"pick": "not applicable",
"note": "Program sourcing is not applicable to a community-run event; only the sponsor activity layer applies."
},
"floor": {
"pick": "not applicable",
"note": "Floor shape is not applicable to a community-run event; only the sponsor activity layer applies."
},
"activities": [
{
"activity": "hiring",
"segments": "Scale-ups hiring",
"placement": "a dedicated area beside the breaks, away from the CFP program (own design)",
"note": "No planned activity currently delivers this for the segment that buys recruiting."
},
{
"activity": "feedback",
"segments": "Dev tool vendors",
"placement": "a separate room set aside from the CFP program (own design)",
"note": "This needs the sponsor's product people, not sales, in the room, with consent secured before anything is recorded."
},
{
"activity": "demo",
"segments": "Cloud vendors",
"placement": "expo hall",
"note": "Local alternatives already run a demo booth row nearby, so this space should aim to stand apart."
}
],
"flips": [
{
"choice": "axis",
"flip": "If this event became company-run, a goal and either a volume target or a named account list would need to be set before any axis could be picked."
},
{
"choice": "activities",
"flip": "If attendees begin asking for meetings, matchmaking could be added for whichever segment requests it, with double opt-in since this is community-run."
},
{
"choice": "activities",
"flip": "Naming the sponsor's product people to run the feedback session, with consent secured, would move the feedback session from needing product people to live."
}
],
"handoffs": [
"Goal-and-funding strategy: whether the prospectus should keep claiming recruiting and feedback before a planned activity delivers either for the sponsors who bought them.",
"Format selection: where beside the program sponsor lightning talks move once pulled out of their CFP program slot."
],
"checks": [
{
"check": "Value axis",
"pass": true
},
{
"check": "Proposed shape",
"pass": true
},
{
"check": "Honest program",
"pass": true
},
{
"check": "Activity coherence",
"pass": false
},
{
"check": "Outside the CFP",
"pass": false
},
{
"check": "Stop rule",
"pass": true
}
],
"caveats": [
"Sponsor lightning talks is scheduled inside the CFP program as a sold slot; move it beside the program instead.",
"The prospectus claims recruiting and feedback, and no activity currently stages them; one activity per category claimed, or renewals fail.",
"Segments buy what no planned activity delivers: Hiring corner for Scale-ups hiring, and Hands-on product feedback session for Dev tool vendors.",
"A product-feedback session for Dev tool vendors needs the sponsor's product people, not sales, and consent before recording anything."
]
}
The page's check: kept - nothing to flag
The choices CSV for the first sample
menu,option,status,picked
shape,Invite-only dinner or roundtable,deleted,
shape,Executive summit,available,
shape,Single-city user conference,promoted,yes
shape,Multi-city roadshow,available,
shape,Floor-primary event,starved,
program sourcing,Open submissions with the company as named host,starved,
program sourcing,Mixed practitioner program with labelled company slots,default,yes
program sourcing,Customer-story program,available,
program sourcing,Company-supplied program,deleted,
activity,Matchmaking,not yet,yes
activity,Hiring corner,default,yes
Build the input yourself
Load the engine in node (global.window = global; require("./shapekit.js"); require("./shapereply.js")), then:
const K = ShapeKit, R = ShapeReply;
const f = K.form({ name: "Platform Days", pole: "company", goal: "volume", audienceTravels: "yes", compounding: "annual",
sponsored: "yes", segments: fs.readFileSync("segments.csv", "utf8"), activities: fs.readFileSync("activities.csv", "utf8") });
// f.axis, f.recShape, f.candidates, f.shapeRungs, f.recSrc, f.recFloor, f.recActs
// f.checks, f.findings
K.choicesCSV(f) // menu, option, status, picked
const b = K.buildInput(f, "brief"); // or "memo"
const wire = K.toWire(b.input); // POST this to /estimate and /run
// then: const reply = R.normalize(R.parseReply(job.output.output), "brief");
// 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://event-shape.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://event-shape.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"]
// event-shape.mjs -- Node 18+ (global fetch)
const BASE = "https://api.skillsafe.ai/v1/app-api";
let TOKEN = "paste-your-token-here"; // from https://event-shape.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://event-shape.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
}
// EventShape.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 EventShape {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static String token = "paste-your-token-here"; // from https://event-shape.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://event-shape.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://event-shape.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://event-shape.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":"event-shape"}' | 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": "event-shape"})["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: "event-shape" })).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": "event-shape"}, 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", "event-shape"), 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: "event-shape" })["token"])
// a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
$TOKEN = "";
$TOKEN = api("POST", "/guest", ["slug" => "event-shape"])["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 = "event-shape" }))!["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": "brief", "company": "Event: ...; company-run; goal volume of qualified conversations ...", "sponsors": "Sponsor segments, in order ...", "facts": "Value axis: reach (volume) ..."}
# - 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": "brief", "company": "Event: ...; company-run; goal volume of qualified conversations ...", "sponsors": "Sponsor segments, in order ...", "facts": "Value axis: reach (volume) ..."}
# - 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: "brief" or "memo", company, sponsors, 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 event-shape:<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 brief carries summary, axis, candidates, recommendation, deleted, sourcing, floor, activities, flips, handoffs, checks and caveats; a memo carries to, subject, recommendation, why, will_not, ask, measure and note. Neither contains a digit. The code below prints the headline parts of either (list items are plain strings).
KEY="event-shape:brief:$(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 // .recommendation, ((.signals // .caveats)[] | "\(.signal // .caveat): \(.text // .text)")' reply.json
jq -r '.ask // .note // empty' reply.json # the ask of the executive (memo)
import hashlib, time
key = "event-shape:" + 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("recommendation", ""))
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("ask"):
print("ask:", reply["ask"])
import { createHash } from "node:crypto";
const key = `event-shape:${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.recommendation);
for (const s of reply.signals || reply.caveats || []) console.log(s.signal ?? s.caveat, "-", s.text ?? s.text);
if (reply.ask) console.log("ask:", reply.ask);
sum := sha256.Sum256(raw) // add "crypto/sha256", "encoding/hex", "time" to the imports
key := "event-shape:" + 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["recommendation"])
for _, x := range items {
c := x.(map[string]any)
fmt.Println(c["signal"], "-", c["status"])
}
if fix, ok := reply["ask"].(string); ok && fix != "" {
fmt.Println("ask:", fix)
}
var digest = java.security.MessageDigest.getInstance("SHA-256").digest(M.writeValueAsBytes(input));
String key = "event-shape:" + 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("recommendation").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("ask").asText().isEmpty()) System.out.println("ask: " + reply.get("ask").asText());
require "digest"
key = "event-shape:#{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["recommendation"]
(reply["signals"] || reply["caveats"] || []).each { |s| puts "#{s["signal"] || s["caveat"]} - #{s["text"] || s["text"]}" }
puts "ask: #{reply["ask"]}" unless reply["ask"].to_s.empty?
$key = "event-shape:" . $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["recommendation"] ?? "", "\n";
foreach ($reply["signals"] ?? $reply["caveats"] ?? [] as $s) echo $s["signal"] ?? $s["caveat"], " - ", $s["text"] ?? $s["text"], "\n";
if (!empty($reply["ask"])) echo "ask: ", $reply["ask"], "\n";
var digest = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(input.ToJsonString()));
var key = $"event-shape:{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["recommendation"]}");
foreach (var s in (reply["signals"] ?? reply["caveats"]) as JsonArray ?? new JsonArray()) Console.WriteLine($"{s!["signal"] ?? s["caveat"]} - {s["text"] ?? s["text"]}");
if (reply["ask"] is JsonNode fix && fix.GetValue<string>() != "") Console.WriteLine("ask: " + 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: event-shape:brief:$(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": "brief", "company": "Event: ...; company-run; goal volume of qualified conversations ...", "sponsors": "Sponsor segments, in order ...", "facts": "Value axis: reach (volume) ..."}
# - 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": "event-shape:brief:" + 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": `event-shape:brief:${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", "event-shape:brief:"+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", "event-shape:brief:" + 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" => "event-shape:brief:#{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: event-shape:brief:" . 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", $"event-shape:brief:{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);