FORMATION engine

Find what holds. Then build with it.

Turn a plain-language brief into a study for your group.

Generate a study from a brief

Funded account or a bound service bearer8 studies / hour per accounttypically 45–150s (design ~30–60s, a lane audit on the practical lane, up to 14 resource serves); hard deadline 270s; TRUTH reviews finish minutes later

Returns the study with each lesson pending while its claims are checked. Poll /study for the lessons as the checks finish.

  • · Gated on the server for every caller, with no switch: on the faith lane, a teaching part is sent only once every claim it rests on (teachingParts[].claims) has been checked, and only if none of them came back CONTRADICTED. Each claim is read through TRUTH's consumer result (truth-consumer-result/v1: SUPPORTED | CONTRADICTED | UNDETERMINED), never its internal enums. UNDETERMINED is TRUTH abstaining, not rejecting: the part is kept, and lesson.leaderNotes says why TRUTH could not stand behind the claim — a genuine dispute as a question careful readers disagree on. A review that failed to run is kept apart from an abstention (lesson review_failed) and is not held either. A part on a pending, unchecked or CONTRADICTED claim is never sent, in any field; lesson.leaderNotes says why. The practical lane's claims are not checked by TRUTH, so its teaching is not held for them.
  • · Poll GET /api/public/formation/study?studyId=… until review.pending is false.
  • · A study carries NO crisis off-ramp and NO Gospel gateway, on either lane. A study that includes one is refused (leader_material_violation); a practical-lane study with religious language is refused (lane_violation).
  • · Claim checks are paid TRUTH reviews on the calling account. A refused claim comes back skipped or failed with its reason, never as a silently unchecked claim.
  • · POST /api/public/formation/gospel-response answers 410 formation_gospel_gateway_retired.
  • · Refusals: session_count_mismatch, session_content_missing. A slot or claim cut off by the deadline comes back empty or skipped, with a reason.

Request body

FieldTypeMeaning
briefrequiredstring (10–2000)What you want to form in your people, in your own words. Topic, occasion, tone, anything.
audience{ ageRange?, ages?, context?, faithFamiliarity? }ageRange: 7-13 · 14-18 · 19-28 · adults · mixed. ages: the exact ages, when you know them — "15" or "13-15". context: e.g. "youth group, mostly unchurched, gamers". faithFamiliarity: new · exploring · familiar · mixed.
voicestring (≤200)How it should sound, e.g. "like a level-headed older friend", "plain, no church words".
sessions1–7How many sessions/days. Default 3.
lanefaith | practicalfaith: scripture, teaching, prayer and an optional depth module. practical: useful to skeptics — no Scripture, prayer or Gospel language, spiritually light lifelines only. Default faith.
trackanger | despair | fear_doubt | harmful_habit | sexual_habitThe struggle this study is closest to; steers LIFELINE. Inferred from the brief when omitted.
goDeeperbooleanEnqueue TRUTH self-reviews for the claims (default true). Faith lane only — the practical lane's claims are not sent to TRUTH, and its teaching is sent as written. Off on the faith lane, the claims stay unchecked, so no teaching that rests on one is ever sent (an unchecked claim was never put to TRUTH, so there is no result to keep it on).
enrichrerank | fastFaith lane only. rerank: LIFELINE's LLM reranker judges each slot. fast: deterministic serve. The practical lane is always served deterministically (inline placement), which is what keeps it narrow. Default rerank.
maxTruthReviews0–21Cap on TRUTH jobs (default: every claim — a session rests on at most 3). A claim past the cap is not reviewed, so the teaching that rests on it is never sent (it is unchecked, not undetermined).

Response

