Run Date Windows from your own code
Both metered lanes are available over HTTP and each returns one JSON object. The division of labour: the page sets the day type from the audience, checks every window for weekdays, clashes and piggyback chances, marks each calendar checked or outstanding and holds the announcement behind the money gate - all free, in datekit.js. The run writes the memo or the outreach; the page then holds the reply to its own decision (datereply.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":"date-windows"}), 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, windows, 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 - decision (the date memo) and outreach (the venue request, and the announcement once the page allows it; needs a surviving window). A missing or unknown task is answered as decision. Both lanes send the same four fields.
The input
| Field | Required | What |
|---|---|---|
task | yes | decision or outreach. If it is missing or neither, do decision and set "task": "decision". |
event | yes | the event, its length, host city and cadence, the audience and how far it travels, the slot, the constraints, and the money, venue and contest answers. |
windows | yes | every candidate window with its status, rank or rejection reason, piggyback chances and same-week events, and every competing event with the page's call. |
facts | yes | the page's day type and source, depth, ranked survivors, calendar statuses, time of day, announcement rung and whether the date can be announced now, the gates, 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
- The first question is whose time attendees spend. Working professionals on employer time: weekdays (the DevOpsDays organizing guide - weekends are for family). Students and hobbyists on personal time: a weekend, Friday evening to Sunday, finishing Sunday afternoon (the MLH hackathon organizer guide). Never average the two: a mixed audience picks the pole it is built for, splits the programme, or - weakest - uses a boundary day, and says which.
- Never invent a holiday or observance date. Lunar and lunisolar observances move every year. Each calendar category is either checked against the body that publishes it, with the source, or outstanding - never "probably fine". A holiday hit is a flag to investigate, not an automatic rejection.
- Search each candidate's exact date, then its whole Monday-to-Sunday week, across adjacent topics, other organizers and large local events. A competing event is not automatically fatal: an event beside you in the same city can be a reason to move toward it (piggybacking, so travellers justify one trip). Decide dodge or attach for every ecosystem anchor.
- Carry three to five candidate windows into the venue conversation ("Get Options") and let availability rank them - date and venue are one joint decision. A window forced from outside deletes the deeper checks; write down what fixes it and who could move it.
- Record the time of day beside the day type and who it excludes: an evening slot reportedly loses caregivers, mostly women, and practitioners with families. A recurring series on one fixed weekday and hour excludes the same people every edition; rotating weekday and hour spreads the loss.
- The one hard gate: no date is announced until there is a working way to handle money. Without it, decide internally and publish nothing. Announcement rungs: venue-locked (the default), money-ready (when the slot is contested or a standing venue makes the signature near-certain), program-teased (a first edition, a judgment call). Once announced, the date holds and scope moves instead. Every gate and signal here is self-set; attach no attendance figure to a date.
House rules
- Use only the input and the rules above. The page's dates, statuses, ranks, calls and picks 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 date, a holiday, an event, a venue, a price 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
decision
summary- one sentence, at most 35 words: the day type, the top window, and the first thing to settle.daytype-pick(weekday,weekend,boundaryorsplit, the page's pick),source(the guide the page names, in a few words),note(one sentence; for a mixed audience, the pole chosen).windows- one object per candidate in the page's order:start(the page's start date),status(survivesorrejected),rank(the page's rank as a number, or null when rejected),reason(one short sentence: the page's rejection reason, or why the window is worth carrying - piggyback chance or same-week caution).conflicts- one object per competing event in the page's order:event(exactly),call(dodge,piggyback,watchorclear, the page's call),why(one short sentence).calendars- one object per category in the page's order:category(the page's name),status(checked,outstandingorhypothesis, as the page states it),next(one short sentence: the source when checked, or what to check and against whom).slot- one sentence: the time of day and who it excludes; mention rotating the weekday and hour only when the page flags a recurring series stuck on one slot - never for a one-off or annual event.announcement-pick(none,venue,moneyorprogram, the page's pick),now(true only when the page says the date can be announced now),note(one sentence: the gate or the trigger still to pass).signals- 2 or 3 short strings, the page's signals, 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.[]when there are none of either.
{
"task": "decision",
"summary": "one sentence",
"daytype": { "pick": "weekday", "source": "a guide", "note": "one sentence" },
"windows": [ { "start": "YYYY-MM-DD", "status": "survives", "rank": 1, "reason": "one short sentence" } ],
"conflicts": [ { "event": "an event", "call": "dodge", "why": "one short sentence" } ],
"calendars": [ { "category": "National public holidays", "status": "outstanding", "next": "one short sentence" } ],
"slot": "one sentence",
"announcement": { "pick": "venue", "now": false, "note": "one sentence" },
"signals": [ "what to record and what it changes" ],
"caveats": [ "a caveat" ]
}
The page holds it to its own decision: the day type, every window in order with its status and rank, every competing-event call, every calendar status, the announcement rung and whether it can be announced now; no date or number may appear that the input does not hold.
outreach
venue_subject- a short email subject line.venue_body- the email to the venue: the event in a sentence, the surviving windows in the page's rank order with their dates exactly, the length in days, and a request for availability across all of them (Get Options). No prices, no headcounts the input does not hold. Placeholders in [brackets] for anything the organizers must fill in.announcement- the public date announcement, a few sentences, naming the date the page says can be announced now; "" when the page says the date cannot be announced now.hold_reason- one sentence: why there is no announcement yet (the money gate, or the venue not signed); "" when there is an announcement.note- one sentence, at most 30 words, for the organizers: what to settle before sending.
{
"task": "outreach",
"venue_subject": "a subject line",
"venue_body": "the email",
"announcement": "",
"hold_reason": "one sentence",
"note": "one sentence"
}
The page requires every surviving window in the venue email in rank order, no rejected window offered, an announcement only when the page allows one and naming its date, and a hold reason that names the money gate when it is unpassed.
Worked examples - every lane, every sample
These are the page's three free samples (fictional events, venues and people; no holiday dates are supplied). 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.
Harborview Platform Days (2 days, professionals) - decision
Weekday; 3 of 6 windows survive, top 2027-04-13; Venue-locked announcement; 5 of 5 gates.
Input:
{
"task": "decision",
"event": "Event: Harborview Platform Days; 2 days; host city Harborview; one edition of an annual event\nAudience: working professionals on employer time; travelling from one country\nSlot: morning; caregivers in the audience unknown\nConstraints: window forced no; must land by no deadline\nAnnouncement: money handling in place yes; venue signed no; standing venue relationship no; contested window no",
"windows": "Candidate windows (2 days each), in the order entered:\nTue 2027-03-16 to Wed 2027-03-17 | REJECTED - clashes with Harborview Cloud Days on the same dates\nTue 2027-03-23 to Wed 2027-03-24 | SURVIVES, rank 2\nSat 2027-03-27 to Sun 2027-03-28 (a Saturday the venue offered) | REJECTED - the wrong day type for an employer-time audience (weekdays)\nTue 2027-04-13 to Wed 2027-04-14 | SURVIVES, rank 1 | piggyback chance: Harborview Python Forum\nTue 2027-05-11 to Wed 2027-05-12 | REJECTED - clashes with Big Ops Summit on the same dates\nTue 2027-05-18 to Wed 2027-05-19 | SURVIVES, rank 3\nCompeting and anchor events:\nHarborview Cloud Days | 2027-03-17 to 2027-03-18 | Harborview | event | call: dodge\nBig Ops Summit | 2027-05-12 to 2027-05-14 | Riverton | event | call: dodge\nHarborview Python Forum | 2027-04-15 to 2027-04-16 | Harborview | event | call: piggyback\nHarborview Marathon | 2027-04-11 | Harborview | civic | call: clear",
"facts": "Day type: weekday - Weekday; source: the DevOpsDays organizing guide - work topics belong to work life; weekends are for family\nChecking depth: competing - Competing-event scan\nSurviving windows, ranked: 1. Tue 2027-04-13 to Wed 2027-04-14; 2. Tue 2027-03-23 to Wed 2027-03-24; 3. Tue 2027-05-18 to Wed 2027-05-19\nCalendars: The audience's own institutional calendar - checked (the two largest employers' fiscal calendars); National public holidays - checked (the national government's public-holiday page for 2027); Religious and cultural observances - outstanding; Ecosystem anchor dates - checked (the foundation's events page); Local civic and cultural events - outstanding; Employer budget and travel-approval cycles - a hypothesis to test\nTime of day: morning\nAnnouncement: venue - Venue-locked announcement; the date can be announced now: no\nGates (5 of 5): Day type matches the audience's time ownership, with its source passed; Every calendar category checked or marked outstanding passed; The competing-event scan covered every candidate's date and week passed; The money gate passed before publication passed; At least three windows for the venue, or the forced window written down passed\nSignals to record: Registrations in the first week after the announcement, against the previous edition (self-set); Attendees citing a conflict in the post-event survey (self-set); No-show rate against the day type chosen (self-set)\nFindings (5):\n[info] piggyback (competing): Harborview Python Forum (2027-04-15 to 2027-04-16, Harborview) sits beside a surviving window in the same city - a chance to piggyback: travellers justify one trip for both.\n[check] outstanding (calendars): Outstanding, not clear: religious and cultural observances; local civic and cultural events. Name the year and the country or institution, and check each against the body that publishes it.\n[info] holidays (calendars): This page never supplies holiday dates. A holiday hit is a flag to investigate, not an automatic rejection - a short break can raise turnout for a single weekend event.\n[info] recurring (announcement): A recurring event pays far more for retracting a published date: once announced the date holds and scope moves instead.\n[info] gate (readiness): Gates before publishing: 5 of 5 (self-set)."
}
Reply:
{
"task": "decision",
"summary": "Weekday event with top-ranked window 2027-04-13 to 2027-04-14; the venue still needs to be signed before anything is announced.",
"daytype": {
"pick": "weekday",
"source": "the DevOpsDays organizing guide",
"note": "Working professionals spend employer time on weekdays, keeping weekends for family, per the guide's reasoning."
},
"windows": [
{
"start": "2027-03-16",
"status": "rejected",
"rank": null,
"reason": "Clashes with Harborview Cloud Days on the same dates."
},
{
"start": "2027-03-23",
"status": "survives",
"rank": 2,
"reason": "No conflicting event or caution found for this week."
},
{
"start": "2027-03-27",
"status": "rejected",
"rank": null,
"reason": "The wrong day type for an employer-time audience, since this is a weekend slot."
},
{
"start": "2027-04-13",
"status": "survives",
"rank": 1,
"reason": "Worth carrying for a piggyback chance with Harborview Python Forum, which sits in the same city right after."
},
{
"start": "2027-05-11",
"status": "rejected",
"rank": null,
"reason": "Clashes with Big Ops Summit on the same dates."
},
{
"start": "2027-05-18",
"status": "survives",
"rank": 3,
"reason": "No conflicting event or caution found for this later week."
}
],
"conflicts": [
{
"event": "Harborview Cloud Days",
"call": "dodge",
"why": "It runs 2027-03-17 to 2027-03-18 in Harborview, overlapping the 2027-03-16 window, which was dropped to dodge the clash."
},
{
"event": "Big Ops Summit",
"call": "dodge",
"why": "It runs 2027-05-12 to 2027-05-14 in Riverton, overlapping the 2027-05-11 window, which was dropped to dodge the clash."
},
{
"event": "Harborview Python Forum",
"call": "piggyback",
"why": "It runs 2027-04-15 to 2027-04-16 in Harborview, right after the top-ranked 2027-04-13 window, letting travellers justify one trip for both."
},
{
"event": "Harborview Marathon",
"call": "clear",
"why": "A civic event on 2027-04-11 in Harborview, with no overlap flagged against any candidate window."
}
],
"calendars": [
{
"category": "The audience's own institutional calendar",
"status": "checked",
"next": "Checked against the two largest employers' fiscal calendars."
},
{
"category": "National public holidays",
"status": "checked",
"next": "Checked against the national government's public-holiday page for 2027."
},
{
"category": "Religious and cultural observances",
"status": "outstanding",
"next": "Name the year and the country, then check each observance against the body that publishes it."
},
{
"category": "Ecosystem anchor dates",
"status": "checked",
"next": "Checked against the foundation's events page."
},
{
"category": "Local civic and cultural events",
"status": "outstanding",
"next": "Still to check against Harborview's own local civic and cultural event listings."
},
{
"category": "Employer budget and travel-approval cycles",
"status": "hypothesis",
"next": "To check directly against the audience's employers' budget and travel-approval cycles."
}
],
"slot": "The event runs in the morning, and whether it excludes caregivers in the audience is not yet known.",
"announcement": {
"pick": "venue",
"now": false,
"note": "The venue is not yet signed, which is the trigger that must happen before the date can be announced."
},
"signals": [
"Registrations in the first week after the announcement, against the previous edition - a weak showing would prompt different promotion timing or channels next edition.",
"Attendees citing a conflict in the post-event survey - repeated conflicts would push next edition's window away from that date.",
"No-show rate against the day type chosen - a high rate would prompt reconsidering the weekday pick next edition."
],
"caveats": [
"Outstanding, not clear: religious and cultural observances and local civic and cultural events - name the year and country or institution, then check each against the body that publishes it."
]
}
The page's check: kept - nothing to flag
Harborview Platform Days (2 days, professionals) - outreach
Weekday; 3 of 6 windows survive, top 2027-04-13; Venue-locked announcement; 5 of 5 gates.
Input:
{
"task": "outreach",
"event": "Event: Harborview Platform Days; 2 days; host city Harborview; one edition of an annual event\nAudience: working professionals on employer time; travelling from one country\nSlot: morning; caregivers in the audience unknown\nConstraints: window forced no; must land by no deadline\nAnnouncement: money handling in place yes; venue signed no; standing venue relationship no; contested window no",
"windows": "Candidate windows (2 days each), in the order entered:\nTue 2027-03-16 to Wed 2027-03-17 | REJECTED - clashes with Harborview Cloud Days on the same dates\nTue 2027-03-23 to Wed 2027-03-24 | SURVIVES, rank 2\nSat 2027-03-27 to Sun 2027-03-28 (a Saturday the venue offered) | REJECTED - the wrong day type for an employer-time audience (weekdays)\nTue 2027-04-13 to Wed 2027-04-14 | SURVIVES, rank 1 | piggyback chance: Harborview Python Forum\nTue 2027-05-11 to Wed 2027-05-12 | REJECTED - clashes with Big Ops Summit on the same dates\nTue 2027-05-18 to Wed 2027-05-19 | SURVIVES, rank 3\nCompeting and anchor events:\nHarborview Cloud Days | 2027-03-17 to 2027-03-18 | Harborview | event | call: dodge\nBig Ops Summit | 2027-05-12 to 2027-05-14 | Riverton | event | call: dodge\nHarborview Python Forum | 2027-04-15 to 2027-04-16 | Harborview | event | call: piggyback\nHarborview Marathon | 2027-04-11 | Harborview | civic | call: clear",
"facts": "Day type: weekday - Weekday; source: the DevOpsDays organizing guide - work topics belong to work life; weekends are for family\nChecking depth: competing - Competing-event scan\nSurviving windows, ranked: 1. Tue 2027-04-13 to Wed 2027-04-14; 2. Tue 2027-03-23 to Wed 2027-03-24; 3. Tue 2027-05-18 to Wed 2027-05-19\nCalendars: The audience's own institutional calendar - checked (the two largest employers' fiscal calendars); National public holidays - checked (the national government's public-holiday page for 2027); Religious and cultural observances - outstanding; Ecosystem anchor dates - checked (the foundation's events page); Local civic and cultural events - outstanding; Employer budget and travel-approval cycles - a hypothesis to test\nTime of day: morning\nAnnouncement: venue - Venue-locked announcement; the date can be announced now: no\nGates (5 of 5): Day type matches the audience's time ownership, with its source passed; Every calendar category checked or marked outstanding passed; The competing-event scan covered every candidate's date and week passed; The money gate passed before publication passed; At least three windows for the venue, or the forced window written down passed\nSignals to record: Registrations in the first week after the announcement, against the previous edition (self-set); Attendees citing a conflict in the post-event survey (self-set); No-show rate against the day type chosen (self-set)\nFindings (5):\n[info] piggyback (competing): Harborview Python Forum (2027-04-15 to 2027-04-16, Harborview) sits beside a surviving window in the same city - a chance to piggyback: travellers justify one trip for both.\n[check] outstanding (calendars): Outstanding, not clear: religious and cultural observances; local civic and cultural events. Name the year and the country or institution, and check each against the body that publishes it.\n[info] holidays (calendars): This page never supplies holiday dates. A holiday hit is a flag to investigate, not an automatic rejection - a short break can raise turnout for a single weekend event.\n[info] recurring (announcement): A recurring event pays far more for retracting a published date: once announced the date holds and scope moves instead.\n[info] gate (readiness): Gates before publishing: 5 of 5 (self-set)."
}
Reply:
{
"task": "outreach",
"venue_subject": "Venue Availability Request - Harborview Platform Days",
"venue_body": "Dear [Venue Contact Name],\n\nHarborview Platform Days is a 2-day event for a working-professional audience, to be held in Harborview. We would like to check your availability across three candidate windows, listed in order of preference:\n\n1. Tue 2027-04-13 to Wed 2027-04-14\n2. Tue 2027-03-23 to Wed 2027-03-24\n3. Tue 2027-05-18 to Wed 2027-05-19\n\nEach window runs 2 days. Please let us know your availability for all three so we can decide together which to confirm, and share any booking details we should know for [Venue Name].\n\nThank you,\n[Organizer Name]\n[Organization Name]",
"announcement": "",
"hold_reason": "The venue is not yet signed, which the venue-locked announcement rung requires before the date can be made public.",
"note": "Before sending, settle who will represent the organizers to the venue and fill in the bracketed placeholders with real names and venue details."
}
The page's check: kept - nothing to flag
Northgate Student Hack (a weekend, students) - decision
Weekend, Friday evening to Sunday; 3 of 6 windows survive, top 2027-02-05; No announcement - the money gate is not passed; 4 of 5 gates.
Input:
{
"task": "decision",
"event": "Event: Northgate Student Hack; 3 days; host city Northgate; one edition of an annual event; first edition\nAudience: students and hobbyists on personal time; travelling from one city\nSlot: evening; caregivers in the audience no\nConstraints: window forced no; must land by 2027-03-15\nAnnouncement: money handling in place no; venue signed no; standing venue relationship no; contested window yes",
"windows": "Candidate windows (3 days each), in the order entered:\nFri 2027-02-05 to Sun 2027-02-07 | SURVIVES, rank 1\nFri 2027-02-12 to Sun 2027-02-14 | SURVIVES, rank 2\nFri 2027-02-19 to Sun 2027-02-21 | REJECTED - clashes with HackRiver on the same dates\nFri 2027-02-26 to Sun 2027-02-28 | SURVIVES, rank 3\nWed 2027-03-03 to Fri 2027-03-05 | REJECTED - the wrong day type for a personal-time audience (Friday evening to Sunday)\nFri 2027-03-12 to Sun 2027-03-14 | REJECTED - clashes with Organizer Summit on the same dates\nCompeting and anchor events:\nHackRiver | 2027-02-19 to 2027-02-21 | Northgate | event | call: dodge\nOrganizer Summit | 2027-03-13 to 2027-03-14 | Elsewhere | anchor | call: dodge",
"facts": "Day type: weekend - Weekend, Friday evening to Sunday; source: the MLH hackathon organizer guide - Friday evening to Sunday, finishing Sunday afternoon\nChecking depth: competing - Competing-event scan\nSurviving windows, ranked: 1. Fri 2027-02-05 to Sun 2027-02-07; 2. Fri 2027-02-12 to Sun 2027-02-14; 3. Fri 2027-02-26 to Sun 2027-02-28\nCalendars: The audience's own institutional calendar - checked (the university's published academic calendar for 2026-27); National public holidays - outstanding; Religious and cultural observances - outstanding; Ecosystem anchor dates - checked (the league's organizer-conference page); Local civic and cultural events - outstanding; Employer budget and travel-approval cycles - a hypothesis to test\nTime of day: evening - reportedly excludes caregivers and practitioners with families\nAnnouncement: none - No announcement - the money gate is not passed; the date can be announced now: no\nGates (4 of 5): Day type matches the audience's time ownership, with its source passed; Every calendar category checked or marked outstanding passed; The competing-event scan covered every candidate's date and week passed; The money gate passed before publication MISSING; At least three windows for the venue, or the forced window written down passed\nSignals to record: Registrations in the first week after the announcement, against the previous edition (self-set); Attendees citing a conflict in the post-event survey (self-set); No-show rate against the day type chosen (self-set)\nFindings (7):\n[info] anchor (competing): Organizer Summit is an ecosystem anchor date - decide deliberately whether to dodge it or attach to it.\n[check] outstanding (calendars): Outstanding, not clear: national public holidays; religious and cultural observances; local civic and cultural events. Name the year and the country or institution, and check each against the body that publishes it.\n[info] holidays (calendars): This page never supplies holiday dates. A holiday hit is a flag to investigate, not an automatic rejection - a short break can raise turnout for a single weekend event.\n[info] evening (time of day): An evening slot reportedly loses caregivers - mostly women - and experienced practitioners with families. Record who it excludes, and route childcare or caregiver tickets to accessibility and inclusion.\n[fix] money_gate (announcement): No working way to handle money yet - the date can be decided internally but not published. The announcement menu is deleted until ticketing, payment processing and an entity that can receive money exist.\n[info] recurring (announcement): A recurring event pays far more for retracting a published date: once announced the date holds and scope moves instead.\n[check] gate (readiness): Gates before publishing: 4 of 5 - missing: The money gate passed before publication (self-set)."
}
Reply:
{
"task": "decision",
"summary": "This is a weekend event, Friday evening to Sunday, the top window is 2027-02-05 to 2027-02-07, and the first thing to settle is a working way to handle money.",
"daytype": {
"pick": "weekend",
"source": "the MLH hackathon organizer guide",
"note": "Students and hobbyists on personal time take a weekend, Friday evening to Sunday, finishing Sunday afternoon."
},
"windows": [
{
"start": "2027-02-05",
"status": "survives",
"rank": 1,
"reason": "No competing or anchor event was flagged against this window, and it carries the top rank."
},
{
"start": "2027-02-12",
"status": "survives",
"rank": 2,
"reason": "No competing or anchor event was flagged against this window, carrying rank 2."
},
{
"start": "2027-02-19",
"status": "rejected",
"rank": null,
"reason": "Clashes with HackRiver, which runs the same dates in Northgate."
},
{
"start": "2027-02-26",
"status": "survives",
"rank": 3,
"reason": "No competing or anchor event was flagged against this window, carrying rank 3."
},
{
"start": "2027-03-03",
"status": "rejected",
"rank": null,
"reason": "The wrong day type for a personal-time audience, which needs Friday evening to Sunday."
},
{
"start": "2027-03-12",
"status": "rejected",
"rank": null,
"reason": "Clashes with Organizer Summit, an ecosystem anchor running the same dates elsewhere."
}
],
"conflicts": [
{
"event": "HackRiver",
"call": "dodge",
"why": "It runs 2027-02-19 to 2027-02-21 in Northgate, the same dates as a rejected candidate window, so the call is to dodge it."
},
{
"event": "Organizer Summit",
"call": "dodge",
"why": "It is an ecosystem anchor running 2027-03-13 to 2027-03-14 elsewhere, the same dates as a rejected candidate window, so the call is to dodge it."
}
],
"calendars": [
{
"category": "The audience's own institutional calendar",
"status": "checked",
"next": "Checked against the university's published academic calendar for 2026-27."
},
{
"category": "National public holidays",
"status": "outstanding",
"next": "Check national public holidays for the relevant year and country against the body that publishes them."
},
{
"category": "Religious and cultural observances",
"status": "outstanding",
"next": "Check religious and cultural observances for the relevant year and country against the body that publishes them."
},
{
"category": "Ecosystem anchor dates",
"status": "checked",
"next": "Checked against the league's organizer-conference page."
},
{
"category": "Local civic and cultural events",
"status": "outstanding",
"next": "Check local civic and cultural events in Northgate against the city or venue calendar."
},
{
"category": "Employer budget and travel-approval cycles",
"status": "hypothesis",
"next": "A hypothesis to test by checking employer budget and travel-approval cycles with the audience's employers."
}
],
"slot": "The evening slot reportedly excludes caregivers, mostly women, and practitioners with families, and as this annual series continues, rotating the weekday and hour is worth considering to spread that loss.",
"announcement": {
"pick": "none",
"now": false,
"note": "No announcement until a working way to handle money - ticketing, payment processing, and an entity that can receive money - exists."
},
"signals": [
"Registrations in the first week after the announcement, against the previous edition - a weak showing would change the window chosen next edition (design).",
"Attendees citing a conflict in the post-event survey - frequent hits would change which calendar category gets checked earlier next edition (design).",
"No-show rate against the day type chosen - a high rate would change the day type or slot considered next edition (design)."
],
"caveats": [
"No working way to handle money yet, so the date can be decided internally but not announced.",
"National public holidays, religious and cultural observances, and local civic and cultural events are outstanding and need checking against the bodies that publish them.",
"Only 4 of 5 gates pass before publishing; the money gate is missing."
]
}
The page's check: kept - nothing to flag
Northgate Student Hack (a weekend, students) - outreach
Weekend, Friday evening to Sunday; 3 of 6 windows survive, top 2027-02-05; No announcement - the money gate is not passed; 4 of 5 gates.
Input:
{
"task": "outreach",
"event": "Event: Northgate Student Hack; 3 days; host city Northgate; one edition of an annual event; first edition\nAudience: students and hobbyists on personal time; travelling from one city\nSlot: evening; caregivers in the audience no\nConstraints: window forced no; must land by 2027-03-15\nAnnouncement: money handling in place no; venue signed no; standing venue relationship no; contested window yes",
"windows": "Candidate windows (3 days each), in the order entered:\nFri 2027-02-05 to Sun 2027-02-07 | SURVIVES, rank 1\nFri 2027-02-12 to Sun 2027-02-14 | SURVIVES, rank 2\nFri 2027-02-19 to Sun 2027-02-21 | REJECTED - clashes with HackRiver on the same dates\nFri 2027-02-26 to Sun 2027-02-28 | SURVIVES, rank 3\nWed 2027-03-03 to Fri 2027-03-05 | REJECTED - the wrong day type for a personal-time audience (Friday evening to Sunday)\nFri 2027-03-12 to Sun 2027-03-14 | REJECTED - clashes with Organizer Summit on the same dates\nCompeting and anchor events:\nHackRiver | 2027-02-19 to 2027-02-21 | Northgate | event | call: dodge\nOrganizer Summit | 2027-03-13 to 2027-03-14 | Elsewhere | anchor | call: dodge",
"facts": "Day type: weekend - Weekend, Friday evening to Sunday; source: the MLH hackathon organizer guide - Friday evening to Sunday, finishing Sunday afternoon\nChecking depth: competing - Competing-event scan\nSurviving windows, ranked: 1. Fri 2027-02-05 to Sun 2027-02-07; 2. Fri 2027-02-12 to Sun 2027-02-14; 3. Fri 2027-02-26 to Sun 2027-02-28\nCalendars: The audience's own institutional calendar - checked (the university's published academic calendar for 2026-27); National public holidays - outstanding; Religious and cultural observances - outstanding; Ecosystem anchor dates - checked (the league's organizer-conference page); Local civic and cultural events - outstanding; Employer budget and travel-approval cycles - a hypothesis to test\nTime of day: evening - reportedly excludes caregivers and practitioners with families\nAnnouncement: none - No announcement - the money gate is not passed; the date can be announced now: no\nGates (4 of 5): Day type matches the audience's time ownership, with its source passed; Every calendar category checked or marked outstanding passed; The competing-event scan covered every candidate's date and week passed; The money gate passed before publication MISSING; At least three windows for the venue, or the forced window written down passed\nSignals to record: Registrations in the first week after the announcement, against the previous edition (self-set); Attendees citing a conflict in the post-event survey (self-set); No-show rate against the day type chosen (self-set)\nFindings (7):\n[info] anchor (competing): Organizer Summit is an ecosystem anchor date - decide deliberately whether to dodge it or attach to it.\n[check] outstanding (calendars): Outstanding, not clear: national public holidays; religious and cultural observances; local civic and cultural events. Name the year and the country or institution, and check each against the body that publishes it.\n[info] holidays (calendars): This page never supplies holiday dates. A holiday hit is a flag to investigate, not an automatic rejection - a short break can raise turnout for a single weekend event.\n[info] evening (time of day): An evening slot reportedly loses caregivers - mostly women - and experienced practitioners with families. Record who it excludes, and route childcare or caregiver tickets to accessibility and inclusion.\n[fix] money_gate (announcement): No working way to handle money yet - the date can be decided internally but not published. The announcement menu is deleted until ticketing, payment processing and an entity that can receive money exist.\n[info] recurring (announcement): A recurring event pays far more for retracting a published date: once announced the date holds and scope moves instead.\n[check] gate (readiness): Gates before publishing: 4 of 5 - missing: The money gate passed before publication (self-set)."
}
Reply:
{
"task": "outreach",
"venue_subject": "Venue availability request - Northgate Student Hack, 3-day event",
"venue_body": "Hello [venue contact name],\n\nWe are organizing Northgate Student Hack, a 3-day hackathon for students and hobbyists hosted in Northgate. We are holding three candidate windows open, in our order of preference, and would like to know your availability across all of them before we finalize a date:\n\n1. Friday 2027-02-05 to Sunday 2027-02-07\n2. Friday 2027-02-12 to Sunday 2027-02-14\n3. Friday 2027-02-26 to Sunday 2027-02-28\n\nCould you confirm which of these three windows, if any, your venue can hold for a 3-day event?\n\nThank you,\n[organizer name]\n[organizer contact details]",
"announcement": "",
"hold_reason": "There is no announcement yet because a working way to handle money - ticketing, payment processing, and an entity that can receive money - is not yet in place.",
"note": "Before sending, settle a way to handle money and fill in the venue contact and organizer details still shown as placeholders."
}
The page's check: kept - nothing to flag
Riverside Python Evenings (a monthly series) - decision
Weekday; 3 of 4 windows survive, top 2027-01-26; Money-ready announcement; 5 of 5 gates.
Input:
{
"task": "decision",
"event": "Event: Riverside Python Evenings; 1 day; host city Riverside; one edition of a recurring series\nAudience: working professionals on employer time; travelling from one city\nSlot: evening; caregivers in the audience yes; recent slots: Tue 18:30, Tue 18:30, Tue 18:30, Tue 18:30, Tue 18:30, Tue 18:30\nConstraints: window forced no; must land by no deadline\nAnnouncement: money handling in place yes; venue signed no; standing venue relationship yes; contested window no",
"windows": "Candidate windows (1 day each), in the order entered:\nTue 2027-01-19 | SURVIVES, rank 2 | same week as: Riverside JS Night\nWed 2027-01-20 | SURVIVES, rank 3 | same week as: Riverside JS Night\nThu 2027-01-21 | REJECTED - clashes with Riverside JS Night on the same dates\nTue 2027-01-26 | SURVIVES, rank 1\nCompeting and anchor events:\nRiverside JS Night | 2027-01-21 | Riverside | event | call: dodge",
"facts": "Day type: weekday - Weekday; source: the DevOpsDays organizing guide - work topics belong to work life; weekends are for family\nChecking depth: competing - Competing-event scan\nSurviving windows, ranked: 1. Tue 2027-01-26; 2. Tue 2027-01-19; 3. Wed 2027-01-20\nCalendars: The audience's own institutional calendar - checked (members' employers - no quarter-end freeze in January); National public holidays - checked (the national public-holiday page for 2027); Religious and cultural observances - checked (the community centre's published observance calendar for 2027); Ecosystem anchor dates - checked (none in this ecosystem); Local civic and cultural events - checked (the city events listing); Employer budget and travel-approval cycles - a hypothesis to test\nTime of day: evening - reportedly excludes caregivers and practitioners with families\nAnnouncement: money - Money-ready announcement; the date can be announced now: yes, 2027-01-26 (top window, before the venue signs)\nGates (5 of 5): Day type matches the audience's time ownership, with its source passed; Every calendar category checked or marked outstanding passed; The competing-event scan covered every candidate's date and week passed; The money gate passed before publication passed; At least three windows for the venue, or the forced window written down passed\nSignals to record: Registrations in the first week after the announcement, against the previous edition (self-set); Attendees citing a conflict in the post-event survey (self-set); Attendance and newcomer share per weekday and hour slot (self-set)\nFindings (9):\n[check] same_week (competing): Tue 2027-01-19 shares its week with Riverside JS Night - it splits travel budgets, days off and speakers even without overlapping.\n[check] same_week (competing): Wed 2027-01-20 shares its week with Riverside JS Night - it splits travel budgets, days off and speakers even without overlapping.\n[info] holidays (calendars): This page never supplies holiday dates. A holiday hit is a flag to investigate, not an automatic rejection - a short break can raise turnout for a single weekend event.\n[check] evening (time of day): An evening slot reportedly loses caregivers - mostly women - and experienced practitioners with families. Record who it excludes, and route childcare or caregiver tickets to accessibility and inclusion.\n[check] caregivers (time of day): Caregivers in the intended audience and an evening slot - a daytime or lunch slot reportedly widens the room.\n[check] fixed_slot (series): The last 6 editions all ran on \"Tue 18:30\" - a fixed slot excludes the same people every time, and the loss never shows in attendance data. Rotate the weekday and the hour, or write down why the slot is fixed.\n[info] money_ready (announcement): A standing venue relationship makes the signature near-certain: a money-ready announcement claims the slot early, at a real retraction risk.\n[info] recurring (announcement): A recurring event pays far more for retracting a published date: once announced the date holds and scope moves instead.\n[info] gate (readiness): Gates before publishing: 5 of 5 (self-set)."
}
Reply:
{
"task": "decision",
"summary": "The page picks a weekday for this working-professional audience, with Tue 2027-01-26 as the top-ranked window; the organizers should settle venue availability across the surviving windows first.",
"daytype": {
"pick": "weekday",
"source": "the DevOpsDays organizing guide",
"note": "Work topics belong to work life, so this employer-time audience keeps the series on a weekday rather than a weekend."
},
"windows": [
{
"start": "2027-01-19",
"status": "survives",
"rank": 2,
"reason": "Shares its week with Riverside JS Night, splitting travel budgets, days off and speakers even without a direct overlap."
},
{
"start": "2027-01-20",
"status": "survives",
"rank": 3,
"reason": "Shares its week with Riverside JS Night, splitting travel budgets, days off and speakers even without a direct overlap."
},
{
"start": "2027-01-21",
"status": "rejected",
"rank": null,
"reason": "Clashes with Riverside JS Night on the same dates."
},
{
"start": "2027-01-26",
"status": "survives",
"rank": 1,
"reason": "Top-ranked window, with no same-week caution tied to Riverside JS Night."
}
],
"conflicts": [
{
"event": "Riverside JS Night",
"call": "dodge",
"why": "It falls on 2027-01-21, the same date rejected for a direct clash, and its week also overlaps the 2027-01-19 and 2027-01-20 survivors."
}
],
"calendars": [
{
"category": "The audience's own institutional calendar",
"status": "checked",
"next": "Checked against members' employers - no quarter-end freeze in January."
},
{
"category": "National public holidays",
"status": "checked",
"next": "Checked against the national public-holiday page for 2027."
},
{
"category": "Religious and cultural observances",
"status": "checked",
"next": "Checked against the community centre's published observance calendar for 2027."
},
{
"category": "Ecosystem anchor dates",
"status": "checked",
"next": "Checked - none found in this ecosystem."
},
{
"category": "Local civic and cultural events",
"status": "checked",
"next": "Checked against the city events listing."
},
{
"category": "Employer budget and travel-approval cycles",
"status": "hypothesis",
"next": "Still to check against members' employers' budget and travel-approval cycles."
}
],
"slot": "The evening slot reportedly excludes caregivers and practitioners with families, and since the last 6 editions all ran on Tue 18:30, the organizers should rotate the weekday and hour to spread that loss across editions.",
"announcement": {
"pick": "money",
"now": true,
"note": "The venue is not yet signed, but the standing venue relationship and money handling in place make the signature near-certain, so 2027-01-26 can be announced now."
},
"signals": [
"Registrations in the first week after the announcement, against the previous edition - a drop would flag the date or rung for review next time.",
"Attendees citing a conflict in the post-event survey - repeated mentions of Riverside JS Night would push the next edition's window further from it.",
"Attendance and newcomer share per weekday and hour slot - a low caregiver or newcomer share would support rotating off Tue 18:30 next edition."
],
"caveats": [
"The last 6 editions all ran on Tue 18:30 - rotate the weekday and hour, or write down why the slot stays fixed.",
"The evening slot reportedly excludes caregivers, mostly women, and practitioners with families.",
"Caregivers are in the intended audience, and a daytime or lunch slot reportedly widens the room.",
"Tue 2027-01-19 and Wed 2027-01-20 both share a week with Riverside JS Night, splitting travel budgets, days off and speakers."
]
}
The page's check: kept - nothing to flag
Riverside Python Evenings (a monthly series) - outreach
Weekday; 3 of 4 windows survive, top 2027-01-26; Money-ready announcement; 5 of 5 gates.
Input:
{
"task": "outreach",
"event": "Event: Riverside Python Evenings; 1 day; host city Riverside; one edition of a recurring series\nAudience: working professionals on employer time; travelling from one city\nSlot: evening; caregivers in the audience yes; recent slots: Tue 18:30, Tue 18:30, Tue 18:30, Tue 18:30, Tue 18:30, Tue 18:30\nConstraints: window forced no; must land by no deadline\nAnnouncement: money handling in place yes; venue signed no; standing venue relationship yes; contested window no",
"windows": "Candidate windows (1 day each), in the order entered:\nTue 2027-01-19 | SURVIVES, rank 2 | same week as: Riverside JS Night\nWed 2027-01-20 | SURVIVES, rank 3 | same week as: Riverside JS Night\nThu 2027-01-21 | REJECTED - clashes with Riverside JS Night on the same dates\nTue 2027-01-26 | SURVIVES, rank 1\nCompeting and anchor events:\nRiverside JS Night | 2027-01-21 | Riverside | event | call: dodge",
"facts": "Day type: weekday - Weekday; source: the DevOpsDays organizing guide - work topics belong to work life; weekends are for family\nChecking depth: competing - Competing-event scan\nSurviving windows, ranked: 1. Tue 2027-01-26; 2. Tue 2027-01-19; 3. Wed 2027-01-20\nCalendars: The audience's own institutional calendar - checked (members' employers - no quarter-end freeze in January); National public holidays - checked (the national public-holiday page for 2027); Religious and cultural observances - checked (the community centre's published observance calendar for 2027); Ecosystem anchor dates - checked (none in this ecosystem); Local civic and cultural events - checked (the city events listing); Employer budget and travel-approval cycles - a hypothesis to test\nTime of day: evening - reportedly excludes caregivers and practitioners with families\nAnnouncement: money - Money-ready announcement; the date can be announced now: yes, 2027-01-26 (top window, before the venue signs)\nGates (5 of 5): Day type matches the audience's time ownership, with its source passed; Every calendar category checked or marked outstanding passed; The competing-event scan covered every candidate's date and week passed; The money gate passed before publication passed; At least three windows for the venue, or the forced window written down passed\nSignals to record: Registrations in the first week after the announcement, against the previous edition (self-set); Attendees citing a conflict in the post-event survey (self-set); Attendance and newcomer share per weekday and hour slot (self-set)\nFindings (9):\n[check] same_week (competing): Tue 2027-01-19 shares its week with Riverside JS Night - it splits travel budgets, days off and speakers even without overlapping.\n[check] same_week (competing): Wed 2027-01-20 shares its week with Riverside JS Night - it splits travel budgets, days off and speakers even without overlapping.\n[info] holidays (calendars): This page never supplies holiday dates. A holiday hit is a flag to investigate, not an automatic rejection - a short break can raise turnout for a single weekend event.\n[check] evening (time of day): An evening slot reportedly loses caregivers - mostly women - and experienced practitioners with families. Record who it excludes, and route childcare or caregiver tickets to accessibility and inclusion.\n[check] caregivers (time of day): Caregivers in the intended audience and an evening slot - a daytime or lunch slot reportedly widens the room.\n[check] fixed_slot (series): The last 6 editions all ran on \"Tue 18:30\" - a fixed slot excludes the same people every time, and the loss never shows in attendance data. Rotate the weekday and the hour, or write down why the slot is fixed.\n[info] money_ready (announcement): A standing venue relationship makes the signature near-certain: a money-ready announcement claims the slot early, at a real retraction risk.\n[info] recurring (announcement): A recurring event pays far more for retracting a published date: once announced the date holds and scope moves instead.\n[info] gate (readiness): Gates before publishing: 5 of 5 (self-set)."
}
Reply:
{
"task": "outreach",
"venue_subject": "Venue availability request - Riverside Python Evenings",
"venue_body": "Hello [Venue Contact Name],\n\nWe are organizing Riverside Python Evenings, a 1 day evening event for working professionals, to be held in Riverside, and would like to check your availability across the following candidate dates rather than committing to one upfront, in order of preference: 1) Tuesday, 2027-01-26; 2) Tuesday, 2027-01-19; 3) Wednesday, 2027-01-20.\n\nCould you confirm which of these dates work for your space, and share any requirements we should plan around, such as room setup, capacity limits or access times?\n\nThank you,\n[Organizer Name]\nRiverside Python Evenings",
"announcement": "Riverside Python Evenings returns on Tuesday, 2027-01-26, in Riverside. This one day evening gathering is for working professionals, with venue and registration details to follow. Save the date - we look forward to seeing you there.",
"hold_reason": "",
"note": "Before sending, settle venue availability across the candidate windows and finalize the venue agreement, since the signature is not yet confirmed."
}
The page's check: kept - nothing to flag
The windows CSV for the first sample
start,end,weekday,status,rank,reasons,piggyback,same_week
2027-03-16,2027-03-17,Tue,rejected,,clashes with Harborview Cloud Days on the same dates,,
2027-03-23,2027-03-24,Tue,survives,2,,,
2027-03-27,2027-03-28,Sat,rejected,,the wrong day type for an employer-time audience (weekdays),,
2027-04-13,2027-04-14,Tue,survives,1,,Harborview Python Forum,
2027-05-11,2027-05-12,Tue,rejected,,clashes with Big Ops Summit on the same dates,,
2027-05-18,2027-05-19,Tue,survives,3,,,
Build the input yourself
Load the engine in node (global.window = global; require("./datekit.js"); require("./datereply.js")), then:
const K = DateKit, R = DateReply;
const f = K.form({ name: "My Conf", pole: "employer", geo: "country", city: "Harborview", days: "2", money: "yes",
candidates: "2027-03-16\n2027-03-23\n2027-04-13", competing: fs.readFileSync("events.csv", "utf8") });
// f.dayType, f.source, f.wins (start, survives, rank, reasons, piggy, sameWeek), f.survivors, f.calls (name, call)
// f.calStatus (category, status, source), f.announce, f.canAnnounce, f.announceDate, f.gate, f.findings
K.windowsCSV(f) // start, end, weekday, status, rank, reasons, ...
const b = K.buildInput(f, "decision"); // or "outreach"
const wire = K.toWire(b.input); // POST this to /estimate and /run
// then: const reply = R.normalize(R.parseReply(job.output.output), "decision");
// 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://date-windows.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://date-windows.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"]
// date-windows.mjs -- Node 18+ (global fetch)
const BASE = "https://api.skillsafe.ai/v1/app-api";
let TOKEN = "paste-your-token-here"; // from https://date-windows.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://date-windows.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
}
// DateWindows.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 DateWindows {
static final String BASE = "https://api.skillsafe.ai/v1/app-api";
static String token = "paste-your-token-here"; // from https://date-windows.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://date-windows.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://date-windows.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://date-windows.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":"date-windows"}' | 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": "date-windows"})["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: "date-windows" })).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": "date-windows"}, 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", "date-windows"), 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: "date-windows" })["token"])
// a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
$TOKEN = "";
$TOKEN = api("POST", "/guest", ["slug" => "date-windows"])["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 = "date-windows" }))!["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": "decision", "event": "Event: ...; 2 days; host city ...", "windows": "Candidate windows (2 days each) ...", "facts": "Day type: weekday - ..."}
# - 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": "decision", "event": "Event: ...; 2 days; host city ...", "windows": "Candidate windows (2 days each) ...", "facts": "Day type: weekday - ..."}
# - 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: "decision" or "outreach", event, windows, 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 date-windows:<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 decision carries summary, daytype, windows, conflicts, calendars, slot, announcement, signals and caveats; an outreach carries venue_subject, venue_body, announcement, hold_reason and note. The code below prints the headline parts of either (list items are plain strings).
KEY="date-windows:decision:$(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 // .slot, ((.signals // .caveats)[] | "\(.signal // .caveat): \(.text // .text)")' reply.json
jq -r '.venue_body // .note // empty' reply.json # the email to the venue (outreach)
import hashlib, time
key = "date-windows:" + 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("slot", ""))
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("venue_body"):
print("venue email:", reply["venue_body"])
import { createHash } from "node:crypto";
const key = `date-windows:${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.slot);
for (const s of reply.signals || reply.caveats || []) console.log(s.signal ?? s.caveat, "-", s.text ?? s.text);
if (reply.venue_body) console.log("venue email:", reply.venue_body);
sum := sha256.Sum256(raw) // add "crypto/sha256", "encoding/hex", "time" to the imports
key := "date-windows:" + 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["slot"])
for _, x := range items {
c := x.(map[string]any)
fmt.Println(c["signal"], "-", c["status"])
}
if fix, ok := reply["venue_body"].(string); ok && fix != "" {
fmt.Println("venue email:", fix)
}
var digest = java.security.MessageDigest.getInstance("SHA-256").digest(M.writeValueAsBytes(input));
String key = "date-windows:" + 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("slot").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("venue_body").asText().isEmpty()) System.out.println("venue email: " + reply.get("venue_body").asText());
require "digest"
key = "date-windows:#{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["slot"]
(reply["signals"] || reply["caveats"] || []).each { |s| puts "#{s["signal"] || s["caveat"]} - #{s["text"] || s["text"]}" }
puts "venue email: #{reply["venue_body"]}" unless reply["venue_body"].to_s.empty?
$key = "date-windows:" . $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["slot"] ?? "", "\n";
foreach ($reply["signals"] ?? $reply["caveats"] ?? [] as $s) echo $s["signal"] ?? $s["caveat"], " - ", $s["text"] ?? $s["text"], "\n";
if (!empty($reply["venue_body"])) echo "venue email: ", $reply["venue_body"], "\n";
var digest = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(input.ToJsonString()));
var key = $"date-windows:{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["slot"]}");
foreach (var s in (reply["signals"] ?? reply["caveats"]) as JsonArray ?? new JsonArray()) Console.WriteLine($"{s!["signal"] ?? s["caveat"]} - {s["text"] ?? s["text"]}");
if (reply["venue_body"] is JsonNode fix && fix.GetValue<string>() != "") Console.WriteLine("venue email: " + 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: date-windows:decision:$(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": "decision", "event": "Event: ...; 2 days; host city ...", "windows": "Candidate windows (2 days each) ...", "facts": "Day type: weekday - ..."}
# - 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": "date-windows:decision:" + 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": `date-windows:decision:${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", "date-windows:decision:"+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", "date-windows:decision:" + 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" => "date-windows:decision:#{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: date-windows:decision:" . 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", $"date-windows:decision:{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);