FieldTypeMeaning
title / summary / track / lane / audienceNotestringThe study's frame as designed.
sessions[]Session[]id, title, objective, lesson { status pending | settled, claimsStand, withheldParts, leaderNotes[{claim, note}] }, scripture[{reference, why — null unless every claim of the session stands}], teachingParts[{text, claims[] — indexes into claims[]: what the part rests on}] — on the faith lane, ONLY the parts every claim of which was reviewed and stands: TRUTH's result for it was SUPPORTED, or UNDETERMINED (TRUTH abstained — the part is kept, with a leader note of why), or its review failed to run (kept, with a note); never one resting on a CONTRADICTED or unchecked claim, and none while the session is pending; on the practical lane, whose claims TRUTH does not review, every part, teaching (those parts joined), reflection[], practice{label,text}, journalPrompt (an optional take-home or in-group writing prompt for the leader to use, generated with the session on both lanes; an activity, never teaching or evidence, and never sent to TRUTH — the study audit's journal_claim rule refuses a prompt that states a claim or argues a point: one retry, then claim_binding_violation), prayer, depthModule{scripture, explanation, reflectionQuestion, action, prayer} | null (null unless every claim of the session stands), lifelineNeeds, claims[], lifelines[] (placed slots: slot, surface inline|chip, category, need, lead, alternates[], rankedBy, why, telemetryToken — present with a lead; verifies the lead's shown / open / play events when this site renders the study; a verse resource carries `verse`, the card Shepherd renders, as on LIFELINE /match), goDeeper[] (claim, jobId, insightsToken, status, lesson { status earned | undetermined | review_failed | correction | withheld, reason — for undetermined, why TRUTH abstained: legacy_contract | genuine_dispute | not_assessable | research_failure | missing_corpus | ungrounded_proposition | unresolved_objection | insufficient_evidence — leaderNote }, null until the review is back — and insight, the claim's go-deeper material, sent only for a claim TRUTH supported or refuted) — the token binds the job to YOUR caller; the insights read requires it.
review{ pending, sessionsPending, statusEndpoint }Whether any session's lesson still waits on a TRUTH review. Poll statusEndpoint (GET /api/public/formation/study?studyId=…) until pending is false.
gospelGatewaynullAlways null, on both lanes.
placementHealthThe strategy applied and its health: slots planned/filled, unique resources, repeats (0), worship in the practical lane (0), practical-ceiling refusals (candidates the practical lane refused for sitting above the reviewed song depth ceiling), reused videos (0), inline per session, rules, and offRampCheck { checked, withheld, error } — the embedded resources a model pass read for crisis off-ramps, how many it withheld, or why it could not run (then every embed is withheld).
truth{ enabled, reviewsEnqueued, insightsEndpoint, note }How to fetch each claim's verdict + go-deeper moments. A goDeeper entry that is skipped or failed was NOT checked, and its reason says why; when no claim could be sent, the note says the whole study is unverified.
provenance{ generatedAt, designModel, designUsage, enrich }What produced this study.
dedupe"fresh" | "in_flight" | "cached" | "undeduped"An identical brief from your account shares one generation, so a retry is not charged twice. fresh ran it, in_flight joined a running one, cached returned a recent study, undeduped ran on its own. Never shared across accounts.

Try it

Sign in with a funded account to send.

POST /api/public/formation/generate
Response
// the response will appear here

Call it from your code

server-to-server (service bearer)
curl -sS -X POST 'https://<your-shepherd-host>/api/public/formation/generate' \
  -H 'Authorization: Bearer $SHEPHERD_API_KEY' \
  -H 'content-type: application/json' \
  --data-binary @- <<'JSON'
{
  "brief": "A 3-day identity-in-Christ study for our middle school group. Most of them are gamers, a few are new to church. Keep it real, not preachy.",
  "audience": {
    "ageRange": "14-18",
    "context": "middle school youth group, gamers, half unchurched",
    "faithFamiliarity": "mixed"
  },
  "voice": "like a level-headed older friend",
  "sessions": 3
}
JSON

Never ship the service key to a browser.

Versions

Every response from every Shepherd engine — a result, an estimate, a refusal or an error — says which versions produced it, under the same three headers on every engine. A JSON object body carries the same three as `versions: { api, engine, engines }`.

Response headers

FieldTypeMeaning
X-API-Versionrequiredv<N>The API version: the request/response contract, the same for every engine ("v1"). It changes only for a breaking change to that contract.
X-Engine-Versionrequired<engine>-engine/<line>-v<N>The responding engine's version ("truth-engine/research-v32", "verse-engine/card-v1"). The number goes up whenever the engine's behaviour changes: its prompts, its models, its retrieval or its rules.
X-Engine-VersionsrequiredJSON objectThe versions of the engines this response's own work called, by engine name ({"lifeline": "lifeline-engine/match-v1", "truth": "truth-engine/research-v32"} from a FORMATION study; {} when it called none — a refusal before any work, or a result served from a recent identical run).
  • · Read as a label, a version is the engine's name, its number and the API's: "VERSE v3 · API v1". The example results on every engine page are labelled this way, with the versions each was captured under.
  • · A result stored for later (a TRUTH read, a FORMATION study's reviews) keeps the version that produced it; the headers name the versions answering now.
  • · TRUTH's assessment endpoints return the frozen assessment contract, which carries its own provenance.truth_engine_version, so there the versions are in the headers only. The open contract endpoint runs the deterministic stub and answers as truth-engine/stub-v0.