Fitinty API reference
Generated from the API the server is running right now, filtered to the doors a key may open. It is never written by hand.
Keys and subscriptions are minted from a business's Setup desk: Connected apps > API & webhooks.
3,280 doors across 21 families a key may open, and 200 public doors that need no key.
How a key is presented
A key is minted from a business's Setup desk (Connected apps > API & webhooks) by its admin, shown once, stored as a hash, and revoked by a row. It acts as a member of exactly that business: it reads and writes what a member of that business could, in the families its scopes name, and nothing of any other.
Authorization: Bearer fk_<key>
education:read · education:write · marketplace:read · marketplace:write · publishers:read · publishers:write · workspace:read · workspace:write · services:read · services:write · ministries:read · ministries:write · media:read · media:write · cinema:read · cinema:write · games:read · games:write · marketing:read · marketing:write · tools:read · tools:write · offices:read · offices:write · bank:read · bank:write · treasury:read · treasury:write · tax:read · tax:write · compliance:read · compliance:write · investors:read · investors:write · farms:read · farms:write · manufacturing:read · manufacturing:write · logistics:read · logistics:write · governor-suite:read · governor-suite:write
Webhooks
A subscription names an https URL and families of the audit trail. Each event of those families is delivered once, signed; a receiver that does not answer 2xx is retried on the backoff and the delivery is marked dead after the last try, with the reason. The payload is what the trail records about the event - never the row it is about; read that through the API with a key.
Signature
X-Fitinty-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 over '<t>.' + the exact body bytes, under the subscription's secret> X-Fitinty-Event: <action> X-Fitinty-Delivery: <delivery id> tolerance: 300s
Retries (seconds between attempts)
60, 300, 1800, 7200, 21600 - 6 attempts
Families a subscription may name
assignment, automation, bank, cinema, compliance, course, deck_factory, education, enrollment, farms, games, governor, identity_pack, imperial_draw, investors, lesson, logistics, manufacturing, marketing, marketplace, media, ministries, multimedia_factory, office, offices, payment, presentation_factory, provider, publishers, school, song_factory, tax, theme, tools, treasury, video_factory, voice, workspace
id, occurred_at, family, action, resource_type, resource_id, organization_id, actor_id, decision, delivery_id, attempt
Families a key may open
Education - २९१ doors - education:read, education:write
Schools, programmes and courses - the framework every tenant's school is built from.
DELETE/education/announcements/{announcement_id}· scope education:writeDelete AnnouncementPOST/education/announcements/{announcement_id}/publish· scope education:writePublish AnnouncementTHE OVERRIDE DOOR. Whatever drafted it - a hand or the digest - a person publishes. RLS admits only school staff to the UPDATE.POST/education/assignments/{assignment_id}/submissions· scope education:writeSubmit AssignmentGET/education/assignments/{assignment_id}/submissions· scope education:readList Assignment SubmissionsPOST/education/assignments/{assignment_id}/writing-feedback· scope education:writeWriting FeedbackPOST/education/blocks· scope education:writeCreate BlockPATCH/education/blocks/{block_id}· scope education:writeUpdate BlockDELETE/education/blocks/{block_id}· scope education:writeDelete BlockGET/education/bundles/for-course/{course_id}· scope education:readBundles For CourseThe student's door: published bundles that carry this course as an item.GET/education/bundles/mine· scope education:readMy BundlesThe buyer's shelf: every bundle they bought, with each item's delivery status IN WORDS - a pending or failed delivery is shown, never hidden.POST/education/bundles/purchases/{purchase_id}/retry-delivery· scope education:writeRetry DeliveryThe retry door: the buyer or the selling school's staff re-run whatever is not yet delivered. Delivered rows are never re-attempted.POST/education/bundles/schools/{school_id}· scope education:writeCreate BundleThe seller's hand. Items are validated against REAL rows at creation - a key with no reader is a silent zero, so an unknown reference is refused (422), never stored. Born a draft; publishing is the go-live moment.GET/education/bundles/schools/{school_id}· scope education:readList School BundlesRLS splits honestly: staff see drafts, everyone else the published shop window.POST/education/bundles/{bundle_id}/checkout· scope education:writeStart Bundle CheckoutOne Checkout Session on the SELLER's account for the whole box. When any item is physical, Stripe collects the shipping address and the webhook carries it into the Medusa order - the buyer types their address exactly once.POST/education/bundles/{bundle_id}/publish· scope education:writePublish BundleGET/education/certificates/mine· scope education:readList My CertificatesPOST/education/certificates/{certificate_id}/revoke· scope education:writeRevoke CertificateGET/education/certificates/{certificate_id}/verify· scope education:readVerify CertificateDELETE/education/channel-messages/{message_id}· scope education:writeRemove Channel MessageModeration (addition 18's seed): RLS admits the DELETE only for the author, school staff, or the owner - zero rows deleted is an honest 404.GET/education/commerce/applications/mine· scope education:readMy ApplicationsPOST/education/commerce/applications/{application_id}/decide· scope education:writeDecide ApplicationA decline carries its reason - the reason is the applicant's teacher.POST/education/commerce/courses/{course_id}/book· scope education:writeSet Course BookThe course's book dial. The book must be a PUBLISHED product somewhere on the platform - any org's: a partner bookstore counts.POST/education/commerce/courses/{course_id}/checkout· scope education:writeStart Course CheckoutA Checkout Session ON THE SCHOOL'S ACCOUNT. No card detail touches this platform - Stripe hosts the page; the student pays the school; Fitinty's fee is taken by Stripe's own split.POST/education/commerce/courses/{course_id}/license-seats· scope education:writeLicense SeatsTHE BUYER'S DOOR. Another organization's OWNER buys seats on the seller's own Stripe account - a Checkout Session, the course-purchase construction exactly. No row is granted here: the verified webhook births the seats, so an abandoned checkout leaves no free students behind.POST/education/commerce/courses/{course_id}/seat-offer· scope education:writeSet Seat OfferThe seller's shelf tag. Only on a PUBLISHED course - the pay-to-open gate was passed when the course went public, and seats on a draft would sell an empty room.POST/education/commerce/courses/{course_id}/sponsor-checkout· scope education:writeStart Sponsor CheckoutTHE GIVER'S DOOR. Any signed-in person pays the course's own price on the school's own rail. The claim code is minted NOW and shown on the sponsor's shelf the moment payment lands - handing it over is the gift.POST/education/commerce/courses/{course_id}/template-license· scope education:writeBuy Template LicenseTHE BUYER'S DOOR. A priced template is the seat-purchase construction exactly: pending row, Checkout Session on the seller's account, the webhook births the license. A FREE template births its license paid on the spot - there is no provider payment to wait for.POST/education/commerce/courses/{course_id}/template-offer· scope education:writeSet Template OfferThe seller's dial. Published courses only - a template nobody can inspect as a live course would sell a promise with no shop window. Going ON SALE is a go-live moment, so it rides the ONE pay-to-open gate like every other.GET/education/commerce/courses/{course_id}/template-offer· scope education:readGet Template OfferThe seller's own dial, read back - staff only, drafts and inactive included.POST/education/commerce/enrollments/{enrollment_id}/certificate-checkout· scope education:writeStart Certificate CheckoutThe STUDENT's door - their own enrollment, their own money, the school's own rail.POST/education/commerce/programs/{program_id}/checkout· scope education:writeStart Program CheckoutBuy the whole series at the tenant's one price. Published programs only - a bundle of drafts would sell an empty shelf.POST/education/commerce/schools/{school_id}/application-fee· scope education:writeSet Application FeeThe school's dial. None = free applications (the default, and the honest zero).GET/education/commerce/schools/{school_id}/applications· scope education:readSchool ApplicationsPOST/education/commerce/schools/{school_id}/apply· scope education:writeApply To SchoolThe applicant's door. Free -> SUBMITTED now; priced -> PENDING_PAYMENT with the school's own checkout - the verified webhook turns it submitted.POST/education/commerce/schools/{school_id}/growth-advice· scope education:writeGrowth AdviceProposals, never actions (Override Charter): grounded strictly in the school's own real numbers, benchmarked in spirit against the plan's competitive rider - and everything it suggests is applied by the TENANT's own hand through the existing screens or not at all.GET/education/commerce/schools/{school_id}/money-in· scope education:readSchool Money InThe school's money-in: PAID purchases only (the money-in rule: captured, never intended). Absence of payments is said in words by the screen, never rendered as zero-pretending-to-know.GET/education/commerce/schools/{school_id}/seat-grants· scope education:readList Seat GrantsPOST/education/commerce/schools/{school_id}/seat-grants· scope education:writeCreate Seat GrantGET/education/commerce/seat-purchases· scope education:readList Seat PurchasesBoth sides of the counter: what this org has sold and what it has bought.POST/education/commerce/sponsored/claim· scope education:writeClaim SponsorshipThe student's hand on the gift. One claim, forever; a code on an unpaid or already-claimed gift refuses in words. The claim opens the PRICE gate only - the student still walks enrollment's other gates on the course page.GET/education/commerce/sponsored/mine· scope education:readMy SponsorshipsThe sponsor's shelf: every gift they bought, its state, and the claim code to hand over once paid.GET/education/commerce/template-licenses/mine· scope education:readMy Template LicensesThe buyer's shelf: licenses their organization holds, adopted or waiting.POST/education/commerce/template-licenses/{license_id}/adopt· scope education:writeAdopt TemplateDELIVERY. The paid license turns into a DRAFT course in the buyer's school - the whole spine cloned, owned outright, free to reshape (building is free by ruling; the buyer's own publish walks the standing publish gates later). What is NOT copied is stated in words, never silently absent.GET/education/commerce/template-market· scope education:readTemplate MarketEvery active offer on a published course, platform-wide. Read through the PUBLIC cursor: these are shop-window facts, and a member cursor answers a silent zero on another org's rows - the standing RLS lesson. Free is stated as free, never 0.POST/education/commerce/webhook· scope education:writeEducation Stripe WebhookPATCH/education/competencies/{competency_id}· scope education:writeUpdate CompetencyCorrect a competency's wording. A competency statement is the hardest sentence in a course to get right - it is the promise the whole module is measured against - so it is exactly the sentence a teacher rewrites three times. Shipping create-only was the same capability-without-a-door fault found five times on the night this pillar was walked (Tenant Zero, 2026-08-26). THE LINKS SURVIVE THE REWOR…DELETE/education/competencies/{competency_id}· scope education:writeDelete CompetencyRemove a competency - unless a student has already been credited with it. A demonstration is a statement about a person's ability, granted by a named member of staff with evidence attached. Deleting the competency underneath it would erase that record sideways, which is the thing this platform refuses everywhere: a teacher's tidy-up never rewrites a student's history.POST/education/competencies/{competency_id}/assessed· scope education:writeMark AssessedClaim that an assessment TESTS a competency. This is the claim the coverage report checks.POST/education/competencies/{competency_id}/demonstrated· scope education:writeRecord DemonstrationTHE ALTERNATIVE PATH: this student can already do this, and here is how we know. In job training this is the ordinary case rather than the exception - a technician who has wired houses for ten years has mastered "identify an electrical hazard", and a school that cannot record that wastes their time and loses them. Recognition of prior learning, kept as a record with a name and evidence on it, nev…POST/education/competencies/{competency_id}/taught· scope education:writeMark TaughtDeclare WHERE a competency is taught. Exactly one anchor - the table's own CHECK says the same thing, so a caller that bypasses this endpoint cannot write a nonsense row either.GET/education/courses/published· scope education:readList Published CoursesGET/education/courses/{course_id}· scope education:readGet CoursePATCH/education/courses/{course_id}· scope education:writeUpdate CourseGET/education/courses/{course_id}/assignments· scope education:readList AssignmentsPOST/education/courses/{course_id}/assignments· scope education:writeCreate AssignmentGET/education/courses/{course_id}/attendance· scope education:readList AttendancePOST/education/courses/{course_id}/attendance· scope education:writeRecord AttendanceGET/education/courses/{course_id}/certificates· scope education:readList Course CertificatesGET/education/courses/{course_id}/channel· scope education:readGet ChannelPOST/education/courses/{course_id}/channel· scope education:writePost To ChannelGET/education/courses/{course_id}/competencies· scope education:readList CompetenciesWhat a learner will be able to DO. Visible to students too, deliberately: telling somebody the target is the point of stating it.POST/education/courses/{course_id}/competencies· scope education:writeCreate CompetencyGET/education/courses/{course_id}/coverage· scope education:readCourse CoverageDoes the course's final lab, project and exam test everything it teaches?POST/education/courses/{course_id}/enroll· scope education:writeEnrollGET/education/courses/{course_id}/enrollments· scope education:readList Course RosterGET/education/courses/{course_id}/grades· scope education:readAll GradesGET/education/courses/{course_id}/grades/mine· scope education:readMy GradeGET/education/courses/{course_id}/grading-weights· scope education:readGet Grading WeightsPUT/education/courses/{course_id}/grading-weights· scope education:writeSet Grading WeightsGET/education/courses/{course_id}/labs· scope education:readCourse LabsOne call for the whole course player: which lessons carry a lab, and which of them THIS caller has genuinely completed. Rows exist only where a premise does - a lesson with no lab is absent, not false-flagged.GET/education/courses/{course_id}/live-classes· scope education:readList Live ClassesPOST/education/courses/{course_id}/live-classes· scope education:writeStart Live ClassStaff only. A REAL P01 hosted meeting through create_meeting() itself - its capacity checks, its room tokens, its /meet room - linked to the course.GET/education/courses/{course_id}/modality-coverage· scope education:readCourse Modality CoverageGET/education/courses/{course_id}/module-progress· scope education:readGet Module ProgressGET/education/courses/{course_id}/modules· scope education:readList ModulesPOST/education/courses/{course_id}/modules· scope education:writeCreate ModuleGET/education/courses/{course_id}/prerequisites· scope education:readList PrerequisitesPOST/education/courses/{course_id}/prerequisites· scope education:writeAdd PrerequisiteDELETE/education/courses/{course_id}/prerequisites/{requires_course_id}· scope education:writeRemove PrerequisitePOST/education/courses/{course_id}/publish-to-federation· scope education:writePublish To FederationGET/education/courses/{course_id}/quizzes· scope education:readList QuizzesPOST/education/courses/{course_id}/quizzes· scope education:writeCreate QuizGET/education/courses/{course_id}/review· scope education:readReview CourseThe whole course. Kept as its own route because it is what every screen already calls.GET/education/courses/{course_id}/spine· scope education:readCourse SpineThe student's own gated view of a course. THE EMPEROR'S SEQUENCE, read from records rather than asserted: "student work on each section at a time until complete the lesson; each lesson at a time until they complete the Module; and each module at a time until complete the course." THIS ENDPOINT DOES NOT INVENT A POLICY. Sequencing follows the course's own `require_sequential_modules` flag — the s…GET/education/courses/{course_id}/students/{student_user_id}/report· scope education:readStudent ReportPOST/education/courses/{course_id}/study-groups· scope education:writeCreate Study GroupAnyone inside the walls starts a room; staff-started rooms are the school's official ones. The creator is its first member.GET/education/courses/{course_id}/study-groups· scope education:readList Study GroupsGET/education/courses/{course_id}/translation-siblings· scope education:readList Translation SiblingsEvery real course in this one's translation family - lets a student/teacher navigate to any language version from any other one. Returns an empty list for a course with no translations at all - never a 404, since 'no family' is a real, valid state, not an error.POST/education/courses/{course_id}/translations· scope education:writeCreate TranslationGET/education/courses/{course_id}/translations· scope education:readList TranslationsGET/education/enrollments/mine· scope education:readList My EnrollmentsPATCH/education/enrollments/{enrollment_id}· scope education:writeUpdate Enrollment StatusPOST/education/enrollments/{enrollment_id}/certificate· scope education:writeIssue CertificatePATCH/education/enrollments/{enrollment_id}/term· scope education:writeSet Enrollment TermAttach an enrolment to the term it belongs to - the write migration 397 never got. FOUND BY THE FIRST FULL DRY-RUN (2026-08-25): the term attaches to the ENROLMENT by design, every reader filters on `enrollment.term_id` - and nothing in the product ever wrote it. Every fixture that had a term got it from wave-time SQL, so on a fresh school the whole Registrar was unreachable: every student "unter…PUT/education/factory/categories/{school_type}· scope education:writeSave CategoryAdd a category, or rewrite one. Owner only, and the table says so too. The slug is taken from the URL, not the body: renaming a category's slug would cascade to every school built on it, and that is a different and much larger decision than editing its wording.POST/education/factory/categories/{school_type}/restore· scope education:writeRestore CategoryPOST/education/factory/categories/{school_type}/retire· scope education:writeRetire CategoryWithdraw a category without deleting it. RETIRED, NEVER DELETED, and the database enforces the difference: `school.school_type` is a foreign key with ON DELETE RESTRICT, so a category any school was built on cannot be removed at all. Retiring stops anyone starting a new school of that kind while every existing one keeps a real parent and keeps working.POST/education/factory/lessons/{lesson_id}/multimedia· scope education:writeBridge Lesson To FactoryPOST/education/factory/proposals· scope education:writeCreate ProposalGET/education/factory/proposals· scope education:readList My ProposalsPATCH/education/factory/proposals/{proposal_id}· scope education:writeEdit ProposalThe human-edit door - the Override Charter's first working implementation. The tenant sends the draft as THEY want it; it is validated for shape only, never second- guessed, and the edit marks the proposal human_touched forever.POST/education/factory/proposals/{proposal_id}/apply· scope education:writeApply ProposalPOST/education/factory/proposals/{proposal_id}/discard· scope education:writeDiscard ProposalPOST/education/factory/proposals/{proposal_id}/revise· scope education:writeRevise ProposalThe in-the-loop step: re-draft WITH the current draft and the tenant's instruction.GET/education/factory/starter-packs· scope education:readList Starter PacksRLS does the gating: tenants receive published packs only; the owner also sees drafts awaiting review and retired incumbents.POST/education/factory/starter-packs/{pack_id}/adopt· scope education:writeAdopt Starter PackThe pack becomes the TENANT'S proposal. RLS on the SELECT is the gate: a tenant can only ever read - and therefore adopt - a published pack.POST/education/factory/starter-packs/{pack_id}/publish· scope education:writePublish Starter PackPOST/education/factory/starter-packs/{school_type}/generate· scope education:writeGenerate Starter PackGET/education/factory/templates· scope education:readList Category TemplatesEvery category the Factory can build, with the template each drafts from. Auth-gated but not role-gated: the cockpit shows these to any signed-in tenant choosing what kind of school to start, and nothing in a template is secret - it is the platform's own pedagogy, stated.GET/education/federation/catalog· scope education:readFederation CatalogPublished masters discoverable across tenants (the White-Label catalog).POST/education/federation/{federation_id}/adopt· scope education:writeAdopt FederationGET/education/federation/{federation_id}/events· scope education:readFederation EventsPOST/education/federation/{federation_id}/global-push· scope education:writeGlobal PushGET/education/federation/{federation_id}/subscribers· scope education:readFederation SubscribersPOST/education/guardians/consents· scope education:writeGrant ConsentPOST/education/guardians/consents/{consent_id}/revoke· scope education:writeRevoke ConsentPOST/education/guardians/links· scope education:writeLink GuardianPOST/education/guardians/minor-flags· scope education:writeMark MinorGET/education/guardians/my-students· scope education:readMy StudentsThe guardian portal seed: progress, not just consent. Every query below runs under the guardian's own RLS context - the *_guardian_read policies (387) are what admit each row.GET/education/identity/assets/{asset_id}/file· scope education:readAsset FileServe one identity file to an eye RLS already trusts - the SELECT below runs under the same policies as everything else, so 'not yours to see' and 'does not exist' are the same 404. Used by the cast card (N6): a face is shown only to sessions the ledger's own policy would show the row to.POST/education/identity/avatar/build· scope education:writeBuild AvatarQueue the Foundry (Wave N, N3): the consented photos become a rigged, walking, game-fidelity character. Runs in the background - a body takes a minute or two to build, and the ledger IS the status: the avatar_rig row appears when it is done.GET/education/identity/avatar/status· scope education:readAvatar StatusPOST/education/identity/consents· scope education:writeGrant ConsentAn ADULT's own-hand consent. A minor is refused here IN WORDS - and would be refused by the INSERT policy even if this check were deleted, because a child self-consenting to biometric capture must be impossible, not merely discouraged.POST/education/identity/consents/{scope}/revoke· scope education:writeRevoke ConsentRevocation with a receipt: the consent closes AND every asset in the scope dies with its file, in the same transaction the revocation commits in. The response says how many - a receipt, not a shrug. (A minor's revocation travels through the guardians module, which calls the same `_withdraw_assets` - one implementation, both doors.)GET/education/identity/mine· scope education:readMy IdentityPOST/education/identity/photos· scope education:writeUpload PhotoOne whole-body photo (the agent asks for several, from all angles - each arrives here). Requires the 'likeness' consent to already stand, whichever door granted it.POST/education/identity/voice-sample· scope education:writeUpload Voice SampleThe voice sample - the single most personal thing this platform stores (voice_promise's own words), so it lands in the same ledger the withdrawal walk destroys from.GET/education/integrations· scope education:readList IntegrationsHonest status for the LMS adapters this pillar could integrate with - all genuinely "not configured" right now. The spec (§8) explicitly warns against rushing an LMS integration ("Fitinty shall not attempt to replace all LMS engines immediately") and lays out a real pre-install checklist (license review, security review, SSO design, tenant isolation review, backup/restore, accessibility review) be…POST/education/integrations/import· scope education:writeImport OutlinePOST/education/labs/transcribe· scope education:writeTranscribe SpeechSpeech into the scene (Wave N, N5): the student SPEAKS their answer and the practice dock gets text. Auth-gated pass-through to the Forge STT service - the model is GPU-sized and lives where the GPU lives, exactly like the tutor's brain. A dead bridge answers in words, not a hang: the student can always fall back to typing.DELETE/education/lessons/{lesson_id}· scope education:writeDelete LessonSame rule one level down: a lesson nobody has practised in deletes; one with lab completions or quiz results behind it does not.PATCH/education/lessons/{lesson_id}· scope education:writeUpdate LessonGET/education/lessons/{lesson_id}/blocks· scope education:readList Lesson BlocksPOST/education/lessons/{lesson_id}/compute-sage· scope education:writeCompute Sage In LessonPOST/education/lessons/{lesson_id}/draft-code· scope education:writeDraft Code LessonReal AI-drafted Python/Sage code from a plain-English description (task #159/160), meant for insertion into a real Jupyter cell - the teacher still reviews it and must explicitly click Run before anything executes in the sandbox. Never executed here - text only.POST/education/lessons/{lesson_id}/draft-diagram· scope education:writeDraft Diagram LessonAI-drawn schematic for a lesson (Tenant Zero, 2026-08-26 - the Emperor: a lesson on circuits NEEDS a circuit). The model drafts an SVG figure, it is sanitized to inert shapes-and-text, and it lands in the lesson's EXISTING image slot - the student page already renders that slot, so no new surface. The teacher sees it immediately and can replace or delete it like any uploaded image.POST/education/lessons/{lesson_id}/draft-latex· scope education:writeDraft Latex LessonReal AI-drafted LaTeX from a plain-English description (task #159/160) - Education's real analog of Publishers' draft_latex. The teacher still edits it live in MathLive and must explicitly save the lesson - this endpoint only ever returns text.POST/education/lessons/{lesson_id}/image· scope education:writeUpload Lesson ImageGET/education/lessons/{lesson_id}/image· scope education:readGet Lesson ImageDELETE/education/lessons/{lesson_id}/image· scope education:writeDelete Lesson ImagePOST/education/lessons/{lesson_id}/jupyter/execute· scope education:writeExecute Jupyter Cell LessonPOST/education/lessons/{lesson_id}/jupyter/start· scope education:writeStart Jupyter Session LessonPOST/education/lessons/{lesson_id}/jupyter/stop· scope education:writeStop Jupyter Session LessonGET/education/lessons/{lesson_id}/lab-completion· scope education:readGet Lab CompletionThe caller's own real completion record for this lesson's VR Lab Console (id 76's mandatory interactive-practice requirement) - None if they haven't finished it (or haven't started), never fabricated.POST/education/lessons/{lesson_id}/lab-completion· scope education:writeRecord Lab CompletionCalled once the client has confirmed every interactive object in the lesson's scene has genuinely been inspected - a real per-student record, not a client-only toy. RLS (lesson_lab_completion_write) independently re-enforces active enrollment; this endpoint doesn't duplicate that check, it relies on the database to reject an ineligible write.GET/education/lessons/{lesson_id}/lab-environment· scope education:readGet Lab EnvironmentServes the lab's panorama. RLS on the lesson_scenario SELECT already scopes visibility (published, or staff of the lesson's school) - a row coming back proves the caller may see this lab, and therefore its environment. Same reasoning as the lesson-image route. LOW-BANDWIDTH MODE (P02 Wave K), for the markets the payment rails actually serve. An 8K panorama is ~3.8 MB, which is a real cost on a me…POST/education/lessons/{lesson_id}/move· scope education:writeMove LessonPOST/education/lessons/{lesson_id}/reparent· scope education:writeReparent LessonMove a lesson into a different module of the SAME course.GET/education/lessons/{lesson_id}/scenario· scope education:readGet ScenarioPUT/education/lessons/{lesson_id}/scenario· scope education:writeSet ScenarioPOST/education/lessons/{lesson_id}/scenario/preview· scope education:writePrepare Lab ScenePrepares BOTH halves of a real-life lab (plan v9, addition 23): the interactive scene and its photoreal 360° environment. Either half that already exists is kept; the environment failing NEVER fails the lab - a missing picture must not block mastery.GET/education/lessons/{lesson_id}/sections· scope education:readList SectionsA lesson's sections, in order. An empty list is the honest answer for every lesson that predates this wave - and the reason the level could be added to live data at all.POST/education/lessons/{lesson_id}/sections· scope education:writeCreate SectionDELETE/education/modules/{module_id}· scope education:writeDelete ModuleRemove a module - and everything under it, which is why it refuses once a student has done anything inside it (Tenant Zero, 2026-08-26: the Emperor typed a module title into the wrong box and had no way to take it back).PATCH/education/modules/{module_id}· scope education:writeRename ModuleGET/education/modules/{module_id}/coverage· scope education:readModule CoverageDoes this module's lab, project and exam test everything its sections and lessons teach? The Emperor's rule, made checkable. `gaps` is the list a builder shows in words: "two competencies this module teaches are never tested."GET/education/modules/{module_id}/lessons· scope education:readList LessonsPOST/education/modules/{module_id}/lessons· scope education:writeCreate LessonPOST/education/modules/{module_id}/move· scope education:writeMove ModuleGET/education/modules/{module_id}/shape· scope education:readModule ShapeWhat shape is this module, right now? Shown before saving, so nobody names a template after a module without first seeing what they are about to hold everything else to.POST/education/modules/{module_id}/students/{student_user_id}/certificate· scope education:writeIssue Module CertificateA module is a mini course, so mastering one is worth showing. REFUSED UNLESS THE MODULE IS ACTUALLY MASTERED, by the platform's own existing rule - the same `_is_module_complete` that gates everything else, so a credential can never mean less than the progress bar beside it.GET/education/my/community· scope education:readMy CommunityThe learner's whole community in one reading: streak and badges (awarded from real completions on this very read), the published word from every school they study at, upcoming published events, and their own rooms. A student with nothing yet gets honest zeros and a standing invitation, never a blank.GET/education/my/progress/{scope}/{scope_id}· scope education:readMy ProgressWhat THIS learner can now do, at whichever level they are standing.GET/education/ops/schools/{school_id}/analytics· scope education:readSchool AnalyticsSchool analytics, optionally scoped to one academic term (P02 Wave M). WHAT `term_id` SCOPES, AND WHAT IT DELIBERATELY DOES NOT. Enrolments and attendance become term-scoped: an enrolment stores its term (migration 397) and attendance is filtered by the term's date window - the same two grains the Registrar uses, so a school's summary can never disagree with the report cards it is a summary of. S…GET/education/ops/schools/{school_id}/export/roster.csv· scope education:readExport Roster CsvEvery enrollment in the school, with the grade and attendance facts beside it. EMPTY CELLS, NOT ZEROS. A student with nothing scored gets a blank score column, because a spreadsheet full of zeros is a spreadsheet that will be averaged - and an average that counts "not yet marked" as nought is a lie a school might act on.GET/education/ops/schools/{school_id}/today· scope education:readClasses TodayPOST/education/overrides· scope education:writeGrant OverrideA teacher lets a student past one gate, with a reason. THE HAND IS A PERSON'S. Wave F ruled that the tutor coaches and never grades; opening a gate is a grading decision in everything but name, so no model makes it. The reason is required by the column, by this endpoint and by the screen - an override with no reason is indistinguishable from a mistake, and a student's file should say who decided …GET/education/program-certificates/mine· scope education:readList My Program CertificatesPOST/education/program-certificates/{certificate_id}/revoke· scope education:writeRevoke Program CertificateGET/education/program-certificates/{certificate_id}/verify· scope education:readVerify Program CertificatePOST/education/programme-templates· scope education:writeCreate Programme TemplateBless one programme and make its shape the standard for the others. The same three refusals the module level makes, for the same reasons. A programme with no courses describes nothing and every comparison against it would report "matches" - success it never checked.GET/education/programme-templates/{template_id}/compare/{program_id}· scope education:readCompare ProgrammeHow does this programme differ from the shape? Facts only; nothing is changed.GET/education/programmes/{program_id}/shape· scope education:readProgramme ShapeWhat shape is this programme, right now? Shown before saving, for the same reason the module one is: nobody should name a standard after a programme without seeing the numbers first.GET/education/programs/{program_id}· scope education:readGet ProgramPATCH/education/programs/{program_id}· scope education:writeUpdate ProgramGET/education/programs/{program_id}/certificates· scope education:readList Program CertificatesPOST/education/programs/{program_id}/certificates/{student_user_id}· scope education:writeIssue Program CertificateReal, computed program-completion eligibility - never fabricated: a program-wide certificate can only be issued once every linked course already has a real, non-revoked certificate.course row for this exact student. A program with zero linked courses can never be "complete" - rejected the same as a program still missing a real course certificate, not treated as a vacuous pass.POST/education/programs/{program_id}/courses· scope education:writeAttach Program CourseDELETE/education/programs/{program_id}/courses/{course_id}· scope education:writeDetach Program CoursePOST/education/programs/{program_id}/enroll· scope education:writeEnroll In ProgramGET/education/progress/mine· scope education:readMy ProgressGET/education/prose/{scope}/{scope_id}· scope education:readProse At ScopeThe written content under one scope, for checks that must read the prose itself. The review reads TITLES - it can say a section is empty, duplicated or untested, and nothing at all about whether what is written in it is true. A worked example on production concluded that a 32 V reading against a 36 V expectation with a +/-2 V tolerance was "within expected parameters" (2026-08-27). Every structur…PUT/education/questions/{question_id}· scope education:writeEdit QuestionGET/education/quizzes/{quiz_id}/my-result· scope education:readMy Quiz ResultPOST/education/quizzes/{quiz_id}/proctoring-events· scope education:writeLog Proctoring EventGET/education/quizzes/{quiz_id}/proctoring-events/mine· scope education:readMy Proctoring EventsGET/education/quizzes/{quiz_id}/proctoring-events/{student_user_id}· scope education:readStudent Proctoring EventsGET/education/quizzes/{quiz_id}/proctoring-report· scope education:readProctoring ReportGET/education/quizzes/{quiz_id}/questions· scope education:readList QuestionsPOST/education/quizzes/{quiz_id}/questions· scope education:writeAdd QuestionGET/education/quizzes/{quiz_id}/results· scope education:readQuiz ResultsGET/education/quizzes/{quiz_id}/scenarios· scope education:readList ScenariosEvery scenario in this assessment, each with its own questions already attached. Nested rather than flat because a stem without its questions is unusable and a question without its stem is unreadable - a caller that had to join them itself would eventually render one without the other.POST/education/quizzes/{quiz_id}/scenarios· scope education:writeCreate ScenarioPOST/education/quizzes/{quiz_id}/submit· scope education:writeSubmit QuizPOST/education/results/{result_id}/amend· scope education:writeAmend ResultCorrect a sealed result by issuing a NEW one that supersedes it. THE OLD ROW IS NEVER TOUCHED. It stays exactly as issued, signature intact, because the point of an academic record is that you can still see what was said before the correction. That is also the only design compatible with M2's grant, which is SELECT and INSERT only - the application cannot modify a sealed row even if it wanted to.…GET/education/review/{scope}/{scope_id}· scope education:readReview At ScopeTHE SAME QUESTION AT FOUR LEVELS — the Emperor's architecture (2026-08-27). "When we use them in the course page is to discover and auto fix/cover gaps from the Modules' headings... in the module page... from the Lessons' headings... in the Lessons' page... from the sections' headings... in the section page... from the section's contents to make sure all competencies will be cover…DELETE/education/scenarios/{scenario_id}· scope education:writeDelete ScenarioRefused once anybody has answered its questions. The same rule as a module, a lesson and a section: a teacher's tidy-up never erases a student's record. Deleting the stem would cascade to its questions, and their answers are part of somebody's result.GET/education/schools· scope education:readList SchoolsPOST/education/schools· scope education:writeCreate SchoolGET/education/schools/mine· scope education:readGet My School EndpointPOST/education/schools/{school_id}/announcements· scope education:writeCreate AnnouncementGET/education/schools/{school_id}/announcements· scope education:readList AnnouncementsRLS does the honest split: staff see drafts, an enrolled student sees only the published ones, a stranger sees none.POST/education/schools/{school_id}/announcements/draft-digest· scope education:writeDraft Weekly DigestAUTOMATION WITH ITS HANDS TIED: a deterministic reading of the school's own week - new students, discussion activity, upcoming events, live classes - assembled into a DRAFT announcement. NO MODEL writes it (the growth-advisor doctrine), and it goes nowhere until a person publishes. Every number below is a count of real rows.GET/education/schools/{school_id}/courses· scope education:readList CoursesPOST/education/schools/{school_id}/courses· scope education:writeCreate CourseGET/education/schools/{school_id}/members· scope education:readList School MembersPOST/education/schools/{school_id}/members· scope education:writeAdd School MemberPATCH/education/schools/{school_id}/members/{member_id}/grade-level· scope education:writeSet Grade LevelGET/education/schools/{school_id}/programs· scope education:readList School ProgramsPOST/education/schools/{school_id}/programs· scope education:writeCreate ProgramGET/education/schools/{school_id}/subscriptions· scope education:readSchool SubscriptionsA sub-portal school's own adopted curricula (with a behind flag).GET/education/schools/{school_id}/terms· scope education:readList TermsPOST/education/schools/{school_id}/terms· scope education:writeCreate TermPATCH/education/schools/{school_id}/terms/{term_id}· scope education:writeUpdate TermDELETE/education/schools/{school_id}/terms/{term_id}· scope education:writeDelete TermDelete a term - unless it is holding academic history (Wave M, migration 398). THE REFUSAL IS CHECKED HERE AND ENFORCED BY THE DATABASE, which is deliberate belt and braces for two different failures. The count below produces a sentence a person can act on; the `ON DELETE RESTRICT` constraint behind it is what actually holds if a future caller forgets to ask, or if an enrolment is created between…GET/education/schools/{slug}· scope education:readGet School By SlugPATCH/education/sections/{section_id}· scope education:writeUpdate SectionDELETE/education/sections/{section_id}· scope education:writeDelete SectionRefused once a student has completed it - the same rule as a module or a lesson (Tenant Zero, 2026-08-26): a teacher's tidy-up never erases a student's record.GET/education/sections/{section_id}/blocks· scope education:readList Section BlocksPOST/education/sections/{section_id}/complete· scope education:writeComplete SectionA student marks their own section done. BY THEIR OWN HAND ONLY - the RLS INSERT policy refuses a row written for anybody else, the same rule Wave D drew for guardian consent.POST/education/sections/{section_id}/move· scope education:writeMove SectionMove a section one place, and renumber the whole lesson while we are here. WHY RENUMBER EVERY TIME. Accepting the AI's suggested headings left positions 0, 2, 4, 6, 8 in the Emperor's real course: each suggestion button carried the position the list had when it was drawn, and the list grew under it (2026-08-26). Nothing was broken - order is by position - but gaps invite a future collision, and a…POST/education/sections/{section_id}/reparent· scope education:writeReparent SectionMove a section under a different lesson of the SAME course. A student's completion of this section travels with it - `lesson_section_completion` points at the section, not at where it used to live - so moving a section never erases work.GET/education/students/{student_user_id}/overrides· scope education:readList OverridesEvery gate opened for this student, and why. Readable BY THE STUDENT as well as by staff - a person is entitled to know what was decided about them.GET/education/students/{student_user_id}/report-card· scope education:readReport CardA report card for one student in one term - available at ANY time, which is the point. Nothing here waits for a term to end. A parent asking in week three gets week three's answer, labelled as provisional, rather than "come back in December".GET/education/students/{student_user_id}/transcript· scope education:readTranscriptA student's full academic record across every school, for the people entitled to read it. NO CONSENT IS INVOLVED HERE and that is deliberate. This is the student reading their own record (or their school's staff, or a minor's guardian) through `term_result`'s own policies. Consent governs SHARING - a different act, a different audience, and the next endpoint.DELETE/education/study-group-messages/{message_id}· scope education:writeRemove Group MessagePOST/education/study-groups/{group_id}/join· scope education:writeJoin Study GroupPOST/education/study-groups/{group_id}/leave· scope education:writeLeave Study GroupGET/education/study-groups/{group_id}/messages· scope education:readList Group MessagesPOST/education/study-groups/{group_id}/messages· scope education:writePost Group MessageGET/education/submissions/mine· scope education:readList My SubmissionsPATCH/education/submissions/{submission_id}· scope education:writeGive FeedbackPOST/education/subscriptions/{subscription_id}/sync· scope education:writeSync SubscriptionPOST/education/templates· scope education:writeCreate TemplateBless one module and make its shape the standard.GET/education/templates· scope education:readList TemplatesEvery template this reader can see: their own school's, and the PLATFORM standard for the kind of school they run. RLS decides which, and deliberately so - a tenant is held to the platform template for their category, so they must be able to read it, and they must not see another school's. Platform templates sort first because they are the standard the rest are variations on.DELETE/education/templates/{template_id}· scope education:writeDelete TemplateGET/education/templates/{template_id}/compare/{module_id}· scope education:readCompareHow does this module differ from the shape? Facts only; nothing is changed. Worded as comparison throughout - "has four lessons where the template has five", never "is missing a lesson". The teacher decides whether the difference is a fault, and on a genuinely smaller topic it is not.POST/education/templates/{template_id}/promote· scope education:writePromote TemplateMake one school's template the standard for a KIND of school. Owner only. WHY PROMOTION RATHER THAN AUTHORING. A template is DERIVED from a real module, never typed - that is what stops it becoming an aspiration nobody has met. The owner runs no school and so has no module to read a shape from, which leaves exactly one honest way to create a platform standard: take one a tenant has already met, a…GET/education/terms/{term_id}/report-cards· scope education:readTerm Report CardsEvery student's standing in one term, for that school's staff - the registrar's own view. SEALED AND UNSEALED TERMS BOTH ANSWER, and the difference is reported rather than hidden. An unsealed term computes live (so it moves, and `sealed` says so); a sealed term reads the frozen rows. A screen that only worked after sealing would be useless during the term it is about.POST/education/terms/{term_id}/seal· scope education:writeSeal TermFreeze this term's results, so a transcript stops moving when a teacher regrades. STAFF ONLY, and enforced by the INSERT policy on `term_result` rather than only by the check below - the check produces a sentence, the policy is what holds. SEALING IS ONE-WAY AND ONE-TIME. A second seal is refused rather than overwriting, because overwriting is precisely the thing this table exists to make imposs…POST/education/transcript/shares· scope education:writeCreate Transcript ShareMint a shareable transcript - the student's own act, and nobody else's. THE SCHOOL CANNOT DO THIS FOR THEM. The INSERT policy admits only rows whose `student_user_id` is the caller, so a school publishing a student's record is not merely discouraged, it is impossible through the application. That is the mirror of Wave D, where only a guardian could consent for a minor; here only the student can c…GET/education/transcript/shares/mine· scope education:readMy Transcript SharesEvery transcript link this student has ever minted, withdrawn ones included. "Who have I sent my grades to?" is a question a person is entitled to answer later, which is why 402 has no DELETE policy: revoking a share withdraws the ACCESS and keeps the RECORD. A list that quietly dropped withdrawn shares would answer a different, more comfortable question. Scoped by the caller's own RLS rather th…POST/education/transcript/shares/{share_id}/revoke· scope education:writeRevoke Transcript ShareWithdraw a shared transcript. Immediate, and the record that it existed stays. There is no DELETE policy on the table on purpose: "who have I sent my grades to?" is a question a student is entitled to answer later, and deleting the row would erase the answer along with the access.GET/education/translations/pending-review· scope education:readList Pending ReviewEvery draft_ready translation this user may act on, across ALL their schools. WHY THIS EXISTS. `mark-reviewed` has always been here, and so has the `human_reviewed` state - but the only way to FIND a pending draft was GET /courses/{course_id}/translations, one course at a time. A school with forty courses had no way to see what was waiting, so in practice nothing ever got reviewed and `human_revi…DELETE/education/translations/{translation_id}· scope education:writeDelete TranslationPOST/education/translations/{translation_id}/create-course· scope education:writeCreate Course From TranslationMaterializes a real, human-reviewed translation into a genuine, independent `course` row (with real modules and lessons) - a real sibling of the original, students can actually enroll in and study, not just translated text sitting in a draft table. Only reachable from status='human_reviewed' - the explicit mark_reviewed step above is mandatory first. Each translation can only be materialized once …PATCH/education/translations/{translation_id}/mark-reviewed· scope education:writeMark ReviewedA human explicitly confirming the MT draft has been reviewed - never automatic, matching this project's standing 'AI drafts, never authoritative' pattern. Education has no structured review_cycle system the way Publishers does (a genuine, real scoping difference, not an oversight - Education's own editorial model is simpler: draft/published/archived with no reviewer roles), so this is a plain, dir…GET/education/translations/{translation_id}/modules· scope education:readGet Translated ContentThe real, structured translated preview - every module with its own real translated lessons nested inside, in order - what a reviewer actually reads before deciding to materialize it into a real course.GET/education/tutor/courses/{course_id}/messages· scope education:readGet Tutor HistoryPOST/education/tutor/courses/{course_id}/messages· scope education:writeSend Tutor MessageGET/education/tutor/courses/{course_id}/sessions· scope education:readList Course Tutor SessionsThe educator view's index: every tutor session in this course. Staff-gated at the endpoint for a clean sentence; RLS (390) is the real control on every row.GET/education/tutor/sessions/{session_id}/messages· scope education:readGet Session MessagesOne transcript. RLS decides who this answers for: the student themself, staff of the course's school, a minor student's guardian, or the owner - anyone else gets an honest 404 because for them the session does not exist.GET/education/tutor/students/{student_user_id}/sessions· scope education:readList Student Tutor SessionsThe guardian view's index: a linked MINOR student's tutor sessions, with course titles in student_name's place left to the client. RLS (390) admits rows only for the student, their school staff, a minor's guardian, or the owner - an empty list is the honest answer for everyone else.POST/education/tutor/{course_id}/messages/{message_id}/speak· scope education:writeSpeak Tutor MessageThe character speaks their line aloud (Wave N, N4) - the same counsel guard as the Concierge's /speak, same shape exactly: only a SERVER-OWNED line is ever voiced, here a stored assistant reply from the caller's OWN tutor session (RLS scopes the SELECT, and the role filter means a student cannot make the voice read their own words back as if the platform said them). Voice ladder identical to the C…GET/staff-growth/benefit-plans· scope education:readList Benefit PlansPOST/staff-growth/benefit-plans· scope education:writeCreate Benefit PlanThe owner defines the catalog - 'all types' by construction, because a type is a row. Fitinty records elections and exports them; the CARRIER administers the benefit.GET/staff-growth/cadence-alerts· scope education:readCadence AlertsEVERY ALERT CARRIES A PROPOSED ACTION (the standing rule). Three kinds, computed on read from real rows: a review past its cadence, a role's required training missing or unfinished, a certification aged past recert_days. A quiet list means the checks RAN and found nothing - the response says so rather than returning a bare empty list.GET/staff-growth/cadence-settings· scope education:readGet CadencePOST/staff-growth/cadence-settings· scope education:writeSet CadenceThe owner's dial. NULL/absent = no cadence - an org that reviews ad-hoc is a choice, not a defect, so no alert fires for it.GET/staff-growth/documents/{document_id}/file· scope education:readRead DocumentGET/staff-growth/employees/{employee_id}/compensation· scope education:readList CompensationOwner-only BY RLS: anyone else gets an empty list, and an empty list here means 'not yours to see' as much as 'none recorded' - the endpoint cannot tell you which.POST/staff-growth/employees/{employee_id}/compensation· scope education:writeRecord CompensationGET/staff-growth/employees/{employee_id}/documents· scope education:readList DocumentsPOST/staff-growth/employees/{employee_id}/documents· scope education:writeUpload DocumentCUSTODY, not generation: the paystub or W-2 the tenant's real payroll provider issued gets a home the employee and the owner can both reach - Fitinty computed none of it and never will from here.POST/staff-growth/employees/{employee_id}/elections· scope education:writeElect BenefitThe employee's own act (their login, or the owner recording a paper election). Electing a closed plan is refused - the window is the owner's dial.GET/staff-growth/employees/{employee_id}/elections· scope education:readList ElectionsPOST/staff-growth/employees/{employee_id}/enroll· scope education:writeEnroll EmployeeOne click from the roster into the org's OWN school - Education's machinery does the rest (spine, competencies, coverage, credentials). Never a parallel training system.GET/staff-growth/employees/{employee_id}/offboarding· scope education:readOffboardingThe checklist materializes from the org's template (seeded with honest defaults on first use) the first time a terminated employee's is read.POST/staff-growth/employees/{employee_id}/offboarding/done· scope education:writeOffboarding DoneGET/staff-growth/employees/{employee_id}/orientation· scope education:readOrientationEH1 - THE DAY-ONE PACKET. The checklist (offboarding's mirror, materialized on first read) plus the real facts a new person needs: who their manager is and what training their role requires - read from rows, never invented.POST/staff-growth/employees/{employee_id}/orientation/done· scope education:writeOrientation DoneGET/staff-growth/employees/{employee_id}/reviews· scope education:readList ReviewsPOST/staff-growth/employees/{employee_id}/reviews· scope education:writeCreate ReviewGET/staff-growth/employees/{employee_id}/screening· scope education:readList ScreeningPOST/staff-growth/employees/{employee_id}/screening· scope education:writeRecord ScreeningHis question, his own hard line: eligibility-to-work and background checks are recorded as FACTS - kind, status, date, category note. Never a number.GET/staff-growth/employees/{employee_id}/training· scope education:readEmployee TrainingThe roster's honest training view: the employee's enrollments in THIS org's courses, each with its status. No login yet = no training possible, said in words.GET/staff-growth/employees/{employee_id}/transitions· scope education:readList TransitionsPOST/staff-growth/employees/{employee_id}/transitions· scope education:writeRecord TransitionA promotion is a RECORDED transition that itself applies the change - one writer, so the history and the record can never disagree.POST/staff-growth/employees/{employee_id}/verification· scope education:writeCreate Verification"Worked here, X to Y" on the same signature discipline as certificates. Works for current AND former employees - the reference an ex-employee actually needs. Revoke by deleting the row; the public link then answers not-found, never a fabricated 'valid'.GET/staff-growth/employees/{employee_id}/warnings· scope education:readList WarningsPOST/staff-growth/employees/{employee_id}/warnings· scope education:writeCreate WarningGET/staff-growth/my-work· scope education:readMy WorkONE honest view of the signed-in person's working life: their employee records, what waits for their hand (reviews to sign, warnings to acknowledge), their documents and their courses. Sections whose waves have not landed (shifts, the ID card) are simply absent - never a fake placeholder.GET/staff-growth/org-courses· scope education:readOrg CoursesWhat this org can train ON: its own schools' courses (any type - a store's training school is just a school whose type says so).GET/staff-growth/payroll-export· scope education:readPayroll ExportTHE PAYROLL-READY EXPORT - the boundary made useful. Hours from the ONE time arithmetic, the latest compensation row, and elected benefits, per employee, as CSV a real payroll provider consumes. NO tax math, NO withholdings, NO net pay - the header says so, so the file can never be mistaken for a payroll run. Owner-only.POST/staff-growth/reviews/{review_id}/acknowledge-offline· scope education:writeAcknowledge OfflineFor the record-only worker with no login: the owner records that the review was read together, with a note that must exist - a silent checkbox would be a secret file with extra steps.POST/staff-growth/reviews/{review_id}/share· scope education:writeShare ReviewDraft -> shared: from this moment the employee's own login can read it (RLS's owner-or-self policy) and only THEY can sign it.POST/staff-growth/reviews/{review_id}/sign· scope education:writeSign ReviewTHE EMPLOYEE signs - nobody else. The check is identity, not role: the signer's login must be the one linked to the reviewed employee.GET/staff-growth/role-requirements· scope education:readList RequirementsPOST/staff-growth/role-requirements· scope education:writeAdd RequirementDELETE/staff-growth/role-requirements/{requirement_id}· scope education:writeRemove RequirementPOST/staff-growth/warnings/{warning_id}/acknowledge· scope education:writeAcknowledge WarningThe employee acknowledges - identity, not role, exactly like signing a review.
Marketplace - १०६ doors - marketplace:read, marketplace:write
The commerce spine: listings, carts, orders and vendors, shared by every pillar that sells.
GET/counter/products· scope marketplace:readCounter ProductsWhat the counter can ring up: the org's PUBLISHED products from the same mirror the storefront serves - one catalog, two doors.POST/counter/sales· scope marketplace:writeRecord SaleGET/counter/sales· scope marketplace:readList SalesPOST/customer-issues· scope marketplace:writeRaise IssueThe customer's door. Recorded, told to the book, counted on the advisor - executed by nobody.GET/customer-issues/business/{organization_id}· scope marketplace:readBusiness IssuesThe business's side: open first. Membership asked IN THE BODY, in words - the wall would answer with an empty list, which reads exactly like a business nobody has asked anything of.GET/customer-issues/mine· scope marketplace:readMy IssuesThe person's own requests, on their own cursor (the self arm); business names on the system cursor.POST/customer-issues/{issue_id}/answer· scope marketplace:writeAnswer IssueThe business answers in words; closing needs a reason (the row's own CHECK). The customer's words are never touched. Told to the book so the answer sits beside the request.GET/disputes· scope marketplace:readDispute InboxONE inbox. Opening it IS the sweep - no daemon to trust, and the note says when and what was read. Every dispute carries its proposed action.POST/disputes/{dispute_id}/assemble· scope marketplace:writeAssemblePOST/disputes/{dispute_id}/draft· scope marketplace:writeDraft ResponseAI DRAFTS, GROUNDED IN THE ASSEMBLED PACK ONLY - it runs after the readers, refers to nothing outside them, and what it writes stays a draft until the owner submits.PATCH/disputes/{dispute_id}/note· scope marketplace:writeEdit NotePOST/disputes/{dispute_id}/submit· scope marketplace:writeSubmit ResponseTHE OWNER'S HAND. Pushes the pack + draft + note to Stripe as the dispute's evidence, on the TENANT'S OWN account. Final: the card network reads what is sent.GET/marketplace/affiliate/clickbank-config· scope marketplace:readGet Clickbank ConfigPOST/marketplace/affiliate/clickbank-config· scope marketplace:writeSave Clickbank ConfigReal upsert - saving a config a second time keeps the SAME real webhook_token (it's the org's own permanent webhook address, already pasted into their live ClickBank account) but rotates the secret key, same "replace the stored secret" convention as the other six networks' own credential save endpoint.GET/marketplace/affiliate/clickbank-transactions· scope marketplace:readList Clickbank TransactionsGET/marketplace/affiliate/network-credentials· scope marketplace:readList Network CredentialsReal, per-org configured network connections. Never returns a secret - only the connection/sync bookkeeping a caller needs to decide whether a network is already set up and whether its last real sync succeeded.POST/marketplace/affiliate/network-credentials· scope marketplace:writeSave Network CredentialReal upsert - saving a credential for a network the org has already configured replaces the stored secret rather than creating a duplicate row (the real UNIQUE(organization_id, network) constraint is what enforces this, not just application logic).DELETE/marketplace/affiliate/network-credentials/{network}· scope marketplace:writeDelete Network CredentialGET/marketplace/affiliate/network-credentials/{network}/catalog· scope marketplace:readSearch Network CatalogReal, on-demand product/ad catalog search using the org's own stored credential - genuinely distinct from .../sync above (which reads a vendor's own PAST commission history). Never writes to last_synced_at/last_sync_status/last_sync_detail - a catalog search is a read-only lookup, not a sync event. For jvzoo/warriorplus/ rakuten_advertising this honestly reports that no real catalog endpoint could…POST/marketplace/affiliate/network-credentials/{network}/sync· scope marketplace:writeSync Network CredentialRuns the real, network-specific API call using the org's own stored credential - never a mocked or fabricated result. A real network/auth failure is persisted honestly as last_sync_status='error' with the actual error text, not silently swallowed or reported as success. Combined Roadmap row 6: a real successful sync's own total_amount/count (already computed by sync_network(), previously thrown a…GET/marketplace/affiliate/offers· scope marketplace:readList Published OffersPOST/marketplace/affiliate/offers· scope marketplace:writeCreate OfferGET/marketplace/affiliate/offers/mine· scope marketplace:readList My OffersPATCH/marketplace/affiliate/offers/{offer_id}· scope marketplace:writeUpdate OfferPOST/marketplace/affiliate/offers/{offer_id}/click· scope marketplace:writeRecord ClickGET/marketplace/affiliate/offers/{offer_id}/clicks· scope marketplace:readCount ClicksPOST/marketplace/affiliate/offers/{offer_id}/promo-drafts· scope marketplace:writeDraft Promo ContentRow 5 - AI-drafted affiliate promo content, reusing Content Factory's real Creative Agent (draft_creative_json) and Visual Agent (generate_visual_asset) directly, imported from marketing_content_factory rather than duplicated here. The "research" grounding is the offer's own real merchant/product/disclosure fields - genuinely real connected data, not a knowledge-base search (there's no marketing_p…GET/marketplace/affiliate/offers/{offer_id}/promo-drafts· scope marketplace:readList Promo DraftsGET/marketplace/affiliate/promo-drafts/{asset_id}/image· scope marketplace:readDownload Promo ImagePATCH/marketplace/affiliate/promo-drafts/{asset_id}/status· scope marketplace:writeSet Promo StatusPOST/marketplace/affiliate/webhooks/clickbank/{webhook_token}· scope marketplace:writeReceive Clickbank NotificationThe real, unauthenticated (no Fitinty session - ClickBank itself calls this) receiver ClickBank's own INS posts to. Runs under `db.system_cursor`'s owner-bypass RLS context, same as OnlyOffice's own server-callback endpoints - real access control here is the webhook token itself (an unguessable per-org secret path segment) PLUS the AES decryption succeeding at all, which is only possible with the …POST/marketplace/digital-assets· scope marketplace:writeUpload Digital AssetGET/marketplace/digital-assets/by-product/{medusa_product_id}· scope marketplace:readGet Asset By ProductIs this product a digital download - asked by the BUYER'S OWN ORDER SCREEN, once per line item, to decide whether to offer a Download button. READ ON THE SYSTEM CURSOR SINCE 585, and the reason is the whole design: the row is walled to the SELLING organization, because a buyer's right to a file is a Medusa receipt and no Postgres predicate can see it. Left on the caller's cursor this would answer…GET/marketplace/digital-assets/mine· scope marketplace:readList My AssetsDELETE/marketplace/digital-assets/{asset_id}· scope marketplace:writeDelete Digital AssetGET/marketplace/digital-assets/{asset_id}/download· scope marketplace:readDownload Digital AssetGET/marketplace/integrations· scope marketplace:readList IntegrationsReal status for the integrations this pillar actually has - not a catalog of connectors that could theoretically exist. Extend this list only when a new integration is genuinely wired in, the same discipline already applied to Workspace's System Health page.POST/marketplace/landing· scope marketplace:writeUpsert LandingCreate or rewrite the page as a DRAFT - publishing is its own deliberate door.GET/marketplace/landing/mine· scope marketplace:readMy LandingsPOST/marketplace/landing/{landing_id}/publish· scope marketplace:writePublish LandingGET/marketplace/luxury/provider-status· scope marketplace:readProvider StatusPOST/marketplace/luxury/requests· scope marketplace:writeCreate RequestGET/marketplace/luxury/requests· scope marketplace:readList RequestsPOST/marketplace/luxury/requests/{request_id}/cancel· scope marketplace:writeCancel RequestPOST/marketplace/luxury/requests/{request_id}/record-result· scope marketplace:writeRecord ResultOwner-only. Records a REAL provider verdict (a genuine Entrupy certificate). This is the ONLY way a request reaches an authenticated/not_authentic/inconclusive status — never fabricated by code. Requires a real certificate id so a verdict can always be traced to a real authentication.GET/marketplace/orders/mine· scope marketplace:readList My OrdersA buyer's own orders, read from Medusa. WHAT WAS WRONG. This queried `marketplace_order`, a table dropped when the Marketplace moved to Medusa - `marketplace_return` carries a plain `medusa_order_id TEXT` rather than a foreign key for exactly that reason. Every call raised `relation "marketplace_order" does not exist`, so the endpoint answered 500 for every signed-in person. Nothing reported it b…GET/marketplace/promotions/mine· scope marketplace:readMy PromotionsThe seller's shelf, each row's state in words - a lapsed one says so.GET/marketplace/promotions/prices· scope marketplace:readPromo PricesThe picker - platform data, with the billing truth stated per row.POST/marketplace/promotions/request· scope marketplace:writeRequest PromotionThe seller's ask. Their own PUBLISHED product only; one live or pending promotion per product at a time. Born pending_payment - nothing surfaces until activation, and the response says exactly what happens next.POST/marketplace/promotions/{promotion_id}/activate· scope marketplace:writeActivate PromotionTHE EMPEROR'S HAND - the standing comp ruling until Dodo carries slot products. Stamped 'comp': a comped spotlight is never dressed as revenue, and NO ledger row is written (the ledger records captured payments, never favors).POST/marketplace/rd· scope marketplace:writeCreate RdGET/marketplace/rd· scope marketplace:readList RdPOST/marketplace/rd/{project_id}/advance· scope marketplace:writeAdvance RdOne stage forward, never skipping - and NEVER without a real note, because the stages that matter (sample, test, package) are HUMAN handshakes with suppliers and manufacturers; the record tracks them, it does not fake them.POST/marketplace/returns· scope marketplace:writeRequest ReturnGET/marketplace/returns/mine· scope marketplace:readList My ReturnsGET/marketplace/returns/to-review· scope marketplace:readList Returns To ReviewFetches every pending return, then asks Medusa (via this vendor's own session) which underlying orders they can actually see - Medusa's own store-scoping does the real per-vendor narrowing here, since orders aren't tagged with a Fitinty-visible tenant_id the way our own tables are. See migration 011's comment for the reasoning.PATCH/marketplace/returns/{return_id}· scope marketplace:writeUpdate Return StatusGET/marketplace/suppliers· scope marketplace:readList SuppliersPOST/marketplace/suppliers· scope marketplace:writeCreate SupplierPOST/marketplace/suppliers/sweep· scope marketplace:writeSweep FeedsM3 - THE STANDING SWEEP, made real. Every ACTIVE url_feed and fitinty-partner supplier across the platform syncs through the ONE pipeline; each supplier's failure lands on its own row in words and never blocks the others. Owner-only: this is the Emperor's timer's door (24/7 decree - the doctor timer hits it; the launch checklist carries the systemd entry). A tenant syncs their own supplier from th…PUT/marketplace/suppliers/{supplier_id}· scope marketplace:writeUpdate SupplierPOST/marketplace/suppliers/{supplier_id}/forward· scope marketplace:writeForward OrderSANDBOX. Records the dropship forward the real rail will someday send - practiced end to end, honest about being practice. Nothing leaves the platform; the row says simulated_sent and every screen that shows it must say so too.POST/marketplace/suppliers/{supplier_id}/sync· scope marketplace:writeSync NowSync one supplier now. A csv_feed takes the uploaded sheet; a url_feed fetches its URL (public hosts only, size-capped). The standing sweep (POST /marketplace/suppliers/sweep, the Emperor's timer) runs the same `sync_supplier` for every active url_feed and fitinty partner feed - one pipeline, nothing to drift. (M3 note: an earlier docstring CLAIMED a nightly job that never existed - a promise in p…POST/marketplace/vendor· scope marketplace:writeCreate VendorGET/marketplace/vendor/all· scope marketplace:readList All VendorsPOST/marketplace/vendor/discounts· scope marketplace:writeCreate DiscountGET/marketplace/vendor/discounts· scope marketplace:readList DiscountsThe shelf, with REAL redemption counts read from Medusa per code.POST/marketplace/vendor/discounts/{discount_id}/deactivate· scope marketplace:writeDeactivate DiscountThe off switch - flipped in Medusa AND on the mirror, in that order, so a code that Medusa still honors is never shown as off.GET/marketplace/vendor/listing-options· scope marketplace:readListing OptionsWhat the listing form can honestly offer: the currencies real regions price in, and the categories this vendor's own catalog already uses (a picker seeded from their own data, free text allowed - a marketplace this young must not invent a taxonomy nobody asked for).GET/marketplace/vendor/mine· scope marketplace:readGet My Vendor EndpointGET/marketplace/vendor/orders· scope marketplace:readList Vendor OrdersPOST/marketplace/vendor/orders/{order_id}/fulfill· scope marketplace:writeFulfill OrderM2 - the seller's hand: every unfulfilled item into one fulfillment. When the order is backed by a PAID rail checkout, the Medusa-side payment is captured too, so the store's own records match the money that really moved - a practice-rail order is never dressed as captured.POST/marketplace/vendor/orders/{order_id}/ship· scope marketplace:writeShip OrderM2 - the tracking number goes on the fulfillment; the buyer's order page shows the shipped state the moment this lands.GET/marketplace/vendor/products· scope marketplace:readList Vendor ProductsPOST/marketplace/vendor/products· scope marketplace:writeCreate Vendor ProductOne listing, full width (LST1): many images (first leads), a real currency, a category, and a draft state. `file` stays accepted so older callers keep working.POST/marketplace/vendor/products/bulk-csv· scope marketplace:writeBulk Csv ImportBulk listings from a CSV (LST3) - the first way a whole catalog enters this platform without typing it in one product at a time. Columns: title (required), description, price, currency, category, status (draft|published, default draft). ALL-OR-NOTHING: every row is validated FIRST and a bad file is refused with each problem named by row number - a partial import that silently skipped rows would r…GET/marketplace/vendor/products/costs· scope marketplace:readList Listing CostsPUT/marketplace/vendor/products/{product_id}· scope marketplace:writeUpdate Vendor ProductThe end of write-once (LST1): title, words, price, currency, category and draft state are all the vendor's to change. Ownership is the vendor's own scoped Medusa session - a product their store cannot see cannot be edited, and the mirror updates in the same transaction so the storefront never serves the old words.DELETE/marketplace/vendor/products/{product_id}· scope marketplace:writeDelete Vendor ProductDeletes the Medusa product; the mirror row is DEACTIVATED, never deleted - history survives and a re-list reactivates it (the mirror's own standing rule).PUT/marketplace/vendor/products/{product_id}/cost· scope marketplace:writeSet Listing CostThe tenant's PRIVATE cost (441) - margin advice needs it, only they know it, and it never touches the public mirror. Ownership refereed by their scoped session as every product write is.POST/marketplace/vendor/products/{product_id}/images· scope marketplace:writeAdd Vendor Product ImagesAdd photos to an existing listing - enhanced through the same pipeline as every image upload on the platform, appended after the ones already there.GET/marketplace/vendor/questions· scope marketplace:readList My Product QuestionsThe seller's queue - unanswered first, because each is a buyer mid-decision. Which is exactly why the cap mattered: the ordering puts the unanswered at the top, so the hundred-and-first question a seller never sees is one somebody is still waiting on.POST/marketplace/vendor/questions/{question_id}/answer· scope marketplace:writeAnswer Product QuestionPOST/marketplace/vendor/questions/{question_id}/hide· scope marketplace:writeHide Product QuestionThe Commons' hand: hidden WITH a reason on the row, never erased - abuse happens, erasure of what was asked is how it compounds.POST/marketplace/vendor/referral-program· scope marketplace:writeSet ProgramThe seller's dial. Existing codes keep working when the program pauses - a code already in a friend's hands is a promise; pausing stops NEW codes only.GET/marketplace/vendor/referral-program· scope marketplace:readMy ProgramThe desk view: the dials, every code, every reward minted.POST/marketplace/vendor/vendor/mirror-sync· scope marketplace:writeSync Public Product MirrorBackfill + resync of the public-storefront product mirror (migration 233). Loops every vendor and lists their products through their OWN scoped Medusa session -- the same mechanism the vendor UI uses, and the only reliable store->products mapping (the admin store_id query param is silently ignored by the marketplace plugin; verified with a bogus-id probe before this was written). Credentials neve…PATCH/marketplace/vendor/{vendor_id}/verify· scope marketplace:writeSet Vendor VerifiedPOST/reviews/course/{course_id}· scope marketplace:writeReview CoursePOST/reviews/platform· scope marketplace:writeReview PlatformA tenant's word about Fitinty, published where visitors read it - the hard ones too. Proof: a real org membership. One review per membership; a new org earns another voice.POST/reviews/practice/{office_id}· scope marketplace:writeReview PracticeA practice is reviewed by a client who PAID it at least once on the rail (any kind - consultation, deposit, invoice). The proof ref is the client's ROOM: one relationship, one voice (the unique triple). Satisfaction ratings stay private to the practice; this is the public word, verified the same way every other review on the platform is.GET/reviews/reported· scope marketplace:readReported ReviewsThe Emperor's queue: everything reported, nothing yet hidden.POST/reviews/service/{listing_id}· scope marketplace:writeReview ServicePOST/reviews/{review_id}/hide· scope marketplace:writeHide ReviewTHE EMPEROR'S HAND ALONE, with a reason on the row - including reviews of Fitinty.POST/reviews/{review_id}/report· scope marketplace:writeReport ReviewThe subject's org REPORTS - it cannot hide. A seller who could hide critical reviews would be fake social proof by omission.GET/store-analytics/insights· scope marketplace:readStore InsightsEvery store the caller's organizations run, each with its trend, top products and AOV.POST/storefront-intents/{token}/claim· scope marketplace:writeClaim IntentExecutes a stored intent for the now-authenticated user - the real enrollment / booking / interest write, exactly as the signed-in flows do it. Single-use; expired or unknown tokens 404 (no oracle for guessing tokens).GET/supply/carry· scope marketplace:readWhat I CarryTHE STORE'S OWN READING: every consignment offered to or carried by this business, each line naming its supplier (on the system cursor - 591) and the organ it rides; what is owed per supplier and currency (lines minus the payouts recorded by hand); the payouts recorded; the business's own currency for the screen to print in. A member of another store reads 403 in words.POST/supply/consignments· scope marketplace:writeOfferThe supplier's offer, with the consent words RECORDED; born 'offered'.GET/supply/consignments· scope marketplace:readConsignmentsBoth sides on one door for one of the caller's organizations: what it consigned (and is owed), what it was offered and carries (and owes).POST/supply/consignments/{consignment_id}/accept· scope marketplace:writeAcceptTHE SELLER's word: its retail price, and the thing on ITS store. Only the seller's people accept.POST/supply/consignments/{consignment_id}/decline· scope marketplace:writeDeclinePOST/supply/consignments/{consignment_id}/stop· scope marketplace:writeStop CarryingTHE STORE'S hand: it stops carrying an accepted consignment WITH A REASON the supplier reads. The product comes down on the store's OWN cursor (it owns the vendor row); what sold stays owed; 'closed' is the CHECK's own word for it, and the supplier's withdraw stays the supplier's.POST/supply/consignments/{consignment_id}/withdraw· scope marketplace:writeWithdrawTHE SUPPLIER's hand, at will: what is unsold comes back and the seller's product comes down.GET/supply/sellers· scope marketplace:readSellersEvery tenant with a real store, so a supplier can offer to one. Names and slugs only.POST/supply/{seller_organization_id}/payouts· scope marketplace:writeRecord PayoutRule 3: the seller RECORDS what it paid a supplier by its own hand; no transfer is made here.
Publishers - २३८ doors - publishers:read, publishers:write
Manuscripts to finished books, and the hand-off that turns a book into a course.
PATCH/publishers/audiobook-chapters/{ab_chapter_id}· scope publishers:writeUpdate Ab ChapterPOST/publishers/audiobook-chapters/{ab_chapter_id}/generate-narration· scope publishers:writeGenerate NarrationReal AI narration for one audiobook chapter, cloned from a real consented voice sample - the actual mechanical gate that has been missing since this pillar's voice-consent capture was built: an ACTIVE consent record belonging to the SAME house, and a real captured sample of that same consented voice, checked here before any generation is ever triggered.POST/publishers/audiobook-chapters/{ab_chapter_id}/recording· scope publishers:writeUpload RecordingGET/publishers/audiobook-chapters/{ab_chapter_id}/recording· scope publishers:readDownload RecordingPATCH/publishers/audiobook-projects/{project_id}· scope publishers:writeUpdate Audiobook ProjectPOST/publishers/audiobook-projects/{project_id}/assemble-m4b· scope publishers:writeAssemble M4BReal, complete-book M4B assembly - every chapter's own real recording (human-uploaded or AI-generated, mixed formats/codecs are fine, ffmpeg decodes and re-encodes each one) concatenated in real position order into one genuine M4B with real embedded, correctly-titled chapter markers (ffmpeg's own FFMETADATA1 mechanism, timestamps computed from each real chapter's own measured duration, never estim…GET/publishers/audiobook-projects/{project_id}/assembled-m4b· scope publishers:readDownload Assembled M4BGET/publishers/audiobook-projects/{project_id}/chapters· scope publishers:readList Ab ChaptersPOST/publishers/audiobook-projects/{project_id}/chapters· scope publishers:writeAdd Ab ChapterPATCH/publishers/authors/{author_id}· scope publishers:writeUpdate AuthorDELETE/publishers/authors/{author_id}· scope publishers:writeDelete AuthorPATCH/publishers/authors/{author_id}/archive· scope publishers:writeArchive AuthorGET/publishers/authors/{author_id}/can-delete· scope publishers:readCan Delete AuthorPOST/publishers/authors/{author_id}/photo· scope publishers:writeUpload Author PhotoAccepts an uploaded photo OR a direct camera capture (the frontend's file input uses the `capture` attribute so phones/tablets open their camera app directly, matching the Emperor's "upload, link to phone, tablet, or camera" request - both paths arrive here as a normal multipart file). Every photo is run through enhance_image() before storage - the Emperor's explicit instruction that all images in…GET/publishers/authors/{author_id}/photo· scope publishers:readGet Author PhotoPOST/publishers/authors/{author_id}/polish-bio· scope publishers:writePolish Author BioAI copy-edit assistance for the author bio (the Emperor's explicit ask - everything else in the pipeline gets AI drafting/polish by default, but the bio and back-cover blurb, both human-typed text fields, had none). Same "AI drafts, never authoritative" shape as writing_voice_profile's own draft endpoint: takes whatever the caller currently has in the field (saved or not), never touches the stored…PATCH/publishers/authors/{author_id}/restore· scope publishers:writeRestore AuthorGET/publishers/books/{book_id}· scope publishers:readGet BookPATCH/publishers/books/{book_id}· scope publishers:writeUpdate BookDELETE/publishers/books/{book_id}· scope publishers:writeDelete BookPATCH/publishers/books/{book_id}/archive· scope publishers:writeArchive BookGET/publishers/books/{book_id}/audiobook-project· scope publishers:readGet Audiobook ProjectPOST/publishers/books/{book_id}/audiobook-project· scope publishers:writeCreate Audiobook ProjectGET/publishers/books/{book_id}/can-delete· scope publishers:readCan Delete BookGET/publishers/books/{book_id}/cross-links· scope publishers:readGet Book Cross LinksRow 11 Phase 4's real "concrete cross-promotion hooks" - the exact same real, live-computed resolution already proven and used inside the composed back-cover image (publishers_barcode.py's own three resolve_*_cta_url functions), now also exposed as a plain JSON endpoint so the book detail page can render real, visible, clickable links - not just a QR code baked into a downloadable PNG. Deliberatel…GET/publishers/books/{book_id}/editions· scope publishers:readList EditionsPOST/publishers/books/{book_id}/editions· scope publishers:writeCreate EditionUNIQUE(book_id, format, language, interior_color_mode) is a real DB constraint enforcing "one format + one language + one interior color = one edition identity" - extended from the spec's original (format, language)-only rule so a house can create BOTH a black-and-white and a color paperback/hardcover/large_print of the same book as two real, separate editions, each able to carry its own real ISBN…POST/publishers/books/{book_id}/lccn-request· scope publishers:writeCreate Lccn RequestGET/publishers/books/{book_id}/lccn-worksheet· scope publishers:readGet Lccn WorksheetGET/publishers/books/{book_id}/plan· scope publishers:readBook PlanPOST/publishers/books/{book_id}/plan/declare· scope publishers:writeDeclare Book PlanThe author's declaration, at will. Re-declaring ADDS missing cells and never doubles or deletes what stands - removal is its own deliberate door.PATCH/publishers/books/{book_id}/restore· scope publishers:writeRestore BookPOST/publishers/books/{book_id}/royalty-splits· scope publishers:writeAdd SplitGET/publishers/books/{book_id}/royalty-splits· scope publishers:readList SplitsDELETE/publishers/chapter-images/{image_id}· scope publishers:writeDelete Chapter ImageGET/publishers/chapter-images/{image_id}/file· scope publishers:readGet Chapter Image FilePOST/publishers/chapter-versions/{version_id}/restore· scope publishers:writeRestore Chapter VersionReal restore-to-a-prior-chapter-version - mirrors restore_manuscript_ version's own "always auto-snapshot the CURRENT state first" discipline (so the restore is itself reversible), then applies the target version's own stored title/content back onto the real chapter row.PATCH/publishers/chapters/{chapter_id}· scope publishers:writeUpdate ChapterDELETE/publishers/chapters/{chapter_id}· scope publishers:writeDelete ChapterPOST/publishers/chapters/{chapter_id}/compute-sage· scope publishers:writeCompute Sage In ChapterReal Sage compute-and-freeze (task #156). Scans `body.content` - the CHAPTER EDITOR'S CURRENT, possibly-unsaved content, not necessarily what's stored in the DB yet, matching the AI polish endpoints' own "operate on whatever the author is currently editing" contract - for every [[sage:expr]] marker, runs each one through a real, genuinely sandboxed Sage container (sage_sandbox.run_sage_code, see t…POST/publishers/chapters/{chapter_id}/draft-code· scope publishers:writeDraft CodeReal AI-drafted Python/Sage code from a plain-English description (task #159/160), meant for insertion into a real Jupyter cell - the author still reviews it and must explicitly click Run before anything executes in the sandbox, matching task #157's own "AI drafts, human triggers execution" boundary exactly. Never executed here - this endpoint only ever returns text.POST/publishers/chapters/{chapter_id}/draft-latex· scope publishers:writeDraft LatexReal AI-drafted LaTeX from a plain-English description (task #159/160) - closes the one real gap flagged once MathLive/SageMath/ Jupyter shipped with zero AI layer. The author still edits it live in MathLive and must explicitly insert/save it - this endpoint only ever returns text, it never touches the database. Gated on real house membership for this specific chapter, same as every other chapter-…GET/publishers/chapters/{chapter_id}/images· scope publishers:readList Chapter ImagesPOST/publishers/chapters/{chapter_id}/images· scope publishers:writeUpload Chapter ImageA real uploaded illustration a chapter can reference inline via [[image:ID]] (see publishers_content_blocks.py) - rendered as a real embedded image in both EPUB and print PDF, not just stored. Run through enhance_image() before storage, same as every other image upload in this pillar.POST/publishers/chapters/{chapter_id}/jupyter/execute· scope publishers:writeExecute Jupyter CellRuns one real cell against an already-started session's live kernel - a later cell genuinely sees state an earlier cell in the SAME session defined, the whole point of task #157 over #156's one-shot primitive. Real ownership is enforced inside jupyter_sandbox.execute_in_session itself (a session belongs to the user who started it), independent of this chapter-level check.POST/publishers/chapters/{chapter_id}/jupyter/start· scope publishers:writeStart Jupyter SessionStarts one real, isolated, long-lived Jupyter kernel container (task #157 - see jupyter_sandbox.py's own module docstring for the full real isolation posture and the real Kernel Gateway investigation behind this design). Opportunistically sweeps any of THIS user's other sessions that have gone idle past the real timeout, so starting a fresh session is also the natural moment stale ones get reaped.POST/publishers/chapters/{chapter_id}/jupyter/stop· scope publishers:writeStop Jupyter SessionExplicitly, immediately tears down the real container - the honest, author-triggered counterpart to the idle-timeout sweep, so a session an author is done with doesn't sit consuming real memory/CPU until the timeout eventually catches it.POST/publishers/chapters/{chapter_id}/polish· scope publishers:writeRun PolishAnalyzes the chapter and REPLACES its still-pending suggestions with the fresh run's results (applied/dismissed history is never touched). Suggestions whose excerpt is not verbatim-findable in the plain text are dropped at creation - the model quoting loosely is a model problem, never the author's.GET/publishers/chapters/{chapter_id}/polish-suggestions· scope publishers:readList SuggestionsGET/publishers/chapters/{chapter_id}/versions· scope publishers:readList Chapter VersionsPOST/publishers/chapters/{chapter_id}/versions· scope publishers:writeSave Chapter VersionReal per-chapter version snapshot (row 9) - the same real, reviewable revision-history discipline as save_manuscript_version below, scoped to just this one chapter so restoring a single scene never touches any other chapter's own, separately-made edits since.PATCH/publishers/comments/{comment_id}· scope publishers:writeUpdate CommentPATCH/publishers/competencies/{competency_id}· scope publishers:writeReview CompetencyPATCH/publishers/copyright-deposit-alerts/{alert_id}/acknowledge· scope publishers:writeAcknowledge Copyright Deposit AlertPATCH/publishers/course-conversion/{item_id}· scope publishers:writeReview Course ConversionDELETE/publishers/course-conversion/{item_id}· scope publishers:writeDiscard Course ConversionReal discard support - a 'proposed'/'reviewed'/'rejected' item that never became real Education content can be permanently removed. A 'pushed' item has real downstream course/module/lesson rows and real audit history behind it, so it must never be deleted - the 'rejected' status (a lighter 'mark bad, keep visible' alternative, matching manuscript_competency's own canon_state pattern) is the right …POST/publishers/course-conversion/{item_id}/push· scope publishers:writePush Course ConversionBook-to-Course bridge, step 2: creates REAL Education course/module/ lesson rows (migration 024's tables) from an approved mapping. This deliberately reuses Education's own RLS INSERT policies as the real authorization check instead of reimplementing school-staff logic here - a publisher with no admin/teacher role at the target school gets a genuine RLS rejection, not a fake permission check. One-…PATCH/publishers/distribution-records/{record_id}· scope publishers:writeUpdate Distribution RecordPATCH/publishers/editions/{edition_id}· scope publishers:writeUpdate EditionDELETE/publishers/editions/{edition_id}· scope publishers:writeDelete EditionPATCH/publishers/editions/{edition_id}/archive· scope publishers:writeArchive EditionGET/publishers/editions/{edition_id}/auto-back-cover· scope publishers:readGenerate Auto Back CoverBack-cover analog of generate_auto_cover above - same real caching fix, same manual-upload-always-wins precedence via the persisted metadata_record.back_cover_image_storage_path.GET/publishers/editions/{edition_id}/auto-cover· scope publishers:readGenerate Auto CoverReal front-cover art, now genuinely CACHED (Phase C of the audit-fix pass, closing the "regenerated randomly on every GET" finding) - the first successful generation for this edition is persisted to metadata_record exactly like a manual upload, so a repeated GET returns the same real image every time instead of a fresh random one. Use POST .../regenerate-cover to deliberately force a genuinely new…POST/publishers/editions/{edition_id}/back-cover· scope publishers:writeUpload Back CoverManual back-cover override - always takes priority over the auto-composed one, same rule as the front cover. Enhanced before storage like every other image upload in this pillar.GET/publishers/editions/{edition_id}/back-cover· scope publishers:readGet Back CoverGET/publishers/editions/{edition_id}/can-delete· scope publishers:readCan Delete EditionPOST/publishers/editions/{edition_id}/cover· scope publishers:writeUpload CoverGET/publishers/editions/{edition_id}/cover· scope publishers:readGet CoverGET/publishers/editions/{edition_id}/distribution-records· scope publishers:readList Distribution RecordsPOST/publishers/editions/{edition_id}/distribution-records· scope publishers:writeCreate Distribution RecordA record tracks manual submission status per platform - it never calls a real KDP/Ingram API. Real auto-publishing to external marketplaces means real seller credentials and putting content up for real public sale, which this platform's spec keeps locked until explicit sign-off and legal review, per this project's standing rule.GET/publishers/editions/{edition_id}/epub· scope publishers:readGenerate EpubGET/publishers/editions/{edition_id}/isbn-assignment· scope publishers:readGet Isbn AssignmentPOST/publishers/editions/{edition_id}/isbn-assignment· scope publishers:writeAssign IsbnGET/publishers/editions/{edition_id}/marketplace-listing· scope publishers:readGet Marketplace ListingPOST/publishers/editions/{edition_id}/marketplace-listing· scope publishers:writeList Edition On MarketplaceOne real action: lists a finished Publishers edition as a real Marketplace product with the actual real EPUB/M4B as its digital- delivery file. Auto-provisions a real Medusa vendor identity for the publisher house's own organization on first use (get_or_provision_vendor) - a house never has to separately sign up as a Marketplace vendor first. Re-listing an already-listed edition UPDATES the same r…GET/publishers/editions/{edition_id}/metadata· scope publishers:readGet MetadataPUT/publishers/editions/{edition_id}/metadata· scope publishers:writeUpsert MetadataGET/publishers/editions/{edition_id}/pod-asset· scope publishers:readGet Pod AssetPOST/publishers/editions/{edition_id}/pod-asset· scope publishers:writeCreate Pod AssetGET/publishers/editions/{edition_id}/pod-print-jobs· scope publishers:readList Pod Print JobsPOST/publishers/editions/{edition_id}/pod-print-jobs· scope publishers:writeCreate Pod Print JobPOST/publishers/editions/{edition_id}/polish-blurb· scope publishers:writePolish BlurbAI copy-edit assistance for the back-cover sales blurb (the same real gap the bio-polish endpoint closes above) - covers/illustrations already get AI drafting by default in this pillar, but the human-typed blurb text had none. Same "AI drafts, never authoritative" shape: the caller sends whatever's currently in the description field (saved or not), the stored metadata_record.description is never t…GET/publishers/editions/{edition_id}/print-pdf· scope publishers:readGenerate Print PdfPOST/publishers/editions/{edition_id}/regenerate-back-cover· scope publishers:writeRegenerate Auto Back CoverForces a genuinely fresh auto-composed back cover, same deliberate- override contract as regenerate_auto_cover above.POST/publishers/editions/{edition_id}/regenerate-cover· scope publishers:writeRegenerate Auto CoverForces a genuinely fresh AI-generated cover, deliberately overwriting whatever is currently cached (or even a prior manual upload - an explicit Regenerate click is a real, intentional override, matching how upload_cover already overwrites a prior generated one). Old cached file deleted first via the same helper's own cleanup, same discipline as every other image-replacement endpoint in this pillar…PATCH/publishers/editions/{edition_id}/restore· scope publishers:writeRestore EditionGET/publishers/editions/{edition_id}/submission-package· scope publishers:readGenerate Submission PackageBundles the EPUB, print PDF, cover (manual if uploaded, else the real auto-generated one), and a metadata JSON into one zip - ready for a human to manually upload to Amazon KDP/IngramSpark/etc. Deliberately NOT an API auto-publish integration - see the Distribution Center's own docstring for why real auto-publishing stays out of Core.GET/publishers/editions/{edition_id}/wraparound-cover· scope publishers:readGenerate Wraparound CoverA real, single continuous print-ready wraparound cover (back cover | spine | front cover) at this edition's own real per-format trim size (matching the actual print-interior PDF's own real trim for its format - paperback 5.5x8.5in, hardcover/large_print 6x9in) + bleed + 300 DPI. Requires the print-interior PDF to have been generated at least once already, since the spine width is computed from the…POST/publishers/figure-briefs/{brief_id}/status· scope publishers:writeSet Figure StatusPOST/publishers/houses· scope publishers:writeCreate HouseGET/publishers/houses/mine· scope publishers:readGet My House EndpointPATCH/publishers/houses/{house_id}· scope publishers:writeUpdate HouseReal, minimal house settings update - currently just country_code, which real ISBN registration agencies key off of (a publisher must apply to the agency that operates where they're based, not any agency they choose - see the ISBN system's own real administration rule).GET/publishers/houses/{house_id}/allocations· scope publishers:readList AllocationsPUT/publishers/houses/{house_id}/allocations· scope publishers:writeSet AllocationAny real house member can set another member's allocation cap - matches this pillar's own established collapsed-role convention (any member can act; owner-role override only where the spec actually names a distinct authority). The platform OWNER role's own spending is never subject to any cap regardless of what's recorded here for them.PATCH/publishers/houses/{house_id}/archive· scope publishers:writeArchive HouseNo DELETE endpoint exists for a house at all - see this module's own docstring for why (every real table in this pillar cascades from publisher_house_id, so a hard delete could never be meaningfully blocked - archive is the only safe path).GET/publishers/houses/{house_id}/authors· scope publishers:readList AuthorsPOST/publishers/houses/{house_id}/authors· scope publishers:writeCreate AuthorAuthor registry with synthetic data (spec section 4 item 3): an author profile is real data owned by a real logged-in Fitinty user, but nothing requires legal-identity verification. link_my_account attaches the caller's own user_id (the common "I am the author" case); leaving it false creates a placeholder co-author/pen-name entry with no linked account, which the spec's own "synthetic data" frami…GET/publishers/houses/{house_id}/books· scope publishers:readList BooksPOST/publishers/houses/{house_id}/books· scope publishers:writeCreate BookA book can only be created from a manuscript already at status='approved' (spec section 17's pipeline starts at "approved manuscript") - checked here, not just implied by UI ordering.POST/publishers/houses/{house_id}/cart/total· scope publishers:writeCompute Cart TotalPOST/publishers/houses/{house_id}/checkout· scope publishers:writeCheckout CartSpends the house's real fuel pool for a real cart. Enforces (a) sufficient house balance, (b) the caller's own real per-user allocation cap, if one has been set for them. The platform OWNER role is always exempt from any allocation cap (the roadmap's own 'Owner exemption') - everyone else's cap, if they have one, is enforced honestly against their own recorded real spend.GET/publishers/houses/{house_id}/copyright-deposit-alerts· scope publishers:readList Copyright Deposit AlertsGET/publishers/houses/{house_id}/fuel-account· scope publishers:readGet Fuel AccountGet-or-create the house's real fuel pool. A house starts at 0 credits until it subscribes to a tier - there is no free grant here, unlike a person's own Pioneer grant in Tools, since a house choosing not to subscribe is a real, valid state.GET/publishers/houses/{house_id}/fuel-ledger· scope publishers:readGet Fuel LedgerGET/publishers/houses/{house_id}/imprints· scope publishers:readList ImprintsPOST/publishers/houses/{house_id}/imprints· scope publishers:writeCreate ImprintGET/publishers/houses/{house_id}/isbn-alert-rules· scope publishers:readList Isbn Alert RulesPOST/publishers/houses/{house_id}/isbn-alert-rules· scope publishers:writeCreate Isbn Alert RuleGET/publishers/houses/{house_id}/isbn-alerts· scope publishers:readList Isbn AlertsGET/publishers/houses/{house_id}/isbn-inventory· scope publishers:readList Isbn InventoryPOST/publishers/houses/{house_id}/isbn-inventory· scope publishers:writeCreate Isbn InventoryPOST/publishers/houses/{house_id}/isbn-inventory/import-real· scope publishers:writeImport Real IsbnsImports real ISBN values the author already bought directly from a real agency (never fabricated, never resold by Fitinty - see migration 177's own comment for why). Every value is validated for real ISBN-13 shape/check-digit correctness and deduped against every ISBN already assigned or pooled anywhere on the platform before landing in a fresh real_import batch.GET/publishers/houses/{house_id}/lccn-requests· scope publishers:readList Lccn RequestsPOST/publishers/houses/{house_id}/logo· scope publishers:writeUpload House LogoReal publisher/imprint logo upload - previously zero logo/branding anywhere in the book pipeline. Same enhance_image() + local-disk pattern as upload_author_photo above, scoped to house-membership rather than author-house lookup.GET/publishers/houses/{house_id}/logo· scope publishers:readGet House LogoGET/publishers/houses/{house_id}/manuscripts· scope publishers:readList ManuscriptsPOST/publishers/houses/{house_id}/manuscripts· scope publishers:writeCreate ManuscriptPOST/publishers/houses/{house_id}/manuscripts/generate· scope publishers:writeGenerate ManuscriptProduce a manuscript from ideas via AI (spec section 1). The AI drafts a title/synopsis/chapter outline from the author's own notes - it never invents unrelated plot beyond what the notes support - and the result lands at status='idea', the earliest lifecycle stage, so nothing is treated as final until a human edits it through the same review-gated workflow every manuscript goes through.POST/publishers/houses/{house_id}/manuscripts/import· scope publishers:writeImport ManuscriptManuscript import (spec section 1: "Manuscript creation and import"). Parses .txt/.md/.docx/.epub into real chapters, never just stores the raw file - the author lands straight in a usable editor. genre/series_bible_id (Phase F of the audit-fix pass) match what the blank-create path (create_manuscript's own ManuscriptIn) already accepts - previously an import required an immediate follow-up PATCH…GET/publishers/houses/{house_id}/members· scope publishers:readList House MembersGET/publishers/houses/{house_id}/provenance· scope publishers:readList ProvenancePOST/publishers/houses/{house_id}/provenance· scope publishers:writeCreate ProvenancePATCH/publishers/houses/{house_id}/restore· scope publishers:writeRestore HouseGET/publishers/houses/{house_id}/rights-records· scope publishers:readList Rights RecordsPOST/publishers/houses/{house_id}/rights-records· scope publishers:writeCreate Rights RecordPOST/publishers/houses/{house_id}/royalty-statements· scope publishers:writeGenerate StatementDraft = regenerated wholesale on every run. Final = refused (history).GET/publishers/houses/{house_id}/royalty-statements· scope publishers:readList StatementsGET/publishers/houses/{house_id}/search· scope publishers:readSearchReal Postgres full-text search (tsvector/GIN), ranked, across manuscripts, chapters, Series/World Bibles, books, authors, and citations - not a placeholder LIKE query. Permission is applied before ranking: RLS already scopes every table to house members, and this additionally filters by house_id explicitly. book/author_profile/reference_entry (Phase F of the audit-fix pass, migration 189) close t…GET/publishers/houses/{house_id}/series-bibles· scope publishers:readList Series BiblesPOST/publishers/houses/{house_id}/series-bibles· scope publishers:writeCreate Series BiblePOST/publishers/houses/{house_id}/subscribe· scope publishers:writeSubscribeSubscribes the house to a real tier and grants its bundled credits immediately - a real, honest SANDBOX grant regardless of bank_payments.is_live_enabled()'s state. Never charges the house's real payment method (none is even wired here).GET/publishers/houses/{house_id}/voice-consent-records· scope publishers:readList Consent RecordsPOST/publishers/houses/{house_id}/voice-consent-records· scope publishers:writeCreate Consent RecordReal consent capture (spec section 18) - typed full legal name + the exact statement agreed to, timestamped, plus an optional uploaded signed document for genuine written authorization. This is the gate itself, built ahead of any actual voice-synthesis engine: nothing in this platform reads this table to authorize synthesis yet, because that engine doesn't exist - this makes the consent mechanism …GET/publishers/houses/{house_id}/writing-voice-profiles· scope publishers:readList Voice ProfilesPOST/publishers/houses/{house_id}/writing-voice-profiles· scope publishers:writeCreate Voice ProfileDELETE/publishers/imprints/{imprint_id}· scope publishers:writeDelete ImprintPATCH/publishers/imprints/{imprint_id}/archive· scope publishers:writeArchive ImprintGET/publishers/imprints/{imprint_id}/can-delete· scope publishers:readCan Delete ImprintPATCH/publishers/imprints/{imprint_id}/restore· scope publishers:writeRestore ImprintGET/publishers/integrations· scope publishers:readList IntegrationsHonest status per spec section 7's candidate list - reports what's genuinely wired into this codebase vs. what's a named candidate still needing its own license/security/sandbox review, same discipline as every other pillar's integration registry (Games, Marketplace, Education).GET/publishers/isbn-agencies· scope publishers:readList Isbn AgenciesPOST/publishers/isbn-agencies· scope publishers:writeCreate Isbn AgencyPATCH/publishers/isbn-agencies/{agency_id}· scope publishers:writeUpdate Isbn AgencyPOST/publishers/isbn-agencies/{agency_id}/tiers· scope publishers:writeCreate Isbn Agency TierPATCH/publishers/isbn-agency-tiers/{tier_id}· scope publishers:writeUpdate Isbn Agency TierPATCH/publishers/isbn-alert-rules/{rule_id}· scope publishers:writeUpdate Isbn Alert RuleReal edit-after-creation for an alert rule - previously only ever creatable, never editable (a house wanting a different threshold, or to pause/resume the rule without deleting its history, had no way to do either).POST/publishers/isbn-alerts/{alert_id}/acknowledge· scope publishers:writeAcknowledge Isbn AlertPATCH/publishers/lccn-requests/{request_id}· scope publishers:writeUpdate Lccn RequestGET/publishers/length-standards· scope publishers:readLength StandardsPOST/publishers/manuscript-versions/{version_id}/restore· scope publishers:writeRestore Manuscript VersionReal restore-to-a-prior-version. First snapshots the CURRENT chapter state as a brand new version (so the restore is itself reversible - see _snapshot_manuscript_version above), then applies the target version's own stored per-chapter title/content/position back onto the real `chapter` rows, matched by each chapter's own real id. Deliberately conservative, not a destructive full rollback: a chapt…GET/publishers/manuscripts/{manuscript_id}· scope publishers:readGet ManuscriptPATCH/publishers/manuscripts/{manuscript_id}· scope publishers:writeUpdate ManuscriptDELETE/publishers/manuscripts/{manuscript_id}· scope publishers:writeDelete ManuscriptPATCH/publishers/manuscripts/{manuscript_id}/archive· scope publishers:writeArchive ManuscriptPOST/publishers/manuscripts/{manuscript_id}/book-type· scope publishers:writeSet Book TypePOST/publishers/manuscripts/{manuscript_id}/build· scope publishers:writeStart BuildQueue the long-form build. Refuses without a book type (no band, no honest target) and refuses to double-run.GET/publishers/manuscripts/{manuscript_id}/build-jobs· scope publishers:readBuild JobsGET/publishers/manuscripts/{manuscript_id}/can-delete· scope publishers:readCan Delete ManuscriptGET/publishers/manuscripts/{manuscript_id}/chapters· scope publishers:readList ChaptersPOST/publishers/manuscripts/{manuscript_id}/chapters· scope publishers:writeCreate ChapterPOST/publishers/manuscripts/{manuscript_id}/chapters/reorder· scope publishers:writeReorder ChaptersReal chapter reordering (row 9) - the corkboard view's own drag-drop action. body.chapter_ids must be EXACTLY the manuscript's current real chapter id set (same count, same ids, any order) before anything is applied - a partial or stale list (e.g. a chapter deleted by someone else mid-drag) is rejected outright rather than silently reordering only some chapters and leaving the rest at ambiguous po…GET/publishers/manuscripts/{manuscript_id}/comments· scope publishers:readList CommentsPOST/publishers/manuscripts/{manuscript_id}/comments· scope publishers:writeCreate CommentGET/publishers/manuscripts/{manuscript_id}/competencies· scope publishers:readList CompetenciesRLS alone scopes this to houses the caller is actually a member of - the same reliance list_references already places on RLS for this exact class of manuscript-scoped child resource.POST/publishers/manuscripts/{manuscript_id}/completeness-check· scope publishers:writeRun Completeness CheckReal AI completeness pass (row 9, item 5) - reads the manuscript against its OWN stated scope (title/genre/synopsis) and flags real gaps a human should look at. A draft suggestion for the author to act on, never an automatic edit - this never writes to chapter/manuscript content itself, only returns findings.POST/publishers/manuscripts/{manuscript_id}/course-conversion· scope publishers:writePropose Course ConversionBook-to-Course bridge, step 1 (spec section 19): AI proposes a chapter -> module -> lesson -> learning-objective mapping. Nothing is written to Education yet - this only creates a reviewable course_conversion_item, matching "Global Push must create a reviewable change set, not overwrite approved courses automatically".GET/publishers/manuscripts/{manuscript_id}/course-conversion· scope publishers:readList Course ConversionsPOST/publishers/manuscripts/{manuscript_id}/edit-pass· scope publishers:writeStart Edit PassOne stage of the rail as a queued pass. Suggestions ride the SAME polish table and doors a person's own polish run uses - never a parallel pile; the pass files a real review_cycle row so the manuscript's approval gate sees the work.POST/publishers/manuscripts/{manuscript_id}/extract-competencies· scope publishers:writeExtract CompetenciesReal AI-assisted extraction (task #162) - grounded strictly in the manuscript's own real chapter text, never invented, mirroring Games' already-proven extracted_entity pattern. Every extracted item lands at canon_state='proposed' - nothing is ever treated as real/actionable until a human explicitly approves it via the review endpoint below. This is the extraction/foundation half of the standing "d…GET/publishers/manuscripts/{manuscript_id}/figure-briefs· scope publishers:readFigure BriefsGET/publishers/manuscripts/{manuscript_id}/references· scope publishers:readList ReferencesTask #124's real reference library - an author's bibliography entries for this manuscript, orderable by whichever ones are actually cited via [[cite:key]] (see collect_citation_order in publishers_content_blocks.py). No explicit access check here, same as list_chapters - reference_entry's own RLS policy already scopes this to real house members.POST/publishers/manuscripts/{manuscript_id}/references· scope publishers:writeCreate ReferencePATCH/publishers/manuscripts/{manuscript_id}/restore· scope publishers:writeRestore ManuscriptGET/publishers/manuscripts/{manuscript_id}/review-cycles· scope publishers:readList Review CyclesPOST/publishers/manuscripts/{manuscript_id}/review-cycles· scope publishers:writeCreate Review CycleGET/publishers/manuscripts/{manuscript_id}/thoroughness· scope publishers:readThoroughnessModel-free: real words against the type's band, the verdict in WORDS. A manuscript with no type gets its counts and a sentence, never a made-up target.GET/publishers/manuscripts/{manuscript_id}/translation-siblings· scope publishers:readList Translation SiblingsEvery real manuscript in this one's translation family - the original plus every other real materialized translated sibling - so a reader/editor can navigate to any language version from any other one. Resolves through the real original regardless of which family member manuscript_id points at (a sibling's own translated_from_manuscript_id, or its own id if it IS the original). Returns an empty li…POST/publishers/manuscripts/{manuscript_id}/translations· scope publishers:writeCreate TranslationGET/publishers/manuscripts/{manuscript_id}/translations· scope publishers:readList TranslationsPOST/publishers/manuscripts/{manuscript_id}/translations/bulk· scope publishers:writeCreate Translations BulkMulti-locale translation in one request - an author picks several target languages from Fitinty's own LANGUAGES registry instead of requesting one locale at a time. Reuses create_translation's own real per-locale insert/dedup logic (the same ON CONFLICT DO NOTHING check, so a locale that already has a translation is reported, never silently duplicated or re-queued) rather than a separate mechanism…GET/publishers/manuscripts/{manuscript_id}/versions· scope publishers:readList Manuscript VersionsPOST/publishers/manuscripts/{manuscript_id}/versions· scope publishers:writeSave Manuscript VersionSnapshots every current chapter's title/content/position into one manuscript_version row - real, reviewable revision history without a per-chapter version table.GET/publishers/narrator-voice-options· scope publishers:readList Narrator Voice OptionsDELETE/publishers/plan-items/{item_id}· scope publishers:writeRemove ItemOnly a still-'planned' promise may be withdrawn - in-progress and kept ones are history.POST/publishers/plan-items/{item_id}/link· scope publishers:writeLink ItemA kept promise names its proof - the human hand links what reconciliation cannot prove from tables (movies, games, trailers, campaigns).POST/publishers/plan-items/{item_id}/status· scope publishers:writeSet Item StatusPATCH/publishers/pod-assets/{pod_id}· scope publishers:writeUpdate Pod AssetPOST/publishers/pod-print-jobs/{job_id}/refresh· scope publishers:writeRefresh Pod Print JobRe-polls the provider for the job's current real status - stored verbatim, never normalized into locally-invented states.GET/publishers/pod-print/artifacts/{edition_id}/{kind}· scope publishers:readServe Signed ArtifactPublic (no-session) artifact route for the POD providers' own file fetchers. Access is not anonymous in the RLS sense: the HMAC-signed URL embeds the submitting user's id, and the artifact is assembled under that exact user's normal RLS context - the URL grants precisely the access its creator already had, time-limited, for these two artifacts only.GET/publishers/pod-print/providers· scope publishers:readProvider StatusPOST/publishers/polish-suggestions/{suggestion_id}/apply· scope publishers:writeApply SuggestionPOST/publishers/polish-suggestions/{suggestion_id}/dismiss· scope publishers:writeDismiss SuggestionPATCH/publishers/references/{reference_id}· scope publishers:writeUpdate ReferenceDELETE/publishers/references/{reference_id}· scope publishers:writeDelete ReferencePATCH/publishers/review-cycles/{review_id}· scope publishers:writeUpdate Review CyclePATCH/publishers/rights-records/{record_id}· scope publishers:writeUpdate Rights RecordPATCH/publishers/rights-records/{record_id}/copyright-details· scope publishers:writeUpdate Copyright DetailsA real, human-entered record of the author's own progress through the Copyright Office's eCO portal - registration_number/registration_filed_at once they receive their real certificate back, first_published_at as the real legal anchor for the mandatory-deposit deadline, deposit_method/ deposit_completed_at once they've actually deposited a copy. Never fabricates, files, or submits anything on the …GET/publishers/rights-records/{record_id}/document· scope publishers:readDownload Rights DocumentPOST/publishers/royalty-lines/{line_id}/record-payment· scope publishers:writeRecord Line PaymentThe handshake record: the house paid the author by its own hand (bank, cash, cheque) and writes down how. Nothing here moves money - Rule 3 is the wall.DELETE/publishers/royalty-splits/{split_id}· scope publishers:writeRemove SplitGET/publishers/royalty-statements/{statement_id}· scope publishers:readStatement DetailDELETE/publishers/royalty-statements/{statement_id}· scope publishers:writeVoid StatementThe escape from a trapped period: a DRAFT voids freely (it was a reading), and a FINAL statement voids ONLY when it holds nothing - no lines, so nothing was ever owed on it. A final statement with lines is history and never deletes.POST/publishers/royalty-statements/{statement_id}/finalize· scope publishers:writeFinalize StatementPOST/publishers/series-bible-versions/{version_id}/restore· scope publishers:writeRestore Series Bible VersionReal restore-to-a-prior-version - the actual point of keeping version history at all, not just a read-only log. Restoring itself FIRST snapshots the current (about-to-be-overwritten) state as a brand new version, so a restore is itself reversible - the same "always snapshot before mutating" discipline update_series_bible already uses, applied one level up. The target snapshot's own immutable/manag…GET/publishers/series-bibles/{bible_id}· scope publishers:readGet Series BiblePATCH/publishers/series-bibles/{bible_id}· scope publishers:writeUpdate Series BibleEvery save snapshots the full prior state into series_bible_version before applying the update - real "version control" (spec section 10) without a per-field diff/merge engine. Snapshots the state BEFORE the change so version N always reads as "what it looked like going into change N+1".DELETE/publishers/series-bibles/{bible_id}· scope publishers:writeDelete Series BiblePATCH/publishers/series-bibles/{bible_id}/archive· scope publishers:writeArchive Series BibleGET/publishers/series-bibles/{bible_id}/books· scope publishers:readList Series BooksGET/publishers/series-bibles/{bible_id}/can-delete· scope publishers:readCan Delete Series BiblePOST/publishers/series-bibles/{bible_id}/plan/declare· scope publishers:writeDeclare Series PlanDeclared once at the series, MATERIALIZED onto every book in it - each volume carries and fulfills its own copy; a book that joins the series later gets the plan by running this again (adds only what's missing, per book).POST/publishers/series-bibles/{bible_id}/program-conversion· scope publishers:writePropose Series ProgramSeries -> Education Program bridge, step 1. Real per-book chapter/ module/lesson mapping stays entirely on the existing, unmodified propose_course_conversion/review_course_conversion/push_course_conversion flow - this only creates the program-level shell (title/description drafted from the series bible's own real fields, grounded, never invented) and, for every not-yet-linked book in the series, a…GET/publishers/series-bibles/{bible_id}/program-conversions· scope publishers:readList Series Program ConversionsPATCH/publishers/series-bibles/{bible_id}/restore· scope publishers:writeRestore Series BibleGET/publishers/series-bibles/{bible_id}/versions· scope publishers:readList Series Bible VersionsPATCH/publishers/series-program-conversions/{item_id}· scope publishers:writeReview Series ProgramDELETE/publishers/series-program-conversions/{item_id}· scope publishers:writeDiscard Series ProgramSame real discard rule as course_conversion_item above - a 'pushed' series program has a real education_program with real linked courses behind it, so it can never be deleted here.POST/publishers/series-program-conversions/{item_id}/push· scope publishers:writePush Series ProgramSeries -> Education Program bridge, step 2: creates the REAL education_program shell (now that a target school is known) and links every book's course_conversion_item to it via program_id. Each course itself still goes through the existing, unmodified per-book review_course_conversion/push_course_conversion flow - THAT is what actually creates the real course/module/lesson rows and, thanks to the …GET/publishers/service-catalog· scope publishers:readList Service CatalogPOST/publishers/service-catalog· scope publishers:writeCreate Service Catalog ItemGET/publishers/subscription-tiers· scope publishers:readList Subscription TiersPOST/publishers/subscription-tiers· scope publishers:writeCreate Subscription TierDELETE/publishers/translations/{translation_id}· scope publishers:writeDelete TranslationGET/publishers/translations/{translation_id}/chapters· scope publishers:readList Translated ChaptersPOST/publishers/translations/{translation_id}/create-manuscript· scope publishers:writeCreate Manuscript From TranslationMaterializes a real, human-reviewed translation into a genuine, independent `manuscript` row - a real sibling of the original, not just translated text sitting in manuscript_translation - so it flows through the exact same real editorial-review pipeline, book/edition creation, and EPUB/print-PDF generation every other manuscript already uses (see the translation-notice wiring in publishers_product…PATCH/publishers/translations/{translation_id}/mark-reviewed· scope publishers:writeMark ReviewedA human explicitly confirming the MT draft has been reviewed - never automatic, matching this project's standing 'AI drafts, never authoritative' pattern. Now requires a real, completed review_cycle approval first (task #123) - the same "real gate, not a cosmetic button" discipline already used for the manuscript's own production-status gate (publishers_manuscripts.py's GATED_STATUS check) - a tra…GET/publishers/translations/{translation_id}/review-cycles· scope publishers:readList Translation Review CyclesPOST/publishers/translations/{translation_id}/review-cycles· scope publishers:writeCreate Translation Review CycleRequests a real, structured editorial review against a translation DRAFT - the same review_cycle machinery a manuscript uses, reused rather than duplicated (see migration 173). Only valid once the real MT draft actually exists (status='draft_ready' or later - requesting a review against a still-generating or failed translation makes no sense, since there's nothing real yet to review).GET/publishers/voice-calibration-scripts· scope publishers:readList Calibration ScriptsGET/publishers/voice-consent-records/{record_id}/document· scope publishers:readDownload Consent DocumentPATCH/publishers/voice-consent-records/{record_id}/revoke· scope publishers:writeRevoke Consent RecordGET/publishers/voice-consent-records/{record_id}/samples· scope publishers:readList Voice SamplesPOST/publishers/voice-consent-records/{record_id}/samples· scope publishers:writeUpload Voice SampleA real microphone recording of the person reading a calibration passage - captured with the browser's own mic permission prompt, never silently. Can ONLY be attached to a consent record that is still ACTIVE (checked here, not just assumed from the UI) - the actual mechanical enforcement of "no voice clone without consent," since nothing downstream can ever have a sample without a live consent reco…POST/publishers/voice-consent-records/{record_id}/train-voice-lora· scope publishers:writeTrain Voice LoraReal per-voice LoRA training (VoxCPM2) against every real captured sample for this consent record - the exact same mechanism already proven end-to-end for Cinema, now available to Publishers' own real, previously- unused consent samples. Blocks synchronously while training runs (real GPU time, can take several minutes) - matching cinema_identity.py's own train_voice_lora precedent for this exact e…DELETE/publishers/voice-samples/{sample_id}· scope publishers:writeDelete Voice SampleGET/publishers/voice-samples/{sample_id}/file· scope publishers:readGet Voice Sample FilePATCH/publishers/writing-voice-profiles/{profile_id}· scope publishers:writeUpdate Voice ProfilePOST/publishers/writing-voice-profiles/{profile_id}/draft· scope publishers:writeDraft With VoiceAI drafting assistance (spec section 11): labeled suggestion, never saved automatically, never overwrites existing text - the caller pastes the returned draft into the manuscript editor themselves.
Workspace - ४२५ doors - workspace:read, workspace:write
The tools every tenant works in - documents, mail, calendar, forms - and the ones sold direct to customers.
GET/bi/dashboards· scope workspace:readList DashboardsPOST/bi/dashboards· scope workspace:writeCreate DashboardGET/bi/dashboards/{dashboard_id}· scope workspace:readGet DashboardPATCH/bi/dashboards/{dashboard_id}· scope workspace:writeUpdate DashboardDELETE/bi/dashboards/{dashboard_id}· scope workspace:writeDelete DashboardGET/bi/dashboards/{dashboard_id}/values· scope workspace:readGet Dashboard ValuesPOST/bi/dashboards/{dashboard_id}/widgets· scope workspace:writeCreate WidgetGET/bi/dashboards/{dashboard_id}/widgets· scope workspace:readList WidgetsGET/bi/metrics· scope workspace:readList MetricsGET/bi/my-organizations· scope workspace:readMy OrganizationsPATCH/bi/widgets/{widget_id}· scope workspace:writeUpdate WidgetDELETE/bi/widgets/{widget_id}· scope workspace:writeDelete WidgetGET/bookkeeping/bills· scope workspace:readList BillsPOST/bookkeeping/bills· scope workspace:writeCreate BillDELETE/bookkeeping/bills/{bill_id}· scope workspace:writeDelete BillPOST/bookkeeping/bills/{bill_id}/status· scope workspace:writeUpdate Bill StatusGET/bookkeeping/invoices· scope workspace:readList InvoicesPOST/bookkeeping/invoices· scope workspace:writeCreate InvoiceDELETE/bookkeeping/invoices/{invoice_id}· scope workspace:writeDelete InvoicePOST/bookkeeping/invoices/{invoice_id}/status· scope workspace:writeUpdate Invoice StatusGET/bookkeeping/my-organizations· scope workspace:readMy OrganizationsGET/bookkeeping/summary/{organization_id}· scope workspace:readSummaryGET/brief/{organization_id}/latest· scope workspace:readLatest BriefPOST/brief/{organization_id}/write· scope workspace:writeWrite BriefPOST/community/announcements· scope workspace:writeCreate AnnouncementGET/community/announcements· scope workspace:readMy AnnouncementsPOST/community/announcements/{announcement_id}/publish· scope workspace:writePublish AnnouncementPOST/community/clubs· scope workspace:writeCreate ClubPOST/community/clubs/{club_id}/join· scope workspace:writeJoin ClubAny signed-in person may join - the open-room design. The RLS WITH CHECK (you join AS YOURSELF) raises, so the polite sentences come first. FM10 (618): a share_room opens only to a holder of its plan, a growers_exchange only to someone on a farm's own membership - both asked on the caller's cursor, both refused in words.DELETE/community/clubs/{club_id}/join· scope workspace:writeLeave ClubPOST/community/clubs/{club_id}/posts· scope workspace:writePost To ClubMembers write as themselves. The membership sentence comes BEFORE the insert - the RLS WITH CHECK raises, never empty-returns (the EC1 lesson).POST/community/posts/{post_id}/hide· scope workspace:writeHide PostThe moderator's hand: hides with a REASON, never erases.GET/content-calendar/entries· scope workspace:readList EntriesPOST/content-calendar/entries· scope workspace:writeCreate EntryPATCH/content-calendar/entries/{entry_id}· scope workspace:writeUpdate EntryDELETE/content-calendar/entries/{entry_id}· scope workspace:writeDelete EntryPOST/content-calendar/entries/{entry_id}/draft· scope workspace:writeDraft EntryPOST/content-calendar/entries/{entry_id}/mark-posted· scope workspace:writeMark PostedThe tenant records that THEY posted this manually. The platform never posts for them - that is the standing real-communications gate, and it holds here.POST/content-calendar/entries/{entry_id}/status· scope workspace:writeSet StatusMoves an entry between planning states. 'posted' is deliberately NOT accepted here - it only exists via /mark-posted, which records the tenant's own manual action.POST/content-calendar/plan-month· scope workspace:writePlan MonthFills a month with idea entries from a plain brief. Ideas land as 'idea' status - the tenant curates; nothing is drafted, let alone posted, on its own.GET/content-calendar/shipped· scope workspace:readShippedReal dated output from the org's other pillars - published blog posts, simulated newsletter sends, published podcast episodes - so the plan sits next to reality.POST/content-translations· scope workspace:writeDraft TranslationA machine draft into one locale. It waits for a person before any visitor can see it.GET/content-translations/pending-review· scope workspace:readPending ReviewEvery machine draft this person may approve, the SOURCE beside each - a reviewer checks a translation against something, and it has to be on the same screen.GET/content-translations/subjects· scope workspace:readMy SubjectsEverything the person's businesses show on their storefronts, with the translations each already has - the list a tenant translates FROM.PUT/content-translations/{kind}/{subject_id}/{locale}· scope workspace:writeWrite TranslationA person's own translation: written = approved. Writes over a machine draft if one stands.DELETE/content-translations/{translation_id}· scope workspace:writeDelete TranslationPATCH/content-translations/{translation_id}/mark-reviewed· scope workspace:writeMark ReviewedA person approving ONE machine draft - the only way a draft ever reaches a visitor.POST/document-assist/compare· scope workspace:writeCreate CompareGET/document-assist/documents· scope workspace:readList DocumentsPOST/document-assist/documents· scope workspace:writeUpload DocumentDELETE/document-assist/documents/{document_id}· scope workspace:writeDelete DocumentPOST/document-assist/draft· scope workspace:writeCreate DraftPOST/document-assist/extract· scope workspace:writeCreate ExtractGET/document-assist/jobs· scope workspace:readList JobsGET/document-assist/jobs/{job_id}/download· scope workspace:readDownload JobPOST/document-assist/minutes· scope workspace:writeCreate MinutesPOST/document-assist/proofread· scope workspace:writeCreate ProofreadGET/document-assist/qa· scope workspace:readQa HistoryPOST/document-assist/qa· scope workspace:writeAskGET/domain-monitor· scope workspace:readList Watched DomainsPOST/domain-monitor· scope workspace:writeAdd Watched DomainGET/domain-monitor/my-organizations· scope workspace:readMy OrganizationsDELETE/domain-monitor/{watch_id}· scope workspace:writeRemove Watched DomainPOST/domain-monitor/{watch_id}/check· scope workspace:writeCheck Watched DomainDELETE/field-inspection/items/{item_id}· scope workspace:writeDelete Template ItemGET/field-inspection/my-organizations· scope workspace:readMy OrganizationsGET/field-inspection/runs· scope workspace:readList RunsPOST/field-inspection/runs· scope workspace:writeCreate RunGET/field-inspection/runs/{run_id}· scope workspace:readGet RunDELETE/field-inspection/runs/{run_id}· scope workspace:writeDelete RunPOST/field-inspection/runs/{run_id}/complete· scope workspace:writeComplete RunGET/field-inspection/runs/{run_id}/responses· scope workspace:readList ResponsesPOST/field-inspection/runs/{run_id}/responses· scope workspace:writeRecord ResponseGET/field-inspection/runs/{run_id}/summary· scope workspace:readRun SummaryGET/field-inspection/templates· scope workspace:readList TemplatesPOST/field-inspection/templates· scope workspace:writeCreate TemplateGET/field-inspection/templates/{template_id}· scope workspace:readGet TemplateDELETE/field-inspection/templates/{template_id}· scope workspace:writeDelete TemplatePOST/field-inspection/templates/{template_id}/items· scope workspace:writeAdd Template ItemGET/fitinchat/analytics· scope workspace:readAnalyticsComputed from real rows, nothing sampled or estimated: AI-resolved = closed without a human ever claiming it; escalated = an escalation reason was recorded.GET/fitinchat/channels· scope workspace:readList ChannelsPOST/fitinchat/channels· scope workspace:writeCreate ChannelPATCH/fitinchat/channels/{channel_id}· scope workspace:writeUpdate ChannelGET/fitinchat/conversations· scope workspace:readList ConversationsPOST/fitinchat/conversations/{conversation_id}/claim· scope workspace:writeClaim ConversationPOST/fitinchat/conversations/{conversation_id}/close· scope workspace:writeClose ConversationGET/fitinchat/conversations/{conversation_id}/messages· scope workspace:readList Conversation MessagesPOST/fitinchat/conversations/{conversation_id}/reply· scope workspace:writeStaff ReplyPOST/fitinchat/kb-reindex· scope workspace:writeKb ReindexGET/fitinchat/my-organizations· scope workspace:readMy OrganizationsGET/fitinchat/site/{slug}/channel· scope workspace:readStorefront ChannelThe storefront's way in: the org's 'Storefront' channel, created lazily the first time a live storefront asks. Live-theme gated like every public window. Landing here means every visitor chat is a REAL fitinchat conversation the tenant's team sees, counts in 'unanswered messages', and can take over - the AI is the first responder, never a separate machine.GET/fitinchat/widget/conversations/{visitor_token}· scope workspace:readWidget Conversation StatusGET/fitinchat/widget/conversations/{visitor_token}/messages· scope workspace:readWidget List MessagesPOST/fitinchat/widget/conversations/{visitor_token}/messages· scope workspace:writeWidget Send MessagePOST/fitinchat/widget/conversations/{visitor_token}/order-lookup· scope workspace:writeWidget Order LookupThe widget's explicit "Look up my order" form. Same trust model as the AI action (fitinchat_intelligence.lookup_order): order number + email must both match the REAL Medusa order and the order must contain this shop's products; the visible result is a public-safe summary posted into the conversation as an assistant message (so staff who later claim the chat see exactly what the visitor saw). Not-f…POST/fitinchat/widget/conversations/{visitor_token}/rate· scope workspace:writeWidget RatePost-conversation satisfaction, 1-5, only once and only after close - the widget shows the prompt when the conversation ends.POST/fitinchat/widget/conversations/{visitor_token}/request-human· scope workspace:writeWidget Request HumanGET/fitinchat/widget/{channel_id}· scope workspace:readWidget Channel ViewPOST/fitinchat/widget/{channel_id}/start· scope workspace:writeStart ConversationGET/fitincrm/book/{organization_id}· scope workspace:readRead BookThe one book. Segments are READERS over recorded touches, never stored lists - the same person appears in 'regular' and 'consented' at once because segments are views, not silos.GET/fitincrm/book/{organization_id}/advice· scope workspace:readSecond Sale AdviceCALL THESE FIVE TODAY. Deterministic rules over the recorded book - every line carries its evidence in words and a proposed_action, and NO MODEL writes any of it (the same contract as analytics_advice: advice is a reader, drafting is the only place AI touches).GET/fitincrm/book/{organization_id}/contacts/{contact_id}· scope workspace:readRead Contact PagePOST/fitincrm/book/{organization_id}/contacts/{contact_id}/consent· scope workspace:writeRecord ConsentAppends to the consent ledger. The ORIGIN is required in words - 'she checked the box', 'he asked at the counter today' - because a yes nobody can place is a yes nobody can defend.POST/fitincrm/book/{organization_id}/contacts/{contact_id}/draft-winback· scope workspace:writeDraft WinbackAI DRAFTS, NEVER SENDS. The draft is grounded in this contact's real recorded moments and the org's identity-pack voice, and it is returned to the human to send by their own hand - nothing here transmits, so the mail rail's gate is not even in the path. CONSENT BY CONSTRUCTION: a marketing message to someone with no recorded 'granted' row is refused before any model runs - the machine cannot draf…GET/fitincrm/contacts· scope workspace:readList ContactsPOST/fitincrm/contacts· scope workspace:writeCreate ContactDELETE/fitincrm/contacts/{contact_id}· scope workspace:writeDelete ContactGET/fitincrm/deals· scope workspace:readList DealsPOST/fitincrm/deals· scope workspace:writeCreate DealPATCH/fitincrm/deals/{deal_id}· scope workspace:writeUpdate DealGET/fitincrm/deals/{deal_id}/activities· scope workspace:readList ActivitiesPOST/fitincrm/deals/{deal_id}/activities· scope workspace:writeCreate ActivityPOST/fitincrm/deals/{deal_id}/stage· scope workspace:writeMove Deal StageGET/fitincrm/my-organizations· scope workspace:readMy OrganizationsGET/fitincrm/pipeline-summary/{organization_id}· scope workspace:readPipeline SummaryGET/fitinevents/events· scope workspace:readList EventsPOST/fitinevents/events· scope workspace:writeCreate EventGET/fitinevents/events/{event_id}· scope workspace:readGet EventPATCH/fitinevents/events/{event_id}· scope workspace:writeUpdate EventDELETE/fitinevents/events/{event_id}· scope workspace:writeDelete EventPOST/fitinevents/events/{event_id}/cancel· scope workspace:writeCancel EventPOST/fitinevents/events/{event_id}/open-to-public· scope workspace:writeOpen Event To PublicMints the event's public slug. Publishing already made it real; this makes it REACHABLE - the door strangers register and pay through.POST/fitinevents/events/{event_id}/publish· scope workspace:writePublish EventGET/fitinevents/events/{event_id}/registrations· scope workspace:readList RegistrationsPOST/fitinevents/events/{event_id}/registrations· scope workspace:writeRegister AttendeeGET/fitinevents/my-organizations· scope workspace:readMy OrganizationsGET/fitinevents/registrations/mine· scope workspace:readMy SeatsTHE PERSON'S OWN SEATS (2026-09-11, the customer Space's "Elsewhere"): every event they hold a seat at, across every business, with the ticket code that IS their confirmation. A seat names its person only by the attendee's email - `registered_by` is the staff hand or 'public-registration', never the attendee - so ownership is the standing rule (own_identity): read on the system cursor, decided her…POST/fitinevents/registrations/{registration_id}/cancel· scope workspace:writeCancel RegistrationPOST/fitinevents/registrations/{ticket_code}/check-in· scope workspace:writeCheck InGET/fitingive/donations· scope workspace:readList DonationsPOST/fitingive/donations· scope workspace:writeCreate DonationGET/fitingive/donations/mine· scope workspace:readMy GiftsGET/fitingive/donations/mine/{donation_id}/receipt· scope workspace:readMy Gift ReceiptThe giver's own copy of the church's receipt - the SAME renderer, so the two cannot differ. A gift not theirs is 404 (existence is not theirs to learn); a gift with no receipt says why.DELETE/fitingive/donations/{donation_id}· scope workspace:writeDelete DonationGET/fitingive/donations/{donation_id}/receipt· scope workspace:readDonation ReceiptThe printable receipt. Facts from the ledger row alone - amount, date, fund, the church's own name - plus the sentence US-style acknowledgment letters carry. Rendered for the CHURCH to print or save and hand over; nothing is emailed here (the mail rail is closed by ruling until launch).POST/fitingive/donations/{donation_id}/receipt-sent· scope workspace:writeMark Receipt SentGET/fitingive/donors· scope workspace:readList DonorsPOST/fitingive/donors· scope workspace:writeCreate DonorDELETE/fitingive/donors/{donor_id}· scope workspace:writeDelete DonorGET/fitingive/funds· scope workspace:readList FundsPOST/fitingive/funds· scope workspace:writeCreate FundPATCH/fitingive/funds/{fund_id}· scope workspace:writeUpdate FundGET/fitingive/grants· scope workspace:readList GrantsPOST/fitingive/grants· scope workspace:writeCreate GrantDELETE/fitingive/grants/{grant_id}· scope workspace:writeDelete GrantPOST/fitingive/grants/{grant_id}/status· scope workspace:writeMove Grant StatusGET/fitingive/my-organizations· scope workspace:readMy OrganizationsGET/fitingive/summary/{organization_id}· scope workspace:readSummaryGET/fitinkb/articles· scope workspace:readList ArticlesPOST/fitinkb/articles· scope workspace:writeCreate ArticleGET/fitinkb/articles/{article_id}· scope workspace:readGet ArticlePATCH/fitinkb/articles/{article_id}· scope workspace:writeUpdate ArticleDELETE/fitinkb/articles/{article_id}· scope workspace:writeDelete ArticlePOST/fitinkb/articles/{article_id}/publish· scope workspace:writePublish ArticlePOST/fitinkb/articles/{article_id}/unpublish· scope workspace:writeUnpublish ArticleGET/fitinkb/categories· scope workspace:readList CategoriesPOST/fitinkb/categories· scope workspace:writeCreate CategoryPATCH/fitinkb/categories/{category_id}· scope workspace:writeUpdate CategoryDELETE/fitinkb/categories/{category_id}· scope workspace:writeDelete CategoryGET/fitinkb/my-organizations· scope workspace:readMy OrganizationsGET/fitinkb/search· scope workspace:readSearch ArticlesGET/fitinsign· scope workspace:readList DocumentsPOST/fitinsign· scope workspace:writeCreate DocumentGET/fitinsign/my-organizations· scope workspace:readMy OrganizationsGET/fitinsign/sign/{token}· scope workspace:readView Signing LinkPOST/fitinsign/sign/{token}· scope workspace:writeSign DocumentPOST/fitinsign/sign/{token}/decline· scope workspace:writeDecline DocumentGET/fitinsign/{document_id}/download· scope workspace:readDownload Document FileGET/fitinsign/{document_id}/events· scope workspace:readList EventsPOST/fitinsign/{document_id}/send· scope workspace:writeSend DocumentPOST/fitinsign/{document_id}/upload· scope workspace:writeUpload Document FilePOST/fitinsign/{document_id}/void· scope workspace:writeVoid DocumentGET/hiring/applications· scope workspace:readList ApplicationsPOST/hiring/applications/{application_id}/fit-summary· scope workspace:writeFit SummaryWORDS WITH EVIDENCE, never a score, never a decision - automated employment decisions are regulated territory (NYC LL144 and kin) and 'no model writes a decision' is this platform's own law. The summary quotes what the applicant SAID against what the post ASKS, names what is unknown, and the staff member decides. Runs on the staff member's own click (the /open precedent for a synchronous model cal…POST/hiring/applications/{application_id}/hire· scope workspace:writeHire ApplicantThe rail's destination: one move mints ST1's employee record and the sign-in invite. The application keeps the whole story - who applied, when, and what they became.POST/hiring/applications/{application_id}/interview· scope workspace:writeSchedule InterviewPOST/hiring/applications/{application_id}/offer· scope workspace:writeMake OfferThe offer LETTER is a deterministic draft from the owner's own data - no model writes a decision. Delivery is the owner's (the mail rail is gated); the screen says so.POST/hiring/applications/{application_id}/reject· scope workspace:writeReject ApplicationGET/hiring/applications/{application_id}/resume· scope workspace:readRead ResumeGET/hiring/internal-openings· scope workspace:readInternal OpeningsST7: published internal-first posts, visible to the signed-in team only - the promotion path a cashier can actually see.GET/hiring/posts· scope workspace:readList PostsPOST/hiring/posts· scope workspace:writeCreate PostPATCH/hiring/posts/{post_id}· scope workspace:writeUpdate PostPOST/hiring/posts/{post_id}/interview-questions· scope workspace:writeDraft Interview QuestionsQuestion DRAFTS from the post's own requirements, for the human interviewer - stored on the post so the whole team asks from one sheet.GET/hris/employees· scope workspace:readList EmployeesPOST/hris/employees· scope workspace:writeCreate EmployeeGET/hris/employees/{employee_id}· scope workspace:readGet EmployeePATCH/hris/employees/{employee_id}· scope workspace:writeUpdate EmployeeDELETE/hris/employees/{employee_id}· scope workspace:writeDelete EmployeeGET/hris/employees/{employee_id}/direct-reports· scope workspace:readList Direct ReportsPOST/hris/employees/{employee_id}/invite· scope workspace:writeInvite EmployeePOST/hris/employees/{employee_id}/on-leave· scope workspace:writeMark On LeavePOST/hris/employees/{employee_id}/rehire· scope workspace:writeRehire EmployeeEH1 - THE WAY BACK. The SAME employee row returns, so reviews, warnings, documents and transitions stay one unbroken record; the old ending folds into the transition history instead of being erased. Sign-in access does NOT return by itself - it died with the termination, and a fresh invite is the honest way in.POST/hris/employees/{employee_id}/reinstate· scope workspace:writeReinstate EmployeePOST/hris/employees/{employee_id}/terminate· scope workspace:writeTerminate EmployeeGET/hris/locations· scope workspace:readList LocationsPOST/hris/locations· scope workspace:writeCreate LocationPATCH/hris/locations/{location_id}· scope workspace:writeUpdate LocationGET/hris/my-organizations· scope workspace:readMy OrganizationsGET/hris/public/staff-invite/{code}· scope workspace:readRead Staff InviteThe join page's data - org and employee NAME only, never the roster. No auth: the code is unguessable and this reveals exactly what the invitee already knows.POST/hris/staff-invite/{code}/claim· scope workspace:writeClaim Staff InviteSigned in (fresh from signup or not), the code becomes a key: the login links to the employee record and org membership is minted. System cursor - the claimant is by definition not yet a member, so RLS correctly cannot see the rows.POST/id-cards/employees/{employee_id}/card· scope workspace:writeMake CardCompose (or re-compose) the card. Reuses the employee's standing employment verification or mints one - the QR is that verification's public link, so ONE revocation story covers the card and the reference letter alike.GET/id-cards/employees/{employee_id}/card.png· scope workspace:readRead CardPOST/id-cards/employees/{employee_id}/photo· scope workspace:writeSet PhotoGET/network/directory· scope workspace:readDirectoryEvery org with a LIVE storefront that has not stepped out of the directory. Served through the public-read context because every fact here is already on a public page - being signed in is required only because the directory is a tenant-space tool.POST/network/partnerships· scope workspace:writePropose PartnershipGET/network/partnerships· scope workspace:readList PartnershipsBoth directions, with the counterpart named and the referral evidence counted from the click ledger - a real count of recorded rows, never an estimate.POST/network/partnerships/{partnership_id}/connect-supplier· scope workspace:writeConnect SupplierTurns an ACCEPTED supplier partnership into a supplier row the buying org syncs from. New items always land as DRAFTS here (auto_publish=false) - the tenant's existing supplier dial flips that, behind the same publish gate every go-live passes through.POST/network/partnerships/{partnership_id}/decide· scope workspace:writeDecide PartnershipTHE SECOND HAND. Only the RECEIVING org's owner decides - the proposer holding both pens would make the two-hand a fiction.POST/network/partnerships/{partnership_id}/end· scope workspace:writeEnd PartnershipEither side's owner may end an accepted partnership. History stays - the row becomes 'ended', it does not vanish, and a fresh proposal is possible afterwards.POST/network/partnerships/{partnership_id}/storefront· scope workspace:writeToggle StorefrontEach side dials its OWN storefront only - the partner's page is the partner's design.PUT/network/profile/{organization_id}· scope workspace:writeSet ProfilePOST/staff-time/clock· scope workspace:writeClockThe employee's own login toggles their own clock - in if out, out if in.POST/staff-time/employees/{employee_id}/kiosk-pin· scope workspace:writeSet Kiosk PinThe OWNER or a manager sets it and hands it over - never self-set, never readable back (a hash is stored). A duplicate PIN in the same org is refused: the PIN is the identity at the kiosk.POST/staff-time/kiosk-clock· scope workspace:writeKiosk ClockThe store tablet (signed in as any team member) clocks whoever scans their card or, with no card to hand, enters their PIN. The card or the PIN identifies the EMPLOYEE; the signed-in session only proves the device belongs to the org - a stranger's tablet cannot fish for PINs or replay cards. ONE door, ONE writer: both paths resolve an employee and hand it to `_toggle_clock`, the same arithmetic th…GET/staff-time/pto· scope workspace:readList PtoPOST/staff-time/pto· scope workspace:writeRequest PtoPOST/staff-time/pto/{request_id}/decide· scope workspace:writeDecide PtoGET/staff-time/shifts· scope workspace:readList ShiftsPOST/staff-time/shifts· scope workspace:writeCreate ShiftDELETE/staff-time/shifts/{shift_id}· scope workspace:writeDelete ShiftGET/staff-time/timesheet· scope workspace:readTimesheetGET/storefront-blog/audience· scope workspace:readAudienceGET/storefront-blog/issues· scope workspace:readList IssuesPOST/storefront-blog/issues/from-post/{post_id}· scope workspace:writeIssue From PostDrafts an issue from a published post: the post's title as subject, its HTML as the body, plus the mandatory unsubscribe footer every real newsletter carries.POST/storefront-blog/issues/{issue_id}/simulate-send· scope workspace:writeSimulate SendThe gated 'send': records the real active-subscriber count and marks the issue sent_simulated. NO EMAIL LEAVES THE PLATFORM - real dispatch waits for the standing real-communications sign-off, and will slot in behind this same issue model.GET/storefront-blog/posts· scope workspace:readList PostsPOST/storefront-blog/posts· scope workspace:writeCreate PostPATCH/storefront-blog/posts/{post_id}· scope workspace:writeUpdate PostDELETE/storefront-blog/posts/{post_id}· scope workspace:writeDelete PostPOST/storefront-blog/posts/{post_id}/publish· scope workspace:writeToggle PublishPOST/storefront-campaigns/compose· scope workspace:writeCompose CampaignAI-drafts a campaign from a plain brief - subject + simple HTML paragraphs - and saves it as an editable DRAFT issue. The composer never sends anything.PATCH/storefront-campaigns/issues/{issue_id}· scope workspace:writeEdit IssueGET/storefront-campaigns/subscribers· scope workspace:readList SubscribersPOST/storefront-campaigns/subscribers/bulk-add· scope workspace:writeBulk AddAdds emails the tenant already has a relationship with. Honest accounting: invalid addresses and existing subscribers are counted, never silently swallowed.POST/storefront-campaigns/subscribers/{subscriber_id}/remove· scope workspace:writeMember RemoveMember-initiated removal marks unsubscribed rather than deleting - the audit-friendly shape, and the person never silently reappears via a later bulk-add.POST/storefront-campaigns/subscribers/{subscriber_id}/tags· scope workspace:writeSet TagsGET/storefront-links/links· scope workspace:readList LinksPOST/storefront-links/links· scope workspace:writeCreate LinkPATCH/storefront-links/links/{link_id}· scope workspace:writeUpdate LinkDELETE/storefront-links/links/{link_id}· scope workspace:writeDelete LinkGET/storefront-links/short· scope workspace:readList Short LinksPOST/storefront-links/short· scope workspace:writeCreate Short LinkPATCH/storefront-links/short/{code}· scope workspace:writeToggle Short LinkGET/tenant-data/export/{organization_id}/contacts.csv· scope workspace:readExport ContactsGET/tenant-data/export/{organization_id}/manifest· scope workspace:readExport ManifestWhat the org owns, counted from the real tables. Always available - no meeting, no retention call, no waiting period. The counts name what the CSVs carry out.GET/tenant-data/export/{organization_id}/products.csv· scope workspace:readExport ProductsPOST/tenant-data/import· scope workspace:writeStart ImportPOST/tenant-data/import/{job_id}/apply· scope workspace:writeApply ImportTHE SECOND HAND. Writes through the writers that already exist, drafts always, and returns an honest tally - a row that could not land is reported by line, not dropped.POST/tenant-data/import/{job_id}/discard· scope workspace:writeDiscard ImportPATCH/tenant-data/import/{job_id}/mapping· scope workspace:writeEdit MappingThe human hand on the mapping. Every column it touches is re-labelled 'hand' - an AI guess survives only where the owner left it standing.GET/tenant-data/imports· scope workspace:readList ImportsGET/theme· scope workspace:readGet ThemeGET/theme/domains· scope workspace:readList DomainsPOST/theme/domains/claim· scope workspace:writeClaim DomainStart (or restart) ownership verification for a domain. Re-claiming an existing PENDING row keeps its token, so a tenant who already published the TXT record and comes back later does not have to edit their DNS again. A VERIFIED row is returned untouched - re-issuing a token there would spuriously un-verify a working domain.POST/theme/domains/verify· scope workspace:writeVerify DomainPerform the REAL DNS lookup and record the outcome.DELETE/theme/domains/{domain}· scope workspace:writeRemove DomainWithdraw a claim. Routing stops immediately, because both the routing lookup and the certificate gate require a verified row to exist.PATCH/theme/draft· scope workspace:writeUpdate Theme DraftGET/theme/live· scope workspace:readGet Live ThemeDashboard-only auth for now. The response contract is shaped so a future genuinely-public variant only swaps the auth dependency (JWT -> domain-resolved org) -- the body never changes. Returns empty defaults rather than 404 when nothing has been published yet, so a future public renderer degrades gracefully.POST/theme/logo· scope workspace:writeUpload Theme LogoGET/theme/logo· scope workspace:readGet Theme LogoGET/theme/my-organizations· scope workspace:readMy OrganizationsGET/theme/preview· scope workspace:readPreview Theme`page` accepted and currently unused (no public pages exist yet) -- always returns the draft snapshot. Swapping in real page-specific rendering later changes only the response body, not the URL contract callers already use.POST/theme/publish· scope workspace:writePublish ThemeGET/theme/site-analytics· scope workspace:readSite AnalyticsLast-30-days storefront traffic for the org's own members. Aggregated daily counters only -- there is no per-visitor data to show, by design. Human and crawler traffic are reported SEPARATELY (migration 262). `unclassified` is traffic recorded before bot separation existed; it is a genuine mixture, so it is neither hidden nor folded into the human figure, and it ages out of the 30-day window by i…GET/theme/site-funnel· scope workspace:readSite FunnelLast-30-days conversion funnel for this org's storefront. Three real stages, from data that already exists (no new table): page views (human only) -> intent started -> intent claimed HONEST ABOUT WHAT STAGE 1 IS. site_visit_daily counts PAGE VIEWS, not unique visitors - there is deliberately no per-visitor record to count. One person browsing four pages is four views. So the view->inten…GET/theme/site-preview· scope workspace:readSite PreviewThe Theme Editor's real-storefront preview: same payload shape as the public GET /public/site/{slug}, but themed with the org's DRAFT instead of its live version. Authenticated + membership-checked -- drafts never leak to anonymous visitors; the public content blocks are collected through the same public-read cursor the real storefront uses, so preview can't show anything the live site wouldn't.GET/theme/site-preview-detail· scope workspace:readSite Preview DetailPer-PAGE draft preview (STOREFRONT_GAPS "Per-PAGE preview targets"): the storefront's course / service / event / product detail pages, rendered by the editor's iframe with the DRAFT theme even before the site is published. Same membership gate as /site-preview; the detail rows come from the very loaders the public endpoints use, through the same public-read cursor - so a preview can never reveal a…GET/theme/versions· scope workspace:readList Theme VersionsPOST/theme/versions/duplicate· scope workspace:writeDuplicate Theme VersionSaves a named, unpublished copy of an existing version into the library -- does not touch the current draft or the live pointer (rollback covers restores).POST/theme/versions/rollback· scope workspace:writeRollback Theme VersionCopy-forward rollback: creates a NEW version row that becomes live, and resets the draft to match, so the editor never shows a stale draft silently diverging from the just-restored live state. History is never rewritten.GET/translate/audio· scope workspace:readList JobsPOST/translate/audio· scope workspace:writeUpload AudioGET/translate/audio/{job_id}/download· scope workspace:readDownloadPOST/translate/audio/{job_id}/dub· scope workspace:writeCreate DubPOST/translate/audio/{job_id}/dub-consent· scope workspace:writeFile Dub ConsentGET/translate/documents· scope workspace:readList JobsPOST/translate/documents· scope workspace:writeCreate JobGET/translate/documents/{job_id}/download· scope workspace:readDownload ResultGET/translate/images· scope workspace:readList JobsPOST/translate/images· scope workspace:writeUpload ImageGET/translate/images/{job_id}/download· scope workspace:readDownloadGET/wallet-passes/issuances/{issuance_id}· scope workspace:readGet IssuanceGET/wallet-passes/issuances/{issuance_id}/pass-payload· scope workspace:readGet Pass PayloadReal, correctly-structured pass content, honestly labeled unsigned unless a real credential is genuinely configured (checked live, never assumed).POST/wallet-passes/issuances/{issuance_id}/revoke· scope workspace:writeRevoke IssuanceGET/wallet-passes/my-organizations· scope workspace:readMy OrganizationsGET/wallet-passes/signing-status· scope workspace:readSigning StatusReal, live-checked readiness — never a cached/assumed value.GET/wallet-passes/templates· scope workspace:readList TemplatesPOST/wallet-passes/templates· scope workspace:writeCreate TemplateGET/wallet-passes/templates/{template_id}· scope workspace:readGet TemplatePATCH/wallet-passes/templates/{template_id}· scope workspace:writeUpdate TemplateDELETE/wallet-passes/templates/{template_id}· scope workspace:writeDelete TemplateGET/wallet-passes/templates/{template_id}/issuances· scope workspace:readList IssuancesPOST/wallet-passes/templates/{template_id}/issuances· scope workspace:writeCreate IssuancePOST/watch· scope workspace:writeCreate WatchGET/watch· scope workspace:readWatch BoardThe board. Opening it takes at most ONE lawful look per watch per interval; inside the interval the last snapshot is re-read instead of the rival's server. Changes are the diff of the two latest OK snapshots, with the readings as evidence.POST/watch/field-intel· scope workspace:writeAdd Field IntelPATCH/watch/field-intel/{intel_id}· scope workspace:writeCorrect Field IntelThe tenant's hand corrects the AI's reading - and the origin says so.PATCH/watch/{watch_id}· scope workspace:writeUpdate WatchPOST/workspace-documents/approvals/{approval_id}:decide· scope workspace:writeDecide ApprovalPATCH/workspace-documents/collaborators/{collaborator_id}· scope workspace:writePatch CollaboratorDELETE/workspace-documents/collaborators/{collaborator_id}· scope workspace:writeRevoke CollaboratorPOST/workspace-documents/collaborators/{collaborator_id}:respond· scope workspace:writeRespond InviteDELETE/workspace-documents/comments/{comment_id}· scope workspace:writeDelete CommentPOST/workspace-documents/comments/{comment_id}:reopen· scope workspace:writeReopen CommentPOST/workspace-documents/comments/{comment_id}:resolve· scope workspace:writeResolve CommentPOST/workspace-documents/projects/{project_id}/approvals· scope workspace:writeRequest ApprovalPOST/workspace-documents/projects/{project_id}/collaborators· scope workspace:writeAdd CollaboratorGET/workspace-documents/projects/{project_id}/comments· scope workspace:readList CommentsPOST/workspace-documents/projects/{project_id}/comments· scope workspace:writeAdd CommentPOST/workspace-documents/projects/{project_id}/share-links· scope workspace:writeCreate Share LinkGET/workspace-documents/projects/{project_id}/sharing· scope workspace:readGet SharingPATCH/workspace-documents/projects/{project_id}/sharing· scope workspace:writePatch SharingPOST/workspace-documents/projects/{project_id}:restore· scope workspace:writeRestore ProjectPOST/workspace-documents/projects/{project_id}:takedown· scope workspace:writeTakedown ProjectPOST/workspace-documents/reports· scope workspace:writeFile ReportGET/workspace-documents/reports· scope workspace:readList ReportsPATCH/workspace-documents/reports/{report_id}· scope workspace:writePatch ReportDELETE/workspace-documents/share-links/{link_id}· scope workspace:writeRevoke Share LinkGET/workspace-documents/shared-with-me· scope workspace:readShared With MeGET/workspace/audit· scope workspace:readList Audit EventsRelies entirely on audit_events' own RLS policy (migration 009) to scope results to orgs the caller is a member of, or everything for the platform owner role - no additional filtering needed here.GET/workspace/automations· scope workspace:readList AutomationsPOST/workspace/automations· scope workspace:writeCreate AutomationGET/workspace/automations/catalog· scope workspace:readCatalogWhat can be built. Named on the screen so a tenant is choosing from what exists rather than imagining what might.DELETE/workspace/automations/{automation_id}· scope workspace:writeDelete AutomationPATCH/workspace/automations/{automation_id}/active· scope workspace:writeSet ActivePOST/workspace/automations/{automation_id}/test· scope workspace:writeTest AutomationWould this rule have matched? Evaluates the conditions and performs NOTHING. A rule people cannot try before trusting is a rule they will not trust - and the only safe way to offer that is a path that cannot create anything at all.GET/workspace/booking/bookings· scope workspace:readList BookingsThe status filter belongs HERE, not on the screen. The screen showed only confirmed bookings and filtered the list after fetching it. That is harmless while the list is whole and wrong the moment it is a page: the pager would count every booking including cancelled ones and announce "showing 1-25 of 40" above a list of six. A pager that miscounts is worse than no pager - it is a number people tru…POST/workspace/booking/bookings/{booking_id}/cancel· scope workspace:writeCancel BookingGET/workspace/booking/types· scope workspace:readList TypesPOST/workspace/booking/types· scope workspace:writeCreate TypePATCH/workspace/booking/types/{type_id}· scope workspace:writeUpdate TypeDELETE/workspace/booking/types/{type_id}· scope workspace:writeDelete TypePUT/workspace/booking/types/{type_id}/availability· scope workspace:writeSet AvailabilityGET/workspace/brand-kits· scope workspace:readList Brand KitsReal Brand Kit Manager (Workspace tools manifest id 79) - a personal registry of brand guidelines (palette, voice, logo) other AI-drafting tools across the platform can eventually reference, closing a real, previously-nonexistent gap. Never exposes logo_storage_path (a raw filesystem path), matching AuthorProfileOut's own established precedent.POST/workspace/brand-kits· scope workspace:writeCreate Brand KitPATCH/workspace/brand-kits/{kit_id}· scope workspace:writeUpdate Brand KitDELETE/workspace/brand-kits/{kit_id}· scope workspace:writeDelete Brand KitPOST/workspace/brand-kits/{kit_id}/logo· scope workspace:writeUpload Brand Kit LogoReal logo upload, same enhance_image() + local-disk pattern already proven for Publishers' house logo (publishers_foundation.py).GET/workspace/brand-kits/{kit_id}/logo· scope workspace:readGet Brand Kit LogoGET/workspace/calendar/calendars· scope workspace:readList CalendarsPOST/workspace/calendar/calendars· scope workspace:writeCreate CalendarPOST/workspace/calendar/check-conflicts· scope workspace:writeCheck ConflictsWhat would this new event collide with? Advisory - it never refuses the booking. Double-booking is sometimes deliberate, and a calendar that forbids it is one people work around by not using it.GET/workspace/calendar/events· scope workspace:readList EventsPOST/workspace/calendar/events· scope workspace:writeCreate EventPATCH/workspace/calendar/events/{event_id}· scope workspace:writeUpdate EventDELETE/workspace/calendar/events/{event_id}· scope workspace:writeDelete EventPOST/workspace/calendar/events/{event_id}/respond· scope workspace:writeRespondYour answer, and only yours. The policy allows nobody else to write this row.GET/workspace/calendar/free-busy· scope workspace:readFree BusyWhen am I actually busy? Declined invitations and cancelled events are not busy. A conflict check that counts them tells people they are unavailable when they are free, and the first time somebody is told they are busy during an hour they know is empty, they stop believing the calendar.GET/workspace/compliance-audit· scope workspace:readList Compliance Audit EventsSame real audit_events table and the same RLS-only access control as list_audit_events above - the compliance lens is purely a WHERE filter on `action`'s real dotted-domain prefix, no new schema or migration.GET/workspace/contacts· scope workspace:readList ContactsReal personal contacts registry (Workspace tools manifest id 26, FitinContacts) - same personal-by-default scoping as Notes/Task Registry, mirrored field-for-field from workspace_note.POST/workspace/contacts· scope workspace:writeCreate ContactPATCH/workspace/contacts/{contact_id}· scope workspace:writeUpdate ContactDELETE/workspace/contacts/{contact_id}· scope workspace:writeDelete ContactGET/workspace/docker-status· scope workspace:readDocker StatusReal host-level Docker introspection (Workspace tools manifest id 112) - shells out to the real `docker ps` CLI (a fixed, hardcoded command with no interpolated input, matching tools.py's own nvidia-smi subprocess pattern), never fabricated. Local-dev-only infrastructure - this reports on whatever's genuinely running on THIS machine, not a remote fleet.GET/workspace/files· scope workspace:readList FilesPOST/workspace/files· scope workspace:writeUpload FilePOST/workspace/files/ai-draft· scope workspace:writeCreate Ai Drafted DocumentPOST/workspace/files/blank· scope workspace:writeCreate Blank DocumentDELETE/workspace/files/{file_id}· scope workspace:writeDelete FilePOST/workspace/files/{file_id}/analyze-spreadsheet· scope workspace:writeAnalyze SpreadsheetGET/workspace/files/{file_id}/download· scope workspace:readDownload FilePOST/workspace/files/{file_id}/office-callback· scope workspace:writeOffice CallbackCalled directly by the OnlyOffice Document Server, not by a Fitinty session. Must respond {"error": 0} regardless of outcome, per OnlyOffice's own callback contract.GET/workspace/files/{file_id}/office-config· scope workspace:readGet Office ConfigGET/workspace/files/{file_id}/office-content· scope workspace:readGet Office ContentFetched directly by the OnlyOffice Document Server container - no Fitinty session available, so access is gated by a short-lived HMAC-signed token instead.GET/workspace/forms· scope workspace:readList FormsPOST/workspace/forms· scope workspace:writeCreate FormPOST/workspace/forms/purge-expired· scope workspace:writePurge ExpiredEnforces the retention promise. Retention that is only ever displayed is not retention. This is callable by the form owner and is safe to run repeatedly; RLS confines it to that owner's own forms, so it can never reach across tenants even if it is called by the wrong person.GET/workspace/forms/templates/catalog· scope workspace:readTemplate CatalogThe starter shelf - key, title, why, and the question count, never the full definitions (the builder shows those after the mint, where they are editable).POST/workspace/forms/templates/{key}· scope workspace:writeCreate From TemplateMint a DRAFT form from a template. An unknown key is refused - the catalog is the picker, never free text (the silent-zero lesson).GET/workspace/forms/{form_id}· scope workspace:readGet FormPATCH/workspace/forms/{form_id}· scope workspace:writeUpdate FormDELETE/workspace/forms/{form_id}· scope workspace:writeDelete FormPOST/workspace/forms/{form_id}/analyze· scope workspace:writeAnalyze ResponsesGET/workspace/forms/{form_id}/export.csv· scope workspace:readExport CsvPUT/workspace/forms/{form_id}/fields· scope workspace:writeReplace FieldsThe builder saves the whole question list at once. Ids are preserved where the client sends one, because a field id is what an already-submitted answer is filed under. Regenerating them on every save would orphan every response the form has already collected.GET/workspace/forms/{form_id}/responses· scope workspace:readList ResponsesDELETE/workspace/forms/{form_id}/responses/{response_id}· scope workspace:writeDelete ResponseGET/workspace/forms/{form_id}/summary· scope workspace:readForm SummaryGET/workspace/health· scope workspace:readFull HealthEverything, with the addresses and the topology. Owner only — this describes the Forge.GET/workspace/health/service/{key}· scope workspace:readService HealthDeliberately tenant-readable, and deliberately narrow. A tenant is entitled to know why the thing they just clicked is not working — that is the entire point of this wave. What they are NOT given is the address, the host, or anything else about Fitinty's infrastructure: `status`, `label` and `breaks` are the whole payload a screen needs, and the screens ask for exactly the one service they are ab…GET/workspace/home-trends· scope workspace:readHome TrendsThe tenant home's org-level series (AN3) - storefront visitors, subscribers, link clicks, event registrations - from the series spine, beside the pulse rather than inside it so the heartbeat stays fast. A read failure is a sentence, not a missing section.GET/workspace/local-ports· scope workspace:readLocal PortsReal host-level listening-port introspection (Workspace tools manifest id 117) - reuses the exact `psutil` dependency Tools' own hardware telemetry already proved works on this machine, in the same lazy-import-with-graceful-fallback style. Local-dev-only infrastructure, matching Docker Status (id 112) - reports on whatever's genuinely listening on THIS machine right now, not a remote fleet. Proces…GET/workspace/meetings· scope workspace:readList MeetingsPOST/workspace/meetings· scope workspace:writeCreate MeetingGET/workspace/meetings/capacity· scope workspace:readCapacityWhat standing up Fitinty-hosted rooms would actually require. Owner only - these are infrastructure decisions, not tenant settings.GET/workspace/meetings/hosted-status· scope workspace:readRead Hosted StatusReadable by any tenant: somebody who cannot create a Fitinty room deserves to know why, and that it is a platform state rather than something wrong with their account.PATCH/workspace/meetings/{meeting_id}· scope workspace:writeUpdate MeetingDELETE/workspace/meetings/{meeting_id}· scope workspace:writeDelete MeetingGET/workspace/my-tenants· scope workspace:readList My TenantsReal cross-pillar aggregator (Workspace tools manifest id 55, "Tenant Dashboard") - queries each pillar's own real tenant/membership tables directly (see _TENANT_QUERIES) and merges the results. No new schema, no fabricated data - a genuinely empty pillar section here means the caller really has no tenant there.GET/workspace/notes· scope workspace:readList NotesPOST/workspace/notes· scope workspace:writeCreate NotePATCH/workspace/notes/{note_id}· scope workspace:writeUpdate NoteDELETE/workspace/notes/{note_id}· scope workspace:writeDelete NoteGET/workspace/payments· scope workspace:readList PaymentsFitintyPay Control Center (manifest item 89) - a real, owner-only, read-only view of real Stripe TEST-mode transactions, sourced live from Medusa's own admin API (the same integration Marketplace checkout already uses). Deliberately read-only - no payout/refund/capture action exists here. Real money movement stays hard-blocked regardless of the 2026-08-01 "ignore all locks" instruction - that inst…POST/workspace/perfection-reviews· scope workspace:writeCreate ReviewGET/workspace/perfection-reviews· scope workspace:readList ReviewsGET/workspace/perfection-reviews/{review_id}· scope workspace:readGet ReviewPOST/workspace/perfection-reviews/{review_id}/advance· scope workspace:writeAdvance ReviewRun the current Council stage's review (draft/audit/verify) and advance.POST/workspace/perfection-reviews/{review_id}/reject· scope workspace:writeReject ReviewPOST/workspace/perfection-reviews/{review_id}/seal· scope workspace:writeSeal ReviewThe Emperor applies the Imperial Seal - final approval after peer review.GET/workspace/pillar-health· scope workspace:readPillar HealthReal, platform-wide Pillar Health Board (Workspace tools manifest id 126) - distinct from both the Pillar Registry Viewer (build status, no live data) and /my-tenants (personal, per-user). Owner-bypass RLS gives a genuine, unfiltered COUNT(*) per pillar's own real tenant table - the same f"SELECT count(*)..." pattern already proven in kpi.py's own TENANT_TABLES, reused here as a health/activity si…GET/workspace/pulse· scope workspace:readTenant PulseGET/workspace/risk-alerts· scope workspace:readList Risk AlertsReal Risk Alerts (Workspace tools manifest id 10) - distinct from the already-built, static High-Risk Task Viewer (id 52). Joins the AI Agency's own real high-risk build_task ledger against this platform's own per-user acknowledgment table, so a genuinely NEW risk (never acknowledged by this owner session) is visibly distinguishable from one already reviewed - the real proactive-alerting mechanism…POST/workspace/risk-alerts/{task_id}/acknowledge· scope workspace:writeAcknowledge Risk AlertGET/workspace/search· scope workspace:readSearchGET/workspace/search/sources· scope workspace:readList SourcesWhat search covers. Named on the screen so a tenant knows what a result set does and does not include, rather than inferring it from what happened to come back.POST/workspace/songs· scope workspace:writeCreate SongGET/workspace/songs· scope workspace:readList SongsDELETE/workspace/songs/{song_id}· scope workspace:writeDelete SongGET/workspace/songs/{song_id}/audio· scope workspace:readDownload Song AudioGET/workspace/system-health· scope workspace:readSystem HealthReal reachability checks only - no fabricated "Council"/agent status. There's no governed agent registry yet (see Part 02's agent control plane gap in CLAUDE.md), so reporting on AI "council members" here would just be invented data. This checks the services that actually exist: Postgres, Redis, and Medusa.GET/workspace/tasks· scope workspace:readList TasksPOST/workspace/tasks· scope workspace:writeCreate TaskGET/workspace/tasks/all· scope workspace:readList All TasksMaster Task Registry Viewer (manifest item 45) - a genuinely distinct view from the personal Task Registry: every task across every user, owner-only. RLS still scopes the underlying SELECT (a non-owner caller would just see their own tasks), the 403 here is the intended access boundary for this specific cross-user view.PATCH/workspace/tasks/{task_id}· scope workspace:writeUpdate TaskDELETE/workspace/tasks/{task_id}· scope workspace:writeDelete TaskGET/workspace/tenant-brand-voice· scope workspace:readTenant Brand VoiceReal, personal, cross-pillar aggregator (Workspace tools manifest id 65, "Tenant Brand Voice Settings") - the caller's own real voice-profile COUNT(*) across every games/publishers tenant they belong to, no new schema. A tenant genuinely having zero profiles renders as an honest zero, not hidden - the actual add/edit UI stays on each pillar's own real page (href), never duplicated here.GET/workspace/tenant-region-settings· scope workspace:readTenant Region SettingsReal, personal, cross-pillar aggregator (Workspace tools manifest id 66, "Tenant Language / Region Settings") - the caller's own real region/languages/time_zone fields across every farms/manufacturing/ logistics tenant they belong to, no new schema. Editing happens through the pillar's own already-existing PATCH endpoint (patch_href), never a duplicate write path - this endpoint is read-only.POST/workspace/tickets· scope workspace:writeCreate TicketGET/workspace/tickets/all· scope workspace:readList All TicketsOwner-only support inbox - RLS still applies, so a non-owner caller would just get back their own tickets rather than everyone's, but the endpoint itself is intended for owner use.GET/workspace/tickets/mine· scope workspace:readList My TicketsPATCH/workspace/tickets/{ticket_id}· scope workspace:writeUpdate Ticket StatusGET/workspace/tickets/{ticket_id}/replies· scope workspace:readList RepliesPOST/workspace/tickets/{ticket_id}/replies· scope workspace:writeCreate Reply
Services - ५७ doors - services:read, services:write
Bookable work: listings, requests and the calendar behind them.
GET/services/availability· scope services:readList RulesPOST/services/availability· scope services:writeAdd RuleDELETE/services/availability/{rule_id}· scope services:writeDelete RulePOST/services/booking-series/{series_id}/stop· scope services:writeStop SeriesPOST/services/bookings· scope services:writeCreate BookingGET/services/bookings/mine· scope services:readList My BookingsGET/services/bookings/providing· scope services:readList Bookings To FulfillPATCH/services/bookings/{booking_id}· scope services:writeUpdate Booking StatusGET/services/bookings/{booking_id}/deliverables· scope services:readList DeliverablesPOST/services/bookings/{booking_id}/deliverables· scope services:writeUpload DeliverablePOST/services/bookings/{booking_id}/make-recurring· scope services:writeMake RecurringSL3 - the customer turns one booking into a standing order. Completion of each visit mints the NEXT pending booking (event-driven - no daemon; nothing a tenant waits on runs on a timer). Either side can stop the series any time.POST/services/bookings/{booking_id}/pay· scope services:writeStart Booking CheckoutThe customer's pay door on their own booking. Stripe hosts the page; the customer pays the PROVIDER; Fitinty's cut rides Stripe's own split. One purchase per booking - a cancelled checkout leaves a pending row this door REUSES rather than doubles.GET/services/bookings/{booking_id}/payment· scope services:readBooking Payment StatusThree honest answers, kept apart: paid (with the captured moment), pending (a checkout was started and no money moved), or no payment record at all.POST/services/bookings/{booking_id}/satisfaction· scope services:writeSubmit SatisfactionGET/services/bookings/{booking_id}/satisfaction· scope services:readGet SatisfactionGET/services/bookings/{booking_id}/value-report· scope services:readGet Value ReportPOST/services/bookings/{booking_id}/value-report· scope services:writeSave Value ReportPOST/services/clients· scope services:writeCreate ClientGET/services/clients/mine· scope services:readList My ClientsPATCH/services/clients/{client_id}· scope services:writeUpdate ClientDELETE/services/clients/{client_id}· scope services:writeDelete ClientGET/services/corrections/mine· scope services:readList My CorrectionsPATCH/services/corrections/{correction_id}· scope services:writeResolve CorrectionGET/services/deliverables/{deliverable_id}/download· scope services:readDownload DeliverableGET/services/leads· scope services:readList LeadsThe desk: every lead with its DERIVED band and reasons, plus the conversion truth - how many leads became captured money. The numbers competitors cannot have, beside the promise they cannot make.POST/services/leads· scope services:writeAdd Manual LeadThe tenant's own hand - a phone call, a walk-in, a referral heard at church.POST/services/leads/from-form-response· scope services:writeFile Form Response As LeadThe tenant's door from Fitinty Forms: an answer that reads like an enquiry gets FILED to the desk by a human hand - deliberate, never auto-classified. The email is found deterministically (the response's answer to an email-kind question).GET/services/leads/offers· scope services:readList OffersPOST/services/leads/offers/{offer_id}/withdraw· scope services:writeWithdraw OfferPATCH/services/leads/{lead_id}· scope services:writeSet Lead StatusGET/services/listing-kits/mine· scope services:readMy Listing KitThe kit for the provider's own vertical - or an honest 'none for yours yet'.POST/services/listing-kits/mine/mint· scope services:writeMint Listing KitMints the vertical's kit as INACTIVE drafts - building is free, and switching each one live walks through pay-to-open like any hand-built listing. Skips any title the provider already has, so minting twice doubles nothing.GET/services/listings· scope services:readList Active ListingsPOST/services/listings· scope services:writeCreate ListingGET/services/listings/mine· scope services:readList My ListingsPATCH/services/listings/{listing_id}· scope services:writeUpdate ListingPOST/services/listings/{listing_id}/feature· scope services:writeFeature ListingSL3 - featured placement in the public market, Fitinty's own future stream. Until Dodo takes live payments this is the EMPEROR'S COMP HAND (the promoted- listings pattern): owner-only, days counted, and the market shows the label - a featured listing is marked, never smuggled.POST/services/listings/{listing_id}/image· scope services:writeSet Listing ImageA service listing's FACE (LST cross-pillar pass, 2026-08-30): the pillar had no images at all - migration 436 added the column and this gives it a writer. Enhanced through the same pipeline as every platform image; stored on the API's own disk (deliverables' pattern) and served publicly through the storefront's own gated route.GET/services/listings/{listing_id}/packages· scope services:readList PackagesPOST/services/listings/{listing_id}/packages· scope services:writeCreate PackageGET/services/packages/mine· scope services:readList My PackagesPATCH/services/packages/{package_id}· scope services:writeUpdate PackagePOST/services/providers· scope services:writeCreate ProviderGET/services/providers/mine· scope services:readGet My Provider EndpointPATCH/services/providers/{provider_id}· scope services:writeUpdate ProviderGET/services/providers/{slug}/listings· scope services:readGet Provider ListingsPOST/services/quotes· scope services:writeCreate QuoteThe provider's estimate, born a DRAFT. It must answer something real - a request or a lead of this organization - so a quote can never be conjured at a stranger.GET/services/quotes· scope services:readList QuotesPOST/services/quotes/accept/{token}· scope services:writeAccept QuoteSIGNED-IN acceptance (the claim posture). The job is born on the standing booking object with NO TIME YET - trades schedule after acceptance, and 'not scheduled yet' is stated, never invented.POST/services/quotes/{quote_id}/send· scope services:writeSend QuoteMarks the quote SENT and hands back the accept link. Sending the link stays the provider's hand - chat, a reply, on paper; mail drafts wait for the rail.POST/services/quotes/{quote_id}/withdraw· scope services:writeWithdraw QuotePOST/services/requests· scope services:writeCreate RequestGET/services/requests/incoming· scope services:readList Incoming RequestsGET/services/requests/mine· scope services:readList My RequestsPATCH/services/requests/{request_id}· scope services:writeUpdate Request StatusGET/services/verticals· scope services:readList VerticalsReal, curated vertical + field-template catalog - Vertical Business Operations v1 (Connected Apps cost-elimination audit, 2026-08-12). The frontend already mirrors this in lib/vertical-fields.ts for zero-latency rendering; this endpoint exists so any other real client (a future mobile app, a script) has one authoritative source, and so the backend's own validation logic below is provably reading t…POST/services/webhook· scope services:writeServices Stripe WebhookThe pillar's own verified door, education's shape exactly: signature or refusal, captured events only, idempotent by the pending->paid guard + the fee ledger's unique payment id.
Ministries - ४६ doors - ministries:read, ministries:write
Congregations: members, giving, live events and the people who run them.
PUT/give-payments/page· scope ministries:writeUpsert PageCreate or update this organization's giving page. Defaults to INACTIVE. A giving link that goes live the moment a row exists is a link that goes live before anybody has read the wording on it - the same rule booking pages already follow.GET/ministries/archive/search· scope ministries:readSearch ArchiveGET/ministries/churches· scope ministries:readList ChurchesPOST/ministries/churches· scope ministries:writeCreate ChurchGET/ministries/churches/mine· scope ministries:readGet My Church EndpointGET/ministries/churches/{church_id}/live-events· scope ministries:readList EventsGET/ministries/churches/{church_id}/members· scope ministries:readList Church MembersPOST/ministries/churches/{church_id}/members· scope ministries:writeAdd Church MemberGET/ministries/cohorts· scope ministries:readList CohortsPOST/ministries/cohorts· scope ministries:writeCreate CohortGET/ministries/congregation/care-requests· scope ministries:readList Care RequestsA congregation's pastors, leaders and admins see every request that came to THEIR congregation; anyone else sees only what the requester let the congregation see. The leaders_only wall is enforced HERE by real role, not by the screen.PATCH/ministries/congregation/care-requests/{request_id}· scope ministries:writeSet Care StatusTending a request is pastoral work: only those who tend care for the congregation it came to may move it, and a refusal returns nothing of the request - not its words, not its asker.POST/ministries/congregation/funds/{fund_id}/share· scope ministries:writeMake Fund ShareableMints the fund's public campaign slug - the link travels beyond the congregation; every gift through it still lands on the church's own account at 0%, designated to this fund.GET/ministries/congregation/groups· scope ministries:readList GroupsEvery group the person can see, or one congregation's when the desk names it - a person in two congregations must not see one church's choir on the other's desk.POST/ministries/congregation/groups· scope ministries:writeCreate GroupGET/ministries/congregation/groups/{group_id}/members· scope ministries:readList Group MembersWho is in the group, leaders first - the roster a leader serves.POST/ministries/congregation/groups/{group_id}/members· scope ministries:writeAdd Group MemberAdding someone already in the group changes their role - the one door for both.DELETE/ministries/congregation/groups/{group_id}/members/{membership_id}· scope ministries:writeRemove Group MemberLeaving a group removes the person from THAT group only - they stay in the congregation.GET/ministries/congregation/mine· scope ministries:readMy CongregationsGET/ministries/congregation/newcomers· scope ministries:readList NewcomersThe shepherd's desk: everyone who reached toward the church, with the honest measure beside it - who returned, who connected, who became a member. Exclusive to this congregation; never resold, never pooled.POST/ministries/congregation/newcomers· scope ministries:writeAdd NewcomerPATCH/ministries/congregation/newcomers/{newcomer_id}· scope ministries:writeUpdate NewcomerGET/ministries/events· scope ministries:readList EventsPOST/ministries/events· scope ministries:writeCreate EventGET/ministries/guardrails· scope ministries:readList GuardrailsPOST/ministries/guardrails· scope ministries:writePropose GuardrailAI may propose content but cannot publish doctrinal material automatically (spec section 7.3) - every guardrail lands as 'draft' regardless of who creates it; a separate approve call moves it forward.POST/ministries/guardrails/{guardrail_id}/approve· scope ministries:writeApprove GuardrailGET/ministries/integrations· scope ministries:readList IntegrationsPOST/ministries/live-events· scope ministries:writeSchedule EventGET/ministries/live-events/{event_id}· scope ministries:readGet EventGET/ministries/live-events/{event_id}/attendance· scope ministries:readEvent AttendanceAttendance audit metrics (church staff). The 'engagement heatmap' is the per-attendee join_count + presence window.POST/ministries/live-events/{event_id}/end· scope ministries:writeEnd EventPOST/ministries/live-events/{event_id}/join· scope ministries:writeJoin EventRecord attendance and return the room. Any church member may join.POST/ministries/live-events/{event_id}/leave· scope ministries:writeLeave EventPOST/ministries/live-events/{event_id}/start· scope ministries:writeStart EventGET/ministries/pastor-workspace· scope ministries:readPastor WorkspaceGET/ministries/sermons· scope ministries:readList SermonsPOST/ministries/sermons· scope ministries:writeCreate SermonGET/ministries/sermons/{sermon_id}· scope ministries:readGet SermonGET/ministries/sermons/{sermon_id}/download· scope ministries:readDownload SermonPOST/ministries/sermons/{sermon_id}/multiplier· scope ministries:writeSermon MultiplierAI-assisted "sermon multiplier" (spec section 9) - drafts a summary, discussion questions, and a Bible-study outline from the sermon's own transcript only, never invented content. Labeled draft, never auto-published (section 9: "No automatic public publishing"; section 7.4: "Fitinty shall not claim theological authority").PATCH/ministries/sermons/{sermon_id}/status· scope ministries:writeSet Sermon StatusPATCH/ministries/sermons/{sermon_id}/transcript· scope ministries:writeSet TranscriptReal transcription (Faster-Whisper per spec section 22) is a bigger infrastructure decision not made yet - this is the manual-entry shell the spec's own build sequence (N08) calls "Transcription shell" before a real engine is wired in, matching this project's honest-scoping pattern for AI/media infra it hasn't built yet (e.g. Publishers' audiobook narration).GET/ministries/studies· scope ministries:readList StudiesPOST/ministries/studies· scope ministries:writeCreate StudyPATCH/ministries/studies/{lesson_id}/status· scope ministries:writeSet Study Status
Media - ५५३ doors - media:read, media:write
Productions and published media, and the rights that travel with them.
GET/design-canvas/assets· scope media:readList AssetsPOST/design-canvas/assets/generate· scope media:writeGenerate AssetReal FLUX.1-dev generation on the local GPU via ComfyUI - reuses the shared flux_image_engine. Slow by nature (a real diffusion render); the frontend calls this with a long timeout.POST/design-canvas/assets/upload· scope media:writeUpload AssetGET/design-canvas/assets/{asset_id}/file· scope media:readAsset FileGET/design-canvas/designs· scope media:readList DesignsPOST/design-canvas/designs· scope media:writeCreate DesignGET/design-canvas/designs/{design_id}· scope media:readGet DesignPATCH/design-canvas/designs/{design_id}· scope media:writeSave DesignDELETE/design-canvas/designs/{design_id}· scope media:writeDelete DesignPOST/design-canvas/designs/{design_id}/duplicate· scope media:writeDuplicate DesignGET/design-canvas/templates· scope media:readList TemplatesThe shelf - key, name and size only; the layout ships when one is minted.POST/design-canvas/templates/{key}· scope media:writeMint From TemplateMints an ordinary DRAFT design from the shelf - from here on it is any other design: the editor owns it, nothing auto-publishes anywhere.GET/identity/organizations/{org}/pack· scope media:readGet PackPUT/identity/organizations/{org}/pack· scope media:writePut PackPOST/identity/song/{project_id}:apply· scope media:writeApply To SongPOST/identity/song/{project_id}:check· scope media:writeCheck SongPOST/identity/video/{project_id}:apply· scope media:writeApply To VideoA new brief version (source 'identity_pack') with the shot grammar and voice merged in. Nothing renders; existing shots are not rewritten - the check names them instead.POST/identity/video/{project_id}:check· scope media:writeCheck VideoPOST/media-payments/licenses· scope media:writeCreate LicenseThe tenant sells usage rights to an episode's audio: the TERMS live in words on the row, the pay link travels like a sponsorship, and the file is delivered only on captured money. The offer IS the pending purchase - one table, one rail.GET/media-payments/licenses· scope media:readList LicensesGET/media-payments/purchases/mine· scope media:readMy MediaTHE LISTENER'S OWN PURCHASES (2026-09-11, the customer Space's "Elsewhere"): premium episodes, show memberships, licences, sponsorships and tips a person paid for, across every show. A purchase names its person by `buyer_user_id` when they were signed in, and otherwise by the email they paid with - the standing ownership rule (own_identity), decided here per row on the system cursor. PAID only: a …POST/media-payments/sponsorships· scope media:writeCreate SponsorshipThe offer IS the pending purchase row - one table, no parallel invoice system. The tenant sends the returned link; nothing is emailed for them (no mail rail).GET/media-payments/sponsorships· scope media:readList SponsorshipsThe tenant's sponsorship desk: every offer with its state and its link.POST/media-payments/sponsorships/{purchase_id}/void· scope media:writeVoid SponsorshipWithdrawing an offer nobody paid. A PAID sponsorship is history, never voidable - money that moved stays on the books.POST/media-payments/webhook· scope media:writeMedia Stripe WebhookThe pillar's own verified door, education's shape exactly: signature or refusal, captured events only.GET/media/assets· scope media:readList AssetsPOST/media/assets· scope media:writeUpload AssetGET/media/assets/{asset_id}/download· scope media:readDownload AssetGET/media/audience-insights· scope media:readList Audience InsightsPOST/media/audience-insights/generate· scope media:writeGenerate Audience InsightExplicitly synthetic (spec section 16: 'Safe Core supports synthetic audience segments') - real external audience data collection is locked regardless of instruction, same as every other real-external-data boundary in this pillar.GET/media/briefs· scope media:readList BriefsPOST/media/briefs· scope media:writeCreate BriefPOST/media/briefs/{brief_id}/approve· scope media:writeApprove BriefGET/media/calendar· scope media:readList CalendarPOST/media/calendar· scope media:writeCreate Calendar ItemGET/media/campaigns· scope media:readList CampaignsPOST/media/campaigns· scope media:writeCreate CampaignPATCH/media/campaigns/{campaign_id}/status· scope media:writeTransition CampaignGET/media/distribution· scope media:readList Distribution JobsPOST/media/distribution· scope media:writeCreate Distribution JobPATCH/media/distribution/{job_id}/status· scope media:writeTransition Distribution JobReal external posting states are never enabled here (spec section 15) - `simulated` is the terminal success state, there is no `posted`/`published` status in the allow-list at all.GET/media/experiments· scope media:readList ExperimentsPOST/media/experiments· scope media:writeCreate ExperimentPATCH/media/experiments/variants/{variant_id}/results· scope media:writeRecord Variant ResultsPATCH/media/experiments/variants/{variant_id}/short-link· scope media:writeBind Variant Short LinkMD6 - binds the variant to ONE of the tenant's own short links. From then on its reading is the link's real clicks; the synthetic numbers stop mattering for this variant and the banner comes off honestly.POST/media/experiments/{experiment_id}/complete· scope media:writeComplete ExperimentDeclares a winner by real conversion-rate comparison across recorded synthetic variants - refuses to declare a winner from insufficient data (spec section 12: 'must avoid declaring winners from insufficient data'), using a minimum-sample-size floor rather than just picking whichever variant has the highest raw rate.GET/media/hooks· scope media:readList HooksPOST/media/hooks· scope media:writeCreate HookGET/media/integrations· scope media:readList IntegrationsGET/media/live-recaps· scope media:readList RecapsPOST/media/live-recaps· scope media:writeCreate RecapPOST/media/live-recaps/{recap_id}/draft-summary· scope media:writeDraft Recap SummaryGET/media/provenance· scope media:readList ProvenancePOST/media/provenance· scope media:writeCreate ProvenanceSigning status stays 'unsigned' by default - production C2PA signing is a separately-gated real infrastructure decision (spec section 26/37), not something recorded here just because a row was created.GET/media/reputation· scope media:readList Reputation SignalsPOST/media/reputation/generate· scope media:writeGenerate Reputation SignalPOST/media/workspaces· scope media:writeCreate WorkspaceGET/media/workspaces/mine· scope media:readGet My Workspace EndpointDELETE/multimedia-factory/items/{iid}· scope media:writeUnlink ItemUnlinks; the project itself stays in its factory.GET/multimedia-factory/productions· scope media:readList ProductionsPOST/multimedia-factory/productions· scope media:writeCreate ProductionGET/multimedia-factory/productions/{pid}· scope media:readGet ProductionDELETE/multimedia-factory/productions/{pid}· scope media:writeDelete ProductionRemoves the production and its links. The song / video / deck projects stay in their factories - they are the person's work, not the production's.GET/multimedia-factory/productions/{pid}/brief.md· scope media:readProduction Brief MdThe one-page production brief a human collaborator (a musician, an editor, a designer) can work from - the same spine the factories got.POST/multimedia-factory/productions/{pid}/certificate· scope media:writeIssue CertificateGET/multimedia-factory/productions/{pid}/certificate· scope media:readGet CertificatePUT/multimedia-factory/productions/{pid}/intake· scope media:writePut IntakePOST/multimedia-factory/productions/{pid}/items· scope media:writeLink ItemLink an existing project (made before the production, or by hand) so it is rolled up too.PUT/multimedia-factory/productions/{pid}/message· scope media:writePut MessagePOST/multimedia-factory/productions/{pid}/plan· scope media:writePlan ProductionThe Producer: message spine + which outputs / roles / order / why + facts + open questions. Falls back to the deterministic rules (and says so) when the model is busy or unavailable.PUT/multimedia-factory/productions/{pid}/plan· scope media:writePatch PlanThe person's edits: outputs on/off, roles, titles, briefs, order - kept as 'user' authored.POST/multimedia-factory/productions/{pid}/produce· scope media:writeProduceCreates the chosen outputs as real projects in their factories, briefs prefilled from the spine, sources copied into the deck's ledger, facts handed to the song and the video. Kinds already linked are skipped (no duplicates); nothing renders here.POST/multimedia-factory/productions/{pid}/sources· scope media:writeAdd SourceDELETE/multimedia-factory/sources/{sid}· scope media:writeDelete SourcePATCH/podcasts/episodes/{episode_id}· scope media:writeUpdate EpisodeDELETE/podcasts/episodes/{episode_id}· scope media:writeDelete EpisodePOST/podcasts/episodes/{episode_id}/audio· scope media:writeUpload Episode AudioPOST/podcasts/episodes/{episode_id}/publish· scope media:writeToggle Episode PublishGET/podcasts/shows· scope media:readList ShowsPOST/podcasts/shows· scope media:writeCreate ShowPATCH/podcasts/shows/{show_id}· scope media:writeUpdate ShowPOST/podcasts/shows/{show_id}/artwork· scope media:writeUpload ArtworkGET/podcasts/shows/{show_id}/episodes· scope media:readList EpisodesPOST/podcasts/shows/{show_id}/episodes· scope media:writeCreate EpisodePOST/podcasts/shows/{show_id}/publish· scope media:writeToggle Show PublishGET/presentation-factory/accessibility-reports/{rid}· scope media:readGet ReportGET/presentation-factory/accessibility-reports/{rid}/markdown· scope media:readReport MarkdownThe handable version. Plain Markdown, because the person who asked for this may well need to paste it into an e-mail or a tender response.POST/presentation-factory/approvals/{approval_id}:decide· scope media:writeDecide ApprovalPOST/presentation-factory/certificates/{cid}:verify· scope media:writeVerify CertificatePATCH/presentation-factory/claims/{cid}· scope media:writePatch ClaimDELETE/presentation-factory/claims/{cid}· scope media:writeDelete ClaimPATCH/presentation-factory/collaborators/{collaborator_id}· scope media:writePatch CollaboratorDELETE/presentation-factory/collaborators/{collaborator_id}· scope media:writeRevoke CollaboratorPOST/presentation-factory/collaborators/{collaborator_id}:respond· scope media:writeRespond InviteDELETE/presentation-factory/comments/{comment_id}· scope media:writeDelete CommentPOST/presentation-factory/comments/{comment_id}:reopen· scope media:writeReopen CommentPOST/presentation-factory/comments/{comment_id}:resolve· scope media:writeResolve CommentGET/presentation-factory/datasets/{did}· scope media:readGet DatasetDELETE/presentation-factory/datasets/{did}· scope media:writeDelete DatasetPUT/presentation-factory/datasets/{did}/columns· scope media:writePatch Dataset ColumnsEdit types/units (the honest metadata) - names and data are the snapshot's, immutable.POST/presentation-factory/datasets/{did}:refresh· scope media:writeRefresh DatasetA NEW snapshot from the current file / a fresh paste, with the drift named. Charts keep showing their reviewed snapshot until someone updates them - never silently.DELETE/presentation-factory/domains/{did}· scope media:writeDelete DomainPOST/presentation-factory/domains/{did}:verify· scope media:writeVerify DomainLook for the token at the .well-known path. Nothing about this is taken on trust: the fetch goes through the same guard as any other, and a near-miss is reported as a failure.GET/presentation-factory/exports/{eid}/certificate· scope media:readGet CertificateGET/presentation-factory/exports/{eid}/file· scope media:readExport FilePOST/presentation-factory/followups/{fid}:answer· scope media:writeAnswer FollowupSaves the presenter's answer. Evidence ids are checked against this deck's ledger - an id from somewhere else is dropped, not trusted - and an answer with none is stored `unsourced`.POST/presentation-factory/followups/{fid}:decline· scope media:writeDecline FollowupNot every question can be answered, and pretending otherwise is worse than saying so. A declined follow-up keeps the question and the reason; it just stops being open work.POST/presentation-factory/followups/{fid}:draft· scope media:writeDraft AnswerDrafts, but never saves - the presenter is the one who answers the room. The draft comes back with its evidence and with any invented number named out loud.POST/presentation-factory/followups/{fid}:publish· scope media:writePublish FollowupPOST/presentation-factory/layout:preview· scope media:writeLayout PreviewDELETE/presentation-factory/locks/{lid}· scope media:writeUnlock RouteGET/presentation-factory/media/available· scope media:readAvailable MediaWhat this person can attach: their ready Song Factory renders and Video Factory exports, each with the certificate it already carries.PATCH/presentation-factory/media/{mid}· scope media:writePatch MediaDELETE/presentation-factory/media/{mid}· scope media:writeDelete MediaGET/presentation-factory/media/{mid}/captions· scope media:readMedia CaptionsGET/presentation-factory/media/{mid}/file· scope media:readMedia FileGET/presentation-factory/narrations/{nid}· scope media:readGet NarrationDELETE/presentation-factory/narrations/{nid}· scope media:writeDelete NarrationGET/presentation-factory/narrations/{nid}/audio· scope media:readNarration AudioGET/presentation-factory/narrations/{nid}/captions· scope media:readNarration CaptionsPOST/presentation-factory/narrations/{nid}/share· scope media:writeShare TalkPOST/presentation-factory/narrations/{nid}/share:revoke· scope media:writeRevoke TalkGET/presentation-factory/narrations/{nid}/transcript· scope media:readNarration TranscriptThe whole talk as text - the thing a person can read instead of listening, or search.GET/presentation-factory/narrations/{nid}/what-would-be-published· scope media:readWhat Would Be PublishedShown BEFORE the share button does anything: the exact words that would become public, in order, so the decision gets made looking at the thing rather than at a checkbox.POST/presentation-factory/organizations/{org_id}/claims· scope media:writeCreate ClaimGET/presentation-factory/organizations/{org_id}/claims· scope media:readList ClaimsPATCH/presentation-factory/organizations/{org_id}/claims/{cid}· scope media:writeUpdate ClaimEditing the library NEVER touches a deck. The response says how many decks now differ, so the consequence of the edit is visible at the moment it is made rather than discovered later.GET/presentation-factory/organizations/{org_id}/claims/{cid}/usage· scope media:readClaim UsagePOST/presentation-factory/organizations/{org_id}/claims/{cid}:retire· scope media:writeRetire ClaimRetiring names the decks it affects. You cannot retire evidence into the void.POST/presentation-factory/organizations/{org_id}/claims/{cid}:verify· scope media:writeVerify ClaimA person went and looked. That is the only thing that resets the clock - there is no automatic re-verification here, because nothing about a stored row can tell you the world still agrees.GET/presentation-factory/organizations/{org_id}/claims:drift· scope media:readDriftEverywhere the library and a deck no longer agree, plus the claims nobody has checked. This is the report that answers "which eleven decks still say 1,240?".POST/presentation-factory/organizations/{org_id}/domains· scope media:writeAdd DomainRegisters a domain as the organisation's own. It starts DECLARED - which means somebody typed it. Verification is a separate, provable step.GET/presentation-factory/organizations/{org_id}/domains· scope media:readList DomainsGET/presentation-factory/organizations/{org_id}/insights· scope media:readOrg InsightsThe same honesty, one level up: across an organisation's decks. RLS decides which decks are visible - this adds no visibility of its own.PUT/presentation-factory/organizations/{organization_id}/brand-profile· scope media:writePut Brand ProfileDELETE/presentation-factory/polls/{poll_id}· scope media:writeDelete PollGET/presentation-factory/projects· scope media:readList ProjectsPOST/presentation-factory/projects· scope media:writeCreate ProjectGET/presentation-factory/projects/{pid}· scope media:readGet ProjectDELETE/presentation-factory/projects/{pid}· scope media:writeDelete ProjectGET/presentation-factory/projects/{pid}/accessibility· scope media:readCheck AccessibilityThe live view. Recomputed every time, so it is never stale - and never a certificate.GET/presentation-factory/projects/{pid}/accessibility/reports· scope media:readList ReportsPOST/presentation-factory/projects/{pid}/accessibility:issue· scope media:writeIssue ReportFreezes the report against this version, hashes it, and stores it. Failures do not block - a report that only exists when everything passes is a marketing document, and the whole value of this one is that it can say what is wrong. But issuing over failures is recorded.GET/presentation-factory/projects/{pid}/brand· scope media:readGet BrandPOST/presentation-factory/projects/{pid}/brand:apply· scope media:writeApply BrandApply the organisation's Theme Editor tokens to the deck: colours, fonts, the logo (as a data URI at render time so exports stay self-contained), the required footer line.POST/presentation-factory/projects/{pid}/brief· scope media:writeSave BriefPOST/presentation-factory/projects/{pid}/brief:strategist· scope media:writeStrategistPOST/presentation-factory/projects/{pid}/check:notes· scope media:writeCheck NotesDo the notes and the slide agree? Rules catch the numbers; the model catches the ones written in words. When the model is unavailable the rules still run, and the answer says which ran.GET/presentation-factory/projects/{pid}/checks· scope media:readChecksPOST/presentation-factory/projects/{pid}/claims· scope media:writeAdd ClaimPOST/presentation-factory/projects/{pid}/claims:adopt· scope media:writeAdopt ClaimsCopies library claims into this deck. A retired claim is refused rather than quietly adopted.POST/presentation-factory/projects/{pid}/datasets· scope media:writeCreate DatasetPOST/presentation-factory/projects/{pid}/deck· scope media:writeSave DeckPOST/presentation-factory/projects/{pid}/deck:architect· scope media:writeArchitectGET/presentation-factory/projects/{pid}/delivery· scope media:readDelivery ReportAcross every ended session of this deck: what the plan said, what actually happened, and the slides that keep costing you time. Nothing here is modelled - it is your own stopwatch.GET/presentation-factory/projects/{pid}/diff· scope media:readDiffCompare two versions. `base` defaults to the previous version, `against` to the current one.POST/presentation-factory/projects/{pid}/evidence:map· scope media:writeEvidence MapPOST/presentation-factory/projects/{pid}/exports· scope media:writeExport DeckPOST/presentation-factory/projects/{pid}/followups· scope media:writeAdd FollowupA question asked out loud that never made it to a phone still deserves an answer.GET/presentation-factory/projects/{pid}/followups· scope media:readList FollowupsPATCH/presentation-factory/projects/{pid}/freshness· scope media:writeSet HorizonHow long this deck's evidence stays fresh. A safety briefing and a conference keynote do not age at the same rate, and the platform should not pretend to know which one this is.POST/presentation-factory/projects/{pid}/import:pptx· scope media:writeImport Pptx DeckA .pptx comes in as a DRAFT deck version - text and speaker notes - with a report of what did not come across. Nothing is overwritten; it is a new version to edit or discard.GET/presentation-factory/projects/{pid}/insights· scope media:readDeck InsightsWhat this deck's rooms are telling you. Read-only; nothing here is stored.POST/presentation-factory/projects/{pid}/locks· scope media:writeLock RouteGET/presentation-factory/projects/{pid}/logo· scope media:readProject LogoPOST/presentation-factory/projects/{pid}/media· scope media:writeAttach MediaAttach a Song Factory render or a Video Factory export, snapshotting its provenance and pulling its accessibility material (captions / lyrics) in.GET/presentation-factory/projects/{pid}/media· scope media:readList MediaGET/presentation-factory/projects/{pid}/media/{sid}· scope media:readSource MediaA Song Factory render / Video export attached as a source, for the slide's <audio>/<video>.POST/presentation-factory/projects/{pid}/narration· scope media:writeStart NarrationSpeaks the deck's own speaker notes. Refuses if there are no notes at all rather than inventing a talk - a narration built from headlines would be a script the presenter never wrote.GET/presentation-factory/projects/{pid}/narrations· scope media:readList NarrationsGET/presentation-factory/projects/{pid}/parts· scope media:readParts RoutePOST/presentation-factory/projects/{pid}/polls· scope media:writeCreate PollGET/presentation-factory/projects/{pid}/polls· scope media:readList PollsGET/presentation-factory/projects/{pid}/preflight· scope media:readPreflightWhat to read in the two minutes before you start a session. Same assessment, ordered worst first and stripped to what a person can actually act on while walking to the front.POST/presentation-factory/projects/{pid}/presence· scope media:writePresence RouteGET/presentation-factory/projects/{pid}/present· scope media:readPresentThe in-app accessible web deck for the CURRENT version (same renderer as the export).GET/presentation-factory/projects/{pid}/readiness· scope media:readReadinessPOST/presentation-factory/projects/{pid}/rehearsal· scope media:writeStart RehearsalThe skeptical panel + the toughest questions, from the brief's own audience and objections. `use_rules` (or a busy GPU) gives the deterministic panel and says so.POST/presentation-factory/projects/{pid}/sessions· scope media:writeStart SessionStarts a live session on the deck's CURRENT version and pins it. Unlike an export, this does NOT block on the quality checker: someone is standing in front of a room and the deck is what it is. The unresolved count comes back with the code so it is said out loud, not buried.GET/presentation-factory/projects/{pid}/sessions· scope media:readList SessionsGET/presentation-factory/projects/{pid}/since-approval· scope media:readSince ApprovalWhat has moved since somebody put their name to this. If nothing was ever approved it says so rather than quietly comparing against something else and calling it the same question.POST/presentation-factory/projects/{pid}/slides/{part_id}:ai-edit· scope media:writeAi Edit RoutePOST/presentation-factory/projects/{pid}/sources· scope media:writeAdd SourcePOST/presentation-factory/projects/{pid}/sources:staleness· scope media:writeStalenessRun the evidence refresh now (same handler the automation loop runs): re-verify every claim's verbatim excerpt, mark vanished ones 'stale', report workspace-file drift.POST/presentation-factory/projects/{pid}/sources:web· scope media:writeAdd Web SourceFetch a page and keep it as a snapshot. The permission field defaults to 'unknown' and stays the person's declaration - Fitinty does not decide that a page is reusable because it loaded.POST/presentation-factory/projects/{pid}/suggestions· scope media:writeSuggest RoutePOST/presentation-factory/projects/{pid}/templates/{tid}:apply· scope media:writeApply TemplatePOST/presentation-factory/projects/{pid}/theme· scope media:writeSet ThemePOST/presentation-factory/projects/{pid}/variants· scope media:writeCreate VariantA LOCALE VARIANT: a full linked deck in the target language - brief, sources and claims copied (evidence stays the single source of truth), slides machine-translated (marked, flagged 'native review needed' until a person marks it reviewed) and judged at the target language's text length. The parent deck is never touched.POST/presentation-factory/projects/{pid}:mark-reviewed· scope media:writeMark ReviewedA person who read the variant in its language signs it off; recorded, not assumed.POST/presentation-factory/projects/{project_id}/approvals· scope media:writeRequest ApprovalPOST/presentation-factory/projects/{project_id}/collaborators· scope media:writeAdd CollaboratorGET/presentation-factory/projects/{project_id}/comments· scope media:readList CommentsPOST/presentation-factory/projects/{project_id}/comments· scope media:writeAdd CommentPOST/presentation-factory/projects/{project_id}/share-links· scope media:writeCreate Share LinkGET/presentation-factory/projects/{project_id}/sharing· scope media:readGet SharingPATCH/presentation-factory/projects/{project_id}/sharing· scope media:writePatch SharingPOST/presentation-factory/projects/{project_id}:restore· scope media:writeRestore ProjectPOST/presentation-factory/projects/{project_id}:takedown· scope media:writeTakedown ProjectGET/presentation-factory/rehearsals/{rid}· scope media:readGet RehearsalPOST/presentation-factory/rehearsals/{rid}:answer· scope media:writeAnswer RehearsalGrade one typed answer against the claim ledger. An invented number is a failed answer.POST/presentation-factory/rehearsals/{rid}:finish· scope media:writeFinish RehearsalThe readiness report: what held, what failed, which objections still have no home, timing.POST/presentation-factory/rehearsals/{rid}:timing· scope media:writeTiming RehearsalThe timing drill: actual seconds per slide (measured by the client) vs the budgets. Pure.POST/presentation-factory/reports· scope media:writeFile ReportGET/presentation-factory/reports· scope media:readList ReportsPATCH/presentation-factory/reports/{report_id}· scope media:writePatch ReportGET/presentation-factory/sessions/{sid}· scope media:readGet SessionThe presenter's own view: every question INCLUDING the hidden ones (with the reason recorded against them), live poll counts, and the running timeline.DELETE/presentation-factory/sessions/{sid}· scope media:writeDelete SessionPOST/presentation-factory/sessions/{sid}/followup-page:publish· scope media:writePublish PageMints (once) an unguessable link the presenter can hand back to the room. This publishes the answers they marked as publishable - nothing else, and nobody is e-mailed or texted.POST/presentation-factory/sessions/{sid}/followup-page:revoke· scope media:writeRevoke PagePOST/presentation-factory/sessions/{sid}/followups:collect· scope media:writeCollect FollowupsTurns every question the room asked that was never answered into a follow-up item. Hidden questions are NOT collected - they were taken off the screen for a reason - but the count of them comes back, so the presenter is told what was left behind rather than left to assume.POST/presentation-factory/sessions/{sid}/polls/{poll_id}:open· scope media:writeOpen PollOne poll open at a time - a room cannot answer two questions at once, and the DB index says so.POST/presentation-factory/sessions/{sid}/polls:close· scope media:writeClose PollPOST/presentation-factory/sessions/{sid}/questions/{qid}:answered· scope media:writeMark AnsweredPOST/presentation-factory/sessions/{sid}/questions/{qid}:hide· scope media:writeHide QuestionTakes a question off the room's screen. It is NOT deleted: the words stay, with who hid them and why, and the session summary counts them. Moderation you can audit is the only honest kind.POST/presentation-factory/sessions/{sid}/questions/{qid}:restore· scope media:writeRestore QuestionPOST/presentation-factory/sessions/{sid}/runs/{run_id}:to-dataset· scope media:writePoll Run To DatasetPull a closed poll's numbers into the deck as a dataset - carrying the provenance sentence with them, permanently. The room's answer can be shown; it cannot be laundered into a statistic, because the sentence that says what it really is travels in the same row.POST/presentation-factory/sessions/{sid}/slide· scope media:writeGo To SlideMoving on closes the previous slide's stopwatch. That is the whole source of the honest 'planned 4 minutes, took 11' report at the end - nothing is estimated after the fact.POST/presentation-factory/sessions/{sid}:end· scope media:writeEnd SessionEnds the room and writes the record once. The part that matters most is the list of questions that were NEVER answered - the thing a presenter forgets by the time they reach the car park.DELETE/presentation-factory/share-links/{link_id}· scope media:writeRevoke Share LinkGET/presentation-factory/shared-with-me· scope media:readShared With MePATCH/presentation-factory/sources/{sid}· scope media:writePatch SourceDELETE/presentation-factory/sources/{sid}· scope media:writeDelete SourceGET/presentation-factory/sources/{sid}/snapshots· scope media:readList SnapshotsGET/presentation-factory/sources/{sid}/text· scope media:readSource TextPOST/presentation-factory/sources/{sid}:recheck· scope media:writeRecheck Web SourceFetch the page again and say plainly whether it still says what was cited. Drift is reported, never silently applied: the old snapshot stays, and the claims keep pointing at what was read.POST/presentation-factory/suggestions/{sid}:accept· scope media:writeAccept RoutePOST/presentation-factory/suggestions/{sid}:reject· scope media:writeReject RouteGET/presentation-factory/templates· scope media:readList TemplatesPOST/presentation-factory/templates· scope media:writeSave TemplateDELETE/presentation-factory/templates/{tid}· scope media:writeDelete TemplatePOST/presentation-factory/templates/{tid}:approve· scope media:writeApprove TemplateGET/presentation-factory/versions/{vid}· scope media:readGet VersionPOST/presentation-factory/versions/{vid}:restore· scope media:writeRestore VersionPOST/song-factory/approvals/{approval_id}:decide· scope media:writeDecide ApprovalGET/song-factory/bridges/cinema/projects· scope media:readList Cinema ProjectsCinema projects the caller can add a music track to (RLS-scoped) - the "Send to Cinema" picker.GET/song-factory/bridges/cinema/renders· scope media:readList Scoreable RendersCompleted Cinema renders the caller can see (RLS on render_attempt / render_job / cinema_project does the scoping), newest first - the picker for "Score a Cinema render".POST/song-factory/bridges/cinema/score-render· scope media:writeScore Cinema RenderPOST/song-factory/bridges/cinema/send/{gen_id}· scope media:writeSend Generation To CinemaA ready/exported generation -> Cinema music asset + audio_track + provenance record, with the Rights Certificate issued first and summarised on the asset.GET/song-factory/bridges/games/assets· scope media:readList Scoreable Game AssetsPOST/song-factory/bridges/games/import· scope media:writeImport Game AssetGET/song-factory/bridges/games/projects· scope media:readList Game ProjectsPOST/song-factory/bridges/games/send/{gen_id}· scope media:writeSend Generation To Game-> game_asset audio, source fitinty_song_factory, certificate summary in `license`, file_hash = audio sha, review_state proposed (the game's own review gate still decides).POST/song-factory/bridges/import· scope media:writeImport SourceGET/song-factory/bridges/marketing/assets· scope media:readList Scoreable Marketing AssetsGET/song-factory/bridges/marketing/campaigns· scope media:readList Marketing CampaignsSend targets: a profile (music for the brand) or a specific campaign under it.POST/song-factory/bridges/marketing/import· scope media:writeImport Marketing AssetPOST/song-factory/bridges/marketing/send/{gen_id}· scope media:writeSend Generation To Marketing-> marketing_content_asset with audio (migration 272) + marketing_provenance_record + marketing_cross_pillar_link (destination song_factory, accepted).GET/song-factory/bridges/media/assets· scope media:readList Scoreable Media AssetsImages and videos in Media workspaces the caller can see (is_media_staff via RLS).POST/song-factory/bridges/media/import· scope media:writeImport Media AssetA Media image -> Visual Score reference; a Media video -> measured video cue. Same rows, same declarations as a direct upload, with the provenance of where it came from.POST/song-factory/bridges/media/send/{gen_id}· scope media:writeSend Generation To MediaA ready/exported generation -> Media audio asset (ai_generated, consent_confirmed, the certificate summarised in rights_metadata) + media_provenance_record.GET/song-factory/bridges/media/workspaces· scope media:readList Media WorkspacesGET/song-factory/bridges/publishers/covers· scope media:readList Scoreable CoversPOST/song-factory/bridges/publishers/import· scope media:writeImport CoverPOST/song-factory/bridges/send/{gen_id}· scope media:writeSend GenerationGET/song-factory/bridges/sources· scope media:readList SourcesOne list for the UI: [{pillar, kind: image|video, ref_id, label}] across pillars, each RLS-scoped by its own query. Empty pillars simply contribute nothing.GET/song-factory/bridges/targets· scope media:readList TargetsEverywhere a finished render can be sent: [{pillar, ref_id, label}]. Workspace is always available (it is the person's own files).GET/song-factory/bridges/workspace/files· scope media:readList Scoreable Workspace FilesPOST/song-factory/bridges/workspace/import· scope media:writeImport Workspace FilePOST/song-factory/bridges/workspace/send/{gen_id}· scope media:writeSend Generation To Workspace-> two workspace_file rows: the FLAC and "<name>.certificate.json" beside it. workspace_file has no metadata column; a delivered song never travels without its certificate, so the certificate becomes a file of its own.GET/song-factory/certificates/{cert_id}/verify· scope media:readVerify CertificatePATCH/song-factory/clips/{clip_id}· scope media:writePatch ClipDELETE/song-factory/clips/{clip_id}· scope media:writeDelete ClipPOST/song-factory/clips/{clip_id}:split· scope media:writeSplit ClipNon-destructive split: the clip ends at `at`, a new clip starts there with the matching source offset. Fades stay where they were (in on the left part, out on the right).PATCH/song-factory/collaborators/{collaborator_id}· scope media:writePatch CollaboratorDELETE/song-factory/collaborators/{collaborator_id}· scope media:writeRevoke CollaboratorPOST/song-factory/collaborators/{collaborator_id}:respond· scope media:writeRespond InviteDELETE/song-factory/comments/{comment_id}· scope media:writeDelete CommentPOST/song-factory/comments/{comment_id}:reopen· scope media:writeReopen CommentPOST/song-factory/comments/{comment_id}:resolve· scope media:writeResolve CommentGET/song-factory/generations/{gen_id}· scope media:readGet GenerationGET/song-factory/generations/{gen_id}/audio· scope media:readGeneration AudioGET/song-factory/generations/{gen_id}/certificate· scope media:readGet CertificateGET/song-factory/generations/{gen_id}/consistency· scope media:readGeneration ConsistencyScore a generation against its project's identity using what was ACTUALLY sent.GET/song-factory/generations/{gen_id}/export· scope media:readExport GenerationDownload + certificate in one act (plan 12.1 - the package is bigger later; today it is the master and the certificate). Marks the generation EXPORTED.GET/song-factory/generations/{gen_id}/export-package· scope media:readExport PackagePlan 12.1 - the delivery package as one ZIP: the FLAC master, the WAV with the C2PA manifest (sandbox-signed when the SDK is present, else unsigned + the manifest JSON beside it), certificate.json, manifest.json, the .c2pa store when signed, and a README that says which is which and what is NOT warranted. Marks the generation exported.GET/song-factory/generations/{gen_id}/provenance· scope media:readGet ProvenanceGET/song-factory/generations/{gen_id}/provenance/asset· scope media:readProvenance AssetPOST/song-factory/generations/{gen_id}/provenance:issue· scope media:writeIssue ProvenancePOST/song-factory/generations/{gen_id}/qc· scope media:writeQc GenerationThe audio quality gate (measured, with fixes): EBU R128 loudness, true peak, lead/trail silence, duration vs the brief. Report, never a block - the person decides.POST/song-factory/generations/{gen_id}:cancel· scope media:writeCancel GenerationPOST/song-factory/generations/{gen_id}:confirm· scope media:writeConfirm GenerationPOST/song-factory/generations/{gen_id}:reroute· scope media:writeReroute GenerationAsk for a fallback estimate by hand - for a failed render that did not get one (no other model was loaded at the time), or to retry routing later. Same rules as the automatic offer.DELETE/song-factory/locks/{lid}· scope media:writeUnlock RouteDELETE/song-factory/markers/{marker_id}· scope media:writeDelete MarkerGET/song-factory/models· scope media:readList ModelsDELETE/song-factory/notes/{note_id}· scope media:writeDelete NoteGET/song-factory/projects· scope media:readList ProjectsPOST/song-factory/projects· scope media:writeCreate ProjectPOST/song-factory/projects/{pid}/locks· scope media:writeLock RouteGET/song-factory/projects/{pid}/parts· scope media:readParts RoutePOST/song-factory/projects/{pid}/parts/{part_id}:ai-edit· scope media:writeAi Edit RoutePOST/song-factory/projects/{pid}/presence· scope media:writePresence RoutePOST/song-factory/projects/{pid}/suggestions· scope media:writeSuggest RouteGET/song-factory/projects/{project_id}· scope media:readGet ProjectDELETE/song-factory/projects/{project_id}· scope media:writeDelete ProjectPOST/song-factory/projects/{project_id}/approvals· scope media:writeRequest ApprovalPOST/song-factory/projects/{project_id}/brief· scope media:writeSave BriefPOST/song-factory/projects/{project_id}/collaborators· scope media:writeAdd CollaboratorGET/song-factory/projects/{project_id}/comments· scope media:readList CommentsPOST/song-factory/projects/{project_id}/comments· scope media:writeAdd CommentGET/song-factory/projects/{project_id}/consistency· scope media:readProject ConsistencyScore the project's CURRENT brief against its bound identity (no render needed).POST/song-factory/projects/{project_id}/generations:estimate· scope media:writeEstimate GenerationPOST/song-factory/projects/{project_id}/lyrics· scope media:writeSave LyricsPOST/song-factory/projects/{project_id}/lyrics:generate· scope media:writeGenerate LyricsPOST/song-factory/projects/{project_id}/moment:compile· scope media:writeCompile MomentPOST/song-factory/projects/{project_id}/references· scope media:writeUpload ReferenceUpload a reference recording. Three declarations are required and stored verbatim with a timestamp; there is no identity detection and none is claimed (plan 7.5: the certificate's no-warranty line exists precisely because of what this cannot check).GET/song-factory/projects/{project_id}/references· scope media:readList ReferencesPOST/song-factory/projects/{project_id}/share-links· scope media:writeCreate Share LinkGET/song-factory/projects/{project_id}/sharing· scope media:readGet SharingPATCH/song-factory/projects/{project_id}/sharing· scope media:writePatch SharingPOST/song-factory/projects/{project_id}/sonic-identity:apply· scope media:writeApply IdentityOnly an APPROVED version is applied (brand/legal gate). Writes a brief version with the identity's rules enforced and binds the project to identity + version.POST/song-factory/projects/{project_id}/sonic-identity:unbind· scope media:writeUnbind IdentityGET/song-factory/projects/{project_id}/timeline· scope media:readGet TimelineGet-or-create the project's timeline. A fresh timeline gets one empty track and tempo from the brief (bpm / time signature) when the brief has them.POST/song-factory/projects/{project_id}/variations:estimate· scope media:writeEstimate VariationsPlan 5.2: one brief, N seeds, one estimate. Every row is a full generation (own certificate, own export); the group is how they are confirmed, rendered and shown together.POST/song-factory/projects/{project_id}/videos· scope media:writeUpload VideoGET/song-factory/projects/{project_id}/videos· scope media:readList VideosPOST/song-factory/projects/{project_id}/visuals· scope media:writeUpload VisualGET/song-factory/projects/{project_id}/visuals· scope media:readList VisualsPOST/song-factory/projects/{project_id}/visuals:analyze· scope media:writeAnalyze VisualsPer-image analysis (cached on the row) + a synthesized VisualMusicBrief. Returns both; applies nothing - the person edits and applies (plan 5.6 steps 5-7).POST/song-factory/projects/{project_id}/visuals:apply· scope media:writeApply Visual BriefPOST/song-factory/projects/{project_id}:remix· scope media:writeRemix ProjectPOST/song-factory/projects/{project_id}:restore· scope media:writeRestore ProjectPOST/song-factory/projects/{project_id}:takedown· scope media:writeTakedown ProjectGET/song-factory/provenance/sdk· scope media:readProvenance SdkDELETE/song-factory/references/{ref_id}· scope media:writeDelete ReferenceGET/song-factory/references/{ref_id}/audio· scope media:readReference AudioGET/song-factory/renders/{render_id}/audio· scope media:readRender AudioPOST/song-factory/reports· scope media:writeFile ReportGET/song-factory/reports· scope media:readList ReportsPATCH/song-factory/reports/{report_id}· scope media:writePatch ReportDELETE/song-factory/share-links/{link_id}· scope media:writeRevoke Share LinkGET/song-factory/shared-with-me· scope media:readShared With MeGET/song-factory/sonic-identities· scope media:readList IdentitiesPOST/song-factory/sonic-identities· scope media:writeCreate IdentityGET/song-factory/sonic-identities/organizations· scope media:readList Identity OrgsThe organizations this person can hold a Sonic Identity for (member or owner).GET/song-factory/sonic-identities/{identity_id}· scope media:readGet IdentityGET/song-factory/sonic-identities/{identity_id}/series· scope media:readSeries ConsistencyEvery render across projects bound to this identity, scored - the 'episode consistency' view for a series or campaign (plan 5.7 / SF-031).POST/song-factory/sonic-identities/{identity_id}/versions· scope media:writeAdd VersionEditing = a new version. Approval does not carry over: the new version is unapproved until an owner approves it (the identity's status drops back to draft if it was approved).POST/song-factory/sonic-identities/{identity_id}:approve· scope media:writeApprove IdentityBrand / legal review (plan 5.7): an org OWNER approves the CURRENT version. Members can draft; only owners approve - the same line themes draw for publishing.POST/song-factory/sonic-identities/{identity_id}:archive· scope media:writeArchive IdentityGET/song-factory/studio/peaks· scope media:readPeaksPOST/song-factory/suggestions/{sid}:accept· scope media:writeAccept RoutePOST/song-factory/suggestions/{sid}:reject· scope media:writeReject RoutePATCH/song-factory/timelines/{timeline_id}· scope media:writePatch TimelinePOST/song-factory/timelines/{timeline_id}/clips· scope media:writeAdd ClipPOST/song-factory/timelines/{timeline_id}/clips:place· scope media:writePlace ClipGET/song-factory/timelines/{timeline_id}/daw-package· scope media:readDaw PackagePlan 12.1 'aligned DAW export': mix.wav, stems/<track>.wav each rendered alone and padded to the full length (so they line up on import), markers.csv, edl.json, tempo.txt, lyrics.txt, certificates/<generation>.json for every render used, README.POST/song-factory/timelines/{timeline_id}/markers· scope media:writeAdd MarkerPOST/song-factory/timelines/{timeline_id}/notes· scope media:writeAdd NotePOST/song-factory/timelines/{timeline_id}/regenerate-region· scope media:writeRegenerate RegionA region -> a new render at the region's EXACT length. Writes a new brief version (the duration, and instrumental when no excerpt) and, with an excerpt, a new lyrics version - both append-only, so the regeneration is provenance, not a side channel. Returns the estimate; the person confirms it in Generate; when ready, `clips:place` drops it into the region.POST/song-factory/timelines/{timeline_id}/renders· scope media:writeRender TimelineSynchronous delivery render of the mix (seconds for a few minutes of audio).POST/song-factory/timelines/{timeline_id}/tracks· scope media:writeAdd TrackPATCH/song-factory/tracks/{track_id}· scope media:writePatch TrackDELETE/song-factory/tracks/{track_id}· scope media:writeDelete TrackPOST/song-factory/variation-groups/{group_id}:cancel· scope media:writeCancel VariationsCancels every row of the grid that has not started rendering; ones already on the GPU finish (the runner skips canceled rows before it starts each one).POST/song-factory/variation-groups/{group_id}:confirm· scope media:writeConfirm VariationsOne set of declarations for the whole grid; the same checks as a single confirm, applied to every row; one background runner for the group.DELETE/song-factory/videos/{video_id}· scope media:writeDelete VideoGET/song-factory/videos/{video_id}/media· scope media:readVideo MediaPOST/song-factory/videos/{video_id}:apply· scope media:writeApply Video BriefThe edited direction becomes a brief version with the clip's EXACT duration, the energy arc, the hit points, and video_reference_id for provenance.POST/song-factory/videos/{video_id}:measure· scope media:writeMeasure VideoMeasure (cached), describe a few frames with the vision model, synthesize a direction. Returns measurement + direction; applies nothing.DELETE/song-factory/visuals/{visual_id}· scope media:writeDelete VisualGET/song-factory/visuals/{visual_id}/image· scope media:readVisual ImageGET/video-factory/animatics/{aid}· scope media:readGet AnimaticGET/video-factory/animatics/{aid}/file· scope media:readAnimatic FilePOST/video-factory/approvals/{approval_id}:decide· scope media:writeDecide ApprovalGET/video-factory/audio-masters/{mid}· scope media:readGet MasterGET/video-factory/audio-masters/{mid}/file· scope media:readMaster FileGET/video-factory/board-frames/{fid}/image· scope media:readBoard ImagePOST/video-factory/certificates/{cert_id}:verify· scope media:writeVerify CertificatePATCH/video-factory/collaborators/{collaborator_id}· scope media:writePatch CollaboratorDELETE/video-factory/collaborators/{collaborator_id}· scope media:writeRevoke CollaboratorPOST/video-factory/collaborators/{collaborator_id}:respond· scope media:writeRespond InviteDELETE/video-factory/comments/{comment_id}· scope media:writeDelete CommentPOST/video-factory/comments/{comment_id}:reopen· scope media:writeReopen CommentPOST/video-factory/comments/{comment_id}:resolve· scope media:writeResolve CommentGET/video-factory/conforms/{cid}· scope media:readGet ConformPOST/video-factory/conforms/{cid}:apply· scope media:writeApply ConformTurn a read timeline into a new cut version - only when every clip in it is media this project already holds. The new cut carries `conformed_from` for the rest of its life, so any certificate issued from it says the edit was made outside Fitinty.GET/video-factory/consent/{record_id}/trace· scope media:readTraceWhere has this person's voice ended up? Asked without changing anything.POST/video-factory/consent/{record_id}/withdraw· scope media:writeWithdrawTake back what can be taken back, and say plainly what cannot.GET/video-factory/continuity-reviews/{rid}· scope media:readGet ReviewPOST/video-factory/continuity-reviews/{rid}/decide· scope media:writeDecideA person telling the machine whether it was right. `disagreed` is a first-class outcome, not an override to be argued with - the model is wrong often enough that a review with no way to say so would just be a machine's opinion with extra steps.GET/video-factory/cost/mine· scope media:readMy FootprintEverything you have asked this machine to do, across all your films. The number an operator would otherwise have to ask for, answerable by the person themselves - which is the point. Somebody who can see their own footprint can manage it; somebody who can only be told about it by an administrator finds out when it is already a conversation.GET/video-factory/credits/{cid}· scope media:readGet CreditsGET/video-factory/credits/{cid}/video· scope media:readCredits VideoPOST/video-factory/cuts/{cut_id}/handoff· scope media:writeCreate HandoffWrite the cut as a timeline an editor can open, and say plainly what did not come with it.GET/video-factory/cuts/{cut_id}/handoffs· scope media:readList HandoffsPOST/video-factory/cuts/{cut_id}:render· scope media:writeRender CutSynchronous ffmpeg render of the EDL -> MP4 review export (+ WebVTT sidecar). Typically seconds for a few shots; the caller shows 'rendering'.GET/video-factory/delivery-reports/{rid}· scope media:readGet ReportPOST/video-factory/delivery-reports/{rid}/answer· scope media:writeAnswer`intended` is a real answer, not an override to be argued with. Most of what this report raises will turn out to be somebody's deliberate edit, and a report that treats every one of those as an outstanding failure is a report people stop opening.PATCH/video-factory/entities/{eid}· scope media:writePatch EntityDELETE/video-factory/entities/{eid}· scope media:writeDelete EntityGET/video-factory/exports/{export_id}/accessibility· scope media:readCheckPOST/video-factory/exports/{export_id}/accessibility:issue· scope media:writeIssueGET/video-factory/exports/{export_id}/captions· scope media:readExport CaptionsGET/video-factory/exports/{export_id}/certificate· scope media:readGet CertificatePOST/video-factory/exports/{export_id}/credits· scope media:writeCreateGET/video-factory/exports/{export_id}/credits· scope media:readList CreditsGET/video-factory/exports/{export_id}/credits:preview· scope media:readPreviewWhat the credits would say, before anything is rendered.GET/video-factory/exports/{export_id}/deliverable· scope media:readWhat Would GoWhat a package for this export would contain, without building one. Worth its own route: the answer is a decision aid - "is this film ready to send?" - and asking it should not cost a zip of a film that is not ready to go anywhere yet.GET/video-factory/exports/{export_id}/described-audio· scope media:readDescribed TrackThe descriptions as a WebVTT `descriptions` track - the standard way to ship audio description as text a screen reader can speak, without re-rendering the video.GET/video-factory/exports/{export_id}/descriptions· scope media:readList DescriptionsEvery shot, with whatever description exists - and, for generated shots with none, what the shot was ASKED to be, offered as a starting point and labelled as such.PUT/video-factory/exports/{export_id}/descriptions· scope media:writeSave DescriptionSaving a description marks it `human`: somebody has looked and written it. There is no path that promotes a suggestion to a description without a person passing it through here.POST/video-factory/exports/{export_id}/loudness· scope media:writeNormaliseMake a rendition at a named delivery loudness. A NEW file - never a replacement.POST/video-factory/exports/{export_id}/package· scope media:writeCreatePut everything that belongs with this film in one place, and say what does not.GET/video-factory/exports/{export_id}/packages· scope media:readList PackagesGET/video-factory/exports/{export_id}/provenance/asset· scope media:readProvenance AssetPOST/video-factory/exports/{export_id}/provenance:issue· scope media:writeIssue ProvenancePOST/video-factory/exports/{export_id}/qc· scope media:writeQc ExportThe video quality gate (measured, with fixes). Report, never a block.GET/video-factory/exports/{export_id}/sound· scope media:readSound ReportEverything about this file's sound: how loud it is, where the speech is, where the silence is, and whether the audio description written for it can actually be heard.POST/video-factory/exports/{export_id}/subtitles· scope media:writeCreateTranslate this film's captions into another language, and measure whether they can be read.GET/video-factory/exports/{export_id}/subtitles· scope media:readList TracksPOST/video-factory/exports/{export_id}/variants· scope media:writeExport VariantsCut the master into per-channel aspects (9:16 social, 1:1 feed, 4:5 portrait feed). Each variant is a real export row: its own QC, its own certificate (same EDL provenance).GET/video-factory/exports/{export_id}/video· scope media:readExport VideoGET/video-factory/exports/{export_id}/withdrawn· scope media:readExport WithdrawnWhether this film contains material somebody has taken back, and what is waiting on a person.POST/video-factory/exports/{export_id}/withdrawn-decision· scope media:writeDecideA person decides what happens to a finished film that holds withdrawn material. `destroy` really destroys the film file. `keep` does not un-flag anything - it records that a named person looked at this and chose to keep it, which is a different and more useful fact than the flag quietly disappearing.GET/video-factory/exports/{to_export_id}/notes-from/{from_export_id}· scope media:readPreviewWhat the notes on the older cut point at now. Nothing is stored or moved.POST/video-factory/exports/{to_export_id}/notes-from/{from_export_id}· scope media:writeIssueKeep the reading, so decisions about individual notes can be recorded against it.PATCH/video-factory/footage/{fid}· scope media:writeUpdate FootageDELETE/video-factory/footage/{fid}· scope media:writeDelete FootagePOST/video-factory/footage/{fid}/lipsync· scope media:writeRefuse Footage LipsyncA route that exists in order to refuse, and to say why in full. Returning 404 here would be cheaper and would teach nobody anything. Somebody who tries this is asking a reasonable-sounding question - "can I sync my own footage?" - and deserves the real answer rather than a dead end.POST/video-factory/footage/{fid}/transcript· scope media:writeStart TranscriptGET/video-factory/footage/{fid}/transcript· scope media:readGet TranscriptPUT/video-factory/footage/{fid}/transcript· scope media:writeSave TranscriptCorrecting a line is what turns a guess into a record. `mark_checked` is the person saying they have actually read it - it is not set for them by editing one segment.GET/video-factory/formats· scope media:readFormatsWhich format to pick, and the honest reason for each.GET/video-factory/generations/{gen_id}· scope media:readGet GenerationGET/video-factory/generations/{gen_id}/thumbnail· scope media:readGeneration ThumbnailGET/video-factory/generations/{gen_id}/video· scope media:readGeneration VideoPOST/video-factory/generations/{gen_id}:cancel· scope media:writeCancelPOST/video-factory/generations/{gen_id}:confirm· scope media:writeConfirmPOST/video-factory/generations/{gen_id}:retry· scope media:writeRetryPlan MVF3 retry/recovery: a retryable or provider-unavailable take goes back to the queue with the SAME declaration (the person already made it) once the engine is reachable.POST/video-factory/generations/{gid}/grade· scope media:writeStart GradeBring one shot closer to the rest of the film. Writes a new file; never touches the take.POST/video-factory/generations/{gid}/lipsync· scope media:writeStart LipsyncRe-animate the mouth in this take to the lines spoken for its shot.GET/video-factory/generations/{gid}/readings· scope media:readShot ReadingsGET/video-factory/grades/{gid}/video· scope media:readGrade VideoGET/video-factory/handoffs/{hid}/file· scope media:readHandoff FileGET/video-factory/line-takes/{take_id}/audio· scope media:readTake AudioGET/video-factory/lipsync/{lid}/video· scope media:readLipsync VideoDELETE/video-factory/locks/{lid}· scope media:writeUnlock RouteGET/video-factory/look-reports/{rid}· scope media:readGet ReportPOST/video-factory/look-reports/{rid}/answer· scope media:writeAnswer"That shot is meant to look different" is the correct answer more often than not.GET/video-factory/models· scope media:readList ModelsPOST/video-factory/note-carries/{cid}/decide· scope media:writeDecideCarry one note onto the new cut, drop it, or leave it where it was. `carry` writes a NEW comment on the new export rather than moving the old one - the original is what a reviewer actually wrote against a film they actually saw, and rewriting its timecode would make the record of that conversation untrue.PUT/video-factory/organizations/{org_id}/machine-rate· scope media:writeSet RateSay what an hour of this machine is worth to you. Optional, and nothing is estimated in its absence. Recorded with a name because a figure that turns up in somebody's budget should be traceable to the person who chose it.GET/video-factory/organizations/{org_id}/machine-rate· scope media:readGet RateGET/video-factory/organizations/{org_id}/stranded-projects· scope media:readStrandedWhich of this organisation's films are held by people who have left. The operational question nobody could ask before. Restricted to owners of the organisation: "whose work is stuck" is a management question, and it names departed colleagues.GET/video-factory/packages/{pkg_id}· scope media:readGet PackageGET/video-factory/packages/{pkg_id}/download· scope media:readDownloadGET/video-factory/projects· scope media:readList ProjectsPOST/video-factory/projects· scope media:writeCreate ProjectPUT/video-factory/projects/{pid}/adaptation· scope media:writeSave AdaptationThe person's edit: a new version authored 'human' (or 'mixed' after an AI draft).POST/video-factory/projects/{pid}/adaptation:draft· scope media:writeDraft AdaptationLogline + treatment + beat sheet from the ingested source. A new adaptation version; the person edits it freely afterwards.POST/video-factory/projects/{pid}/animatic· scope media:writeBuild AnimaticCuts the boarded panels to the planned durations. A shot with no panel becomes a plain card rather than being skipped - the shape of the film depends on that shot taking its time whether or not anybody drew it.GET/video-factory/projects/{pid}/animatics· scope media:readList AnimaticsGET/video-factory/projects/{pid}/bible· scope media:readGet BiblePUT/video-factory/projects/{pid}/bible· scope media:writeSave BibleApply the change; the impact it had is recorded on the version, not just previewed.POST/video-factory/projects/{pid}/bible:impact· scope media:writePreview Bible ImpactWhat this change would touch - BEFORE it propagates (plan 7.2). Read-only.POST/video-factory/projects/{pid}/board· scope media:writeDraw BoardGET/video-factory/projects/{pid}/board· scope media:readGet BoardPOST/video-factory/projects/{pid}/claim· scope media:writeClaimTake custody of a film whose maker has left. Only that - see the refusals.GET/video-factory/projects/{pid}/composition· scope media:readCompositionWhat the latest cut actually contains - and therefore what the certificate is allowed to say.POST/video-factory/projects/{pid}/conform· scope media:writeRead TimelineRead a timeline that came back from an editor. Nothing is applied - this says what is in it.GET/video-factory/projects/{pid}/conforms· scope media:readList ConformsGET/video-factory/projects/{pid}/continuity· scope media:readContinuityPOST/video-factory/projects/{pid}/continuity-review· scope media:writeStart ReviewSamples frames from every rendered take and asks a vision model what it sees. Off the request: three frames a shot on a twenty-shot film is minutes of GPU.GET/video-factory/projects/{pid}/continuity-reviews· scope media:readList ReviewsA review a person cannot find again is a review that did not happen. Reloading the page must bring back the findings they had not decided on yet.GET/video-factory/projects/{pid}/cost· scope media:readProject CostWhat this film has taken out of the machine.GET/video-factory/projects/{pid}/custody· scope media:readCustodyWho controls this film, who made it, and whether either of those is a problem.GET/video-factory/projects/{pid}/delivery· scope media:readPreviewThe film against its own script, recomputed now. Nothing is stored.POST/video-factory/projects/{pid}/delivery· scope media:writeIssueKeep a dated copy, against the script version it was measured on - so a later rewrite cannot silently invalidate a report somebody has already read and acted on.GET/video-factory/projects/{pid}/delivery-reports· scope media:readList ReportsGET/video-factory/projects/{pid}/entities· scope media:readList EntitiesPOST/video-factory/projects/{pid}/entities· scope media:writeAdd EntityPOST/video-factory/projects/{pid}/entities:extract· scope media:writeExtract EntitiesPull the characters, locations and props out of the source/treatment. Everything lands as DRAFT - a human approves each identity pack (and its rights) before shots may use it.GET/video-factory/projects/{pid}/fit· scope media:readGet FitJust the fit report, for the panel that has to show it beside the shot list.POST/video-factory/projects/{pid}/footage· scope media:writeIngest FootageBring in a clip somebody actually shot. What ffprobe finds beats what the uploader says, and the rights declaration travels with the file from here to the certificate.GET/video-factory/projects/{pid}/footage· scope media:readList FootageGET/video-factory/projects/{pid}/grades· scope media:readList GradesGET/video-factory/projects/{pid}/graph· scope media:readProduction GraphThe whole graph in one call for the Story step.POST/video-factory/projects/{pid}/handover· scope media:writeHandoverGive this film to somebody else. The ordinary path, taken while you are still here.GET/video-factory/projects/{pid}/lipsync· scope media:readList LipsyncPOST/video-factory/projects/{pid}/locks· scope media:writeLock RoutePOST/video-factory/projects/{pid}/look· scope media:writeStart ReportMeasure every shot in the cut and say which ones stand apart from the rest.GET/video-factory/projects/{pid}/look-reports· scope media:readList ReportsGET/video-factory/projects/{pid}/note-carries· scope media:readList CarriesGET/video-factory/projects/{pid}/parts· scope media:readParts RoutePOST/video-factory/projects/{pid}/parts/{part_id}:ai-edit· scope media:writeAi Edit RouteGET/video-factory/projects/{pid}/pitch.md· scope media:readPitch PackageThe one-document pitch a human hands to a producer: what it is, what it costs to render, who and what is in it, where the rights stand, and what is still open.GET/video-factory/projects/{pid}/plan· scope media:readProduction PlanPOST/video-factory/projects/{pid}/presence· scope media:writePresence RouteGET/video-factory/projects/{pid}/scenes· scope media:readList ScenesPOST/video-factory/projects/{pid}/scenes· scope media:writeAdd ScenePOST/video-factory/projects/{pid}/scenes:breakdown· scope media:writeBreakdown ScenesTreatment + beats -> the scene list, each naming the entities it uses. Replaces DRAFT scenes only; approved scenes are never overwritten by the assistant.GET/video-factory/projects/{pid}/score· scope media:readScore ReportDoes the music fit the film? Analyses the piece the first time it is asked about, and reuses that analysis for every cut afterwards - the phrase boundaries belong to the piece, not the cut.POST/video-factory/projects/{pid}/score:listen· scope media:writeListenListen to the piece on this project's cut and find where it can end.POST/video-factory/projects/{pid}/source:ingest· scope media:writeIngest SourceBring the source in behind the rights gate. A Publishers manuscript must be confirmed owned/licensed there, or the person declares the adaptation right here - either way the declaration is stored verbatim and quoted by the certificate.POST/video-factory/projects/{pid}/speak· scope media:writeSpeak LinesSay every line in the script, in the voice cast for its speaker.GET/video-factory/projects/{pid}/speech· scope media:readGet SpeechEvery line take, and the fit report - which is the part worth reading.POST/video-factory/projects/{pid}/suggestions· scope media:writeSuggest RouteGET/video-factory/projects/{pid}/voices· scope media:readGet CastWho speaks in this film, who is cast, and who has nobody.POST/video-factory/projects/{pid}/voices· scope media:writeCast VoicePOST/video-factory/projects/{pid}/voices-reconfirmed· scope media:writeReconfirm VoicesSay, by name, that you have checked the people who lent their voices still agree. A name and not a checkbox, for the same reason a subtitle track records who read it through: a box that somebody ticked is not a claim anybody made, and this one is about a real person's voice.GET/video-factory/projects/{project_id}· scope media:readGet ProjectDELETE/video-factory/projects/{project_id}· scope media:writeDelete ProjectPOST/video-factory/projects/{project_id}/approvals· scope media:writeRequest ApprovalPOST/video-factory/projects/{project_id}/brief· scope media:writeSave BriefPOST/video-factory/projects/{project_id}/collaborators· scope media:writeAdd CollaboratorGET/video-factory/projects/{project_id}/comments· scope media:readList CommentsPOST/video-factory/projects/{project_id}/comments· scope media:writeAdd CommentPOST/video-factory/projects/{project_id}/cuts· scope media:writeSave CutPOST/video-factory/projects/{project_id}/cuts:auto· scope media:writeAuto CutA first cut from the approved (or latest ready) take of every shot, in shot order.POST/video-factory/projects/{project_id}/description:compile· scope media:writeCompile DescriptionPOST/video-factory/projects/{project_id}/generations:estimate· scope media:writeEstimatePOST/video-factory/projects/{project_id}/moment:compile· scope media:writeCompile MomentPOST/video-factory/projects/{project_id}/script· scope media:writeSave ScriptPOST/video-factory/projects/{project_id}/script:draft· scope media:writeDraft ScriptPOST/video-factory/projects/{project_id}/share-links· scope media:writeCreate Share LinkGET/video-factory/projects/{project_id}/sharing· scope media:readGet SharingPATCH/video-factory/projects/{project_id}/sharing· scope media:writePatch SharingPOST/video-factory/projects/{project_id}/shots· scope media:writeAdd ShotPOST/video-factory/projects/{project_id}/shots:plan· scope media:writePlan ShotsScript -> shot list with contracts. Replaces PLANNED shots that have no takes yet; shots that already rendered are kept (the person's work is never thrown away by a re-plan).POST/video-factory/projects/{project_id}:recover· scope media:writeRecoverPlan MVF3 recovery. For every take of this project that an interrupted process left in preparing / generating / post_processing: if ComfyUI still has the job, leave it (the person can wait or cancel); if ComfyUI finished it and still holds the output, collect it; otherwise mark it failed_retryable with the reason, so Retry re-queues it under the same declaration.POST/video-factory/projects/{project_id}:restore· scope media:writeRestore ProjectPOST/video-factory/projects/{project_id}:score-in-song-factory· scope media:writeScore In Song FactoryOpens a Song Factory project whose brief carries this video's music intent and whose video reference is the review export (SF6 measures cuts/energy and the Studio gets hit points).POST/video-factory/projects/{project_id}:takedown· scope media:writeTakedown ProjectGET/video-factory/queue· scope media:readRead QueueWhat the machine is doing, and where your work sits in it. Your own jobs come back in full. Everything else is a COUNT - how many are ahead of you is a number you need; whose they are and what they are for is not.GET/video-factory/queue/history· scope media:readRead HistoryYour own finished jobs, with what they really cost.GET/video-factory/queue/machine· scope media:readRead MachineThe card itself, and what each kind of work has really cost on it. Every number here is about the MACHINE, not about this caller - which is why it reads nothing tenant-scoped. Authentication is still required: what the Forge is holding and how long a render takes on it is not a public fact.POST/video-factory/queue/{jid}/cancel· scope media:writeCancelLeave the line. A running job is not stopped from here - see the note.POST/video-factory/reports· scope media:writeFile ReportGET/video-factory/reports· scope media:readList ReportsPATCH/video-factory/reports/{report_id}· scope media:writePatch ReportPATCH/video-factory/scenes/{sid}· scope media:writePatch SceneDELETE/video-factory/scenes/{sid}· scope media:writeDelete SceneGET/video-factory/search· scope media:readDo SearchFind a film by something inside it.GET/video-factory/search/recent· scope media:readRecentYour own recent searches, and how long the live scan really took. The timing is the point: it is the number that will one day decide whether a pre-built index is worth the staleness it brings, and it should be measured by then rather than argued about.DELETE/video-factory/share-links/{link_id}· scope media:writeRevoke Share LinkGET/video-factory/shared-with-me· scope media:readShared With MeGET/video-factory/shot-readings/{reading_id}/frame· scope media:readReading FrameThe frame a finding came from. Without this route the report can say "check it yourself" and then give a person no way to look, which is worse than saying nothing.PATCH/video-factory/shots/{shot_id}· scope media:writePatch ShotDELETE/video-factory/shots/{shot_id}· scope media:writeDelete ShotPOST/video-factory/shots/{shot_id}/scene· scope media:writeAttach Shot To SceneComplete the graph: shot -> scene (and so -> entities, beats, source).GET/video-factory/storage· scope media:readAuditWhat is really on the disk, what nothing points at, and what points at nothing.GET/video-factory/storage/audits· scope media:readList AuditsPOST/video-factory/storage/mark-ghosts· scope media:writeMark GhostsMake the rows tell the truth. Marks; never deletes. Only exports, generations and (since 567) voice takes carry a status this can be said in. The rest are reported and left exactly as they are: inventing a state on a table to record a fact about the disk would be a worse lie than the broken link. Voice takes were added because for them the broken link WAS the worse lie - a 'ready' take is what ev…POST/video-factory/storage/sweep· scope media:writeSweepDelete the orphans, and only the orphans. `confirm_bytes` has to match what the audit said it would free. If the disk changed between looking and deciding, this refuses - somebody agreeing to free 40 MB has not agreed to free 4 GB, and a delete that silently took more than was shown would be unrecoverable.GET/video-factory/subtitles/{tid}· scope media:readGet TrackPOST/video-factory/subtitles/{tid}/reviewed· scope media:writeMark ReviewedRecord that somebody who speaks the language has been through it. Machine output and reviewed output are different things, and a track that cannot tell them apart will eventually be shipped as though a person had checked it.GET/video-factory/subtitles/{tid}/vtt· scope media:readTrack VttPOST/video-factory/suggestions/{sid}:accept· scope media:writeAccept RoutePOST/video-factory/suggestions/{sid}:reject· scope media:writeReject RouteDELETE/video-factory/voices/{cast_id}· scope media:writeUncastGET/video-factory/withdrawals/{wid}· scope media:readGet Withdrawal
Cinema - ८५ doors - cinema:read, cinema:write
Film and video production end to end, from script to a delivered package.
POST/cinema-payments/licenses· scope cinema:writeCreate LicenseGET/cinema-payments/licenses· scope cinema:readList LicensesGET/cinema-payments/purchases/mine· scope cinema:readMy FilmsTHE VIEWER'S OWN FILMS (P5, 2026-09-24). A screening or a licence a person paid for, across every studio - the first customer-side view this pillar has had: until today a buyer held only the link the checkout returned. A purchase names its person by `created_by` when they were signed in and otherwise by the email they paid with - the standing ownership rule (own_identity), decided here per row on …POST/cinema-payments/releases· scope cinema:writeCreate ReleaseMints the release for a DELIVERY-READY project with a completed render - the pipeline's own gate, not a parallel one. Born UNPUBLISHED: a film goes in front of the world by the toggle below, never by a row existing.GET/cinema-payments/releases· scope cinema:readList ReleasesPATCH/cinema-payments/releases/{release_id}· scope cinema:writeUpdate ReleasePOST/cinema-payments/releases/{release_id}/publish· scope cinema:writeToggle Release PublishThe pillar's go-live. PAY-TO-OPEN in the function body, off->on only - unpublishing is always the tenant's right.POST/cinema-payments/webhook· scope cinema:writeCinema Stripe WebhookSignature or refusal, captured events only - education's shape exactly.PATCH/cinema/adaptation-plans/{plan_id}· scope cinema:writeUpdate Adaptation PlanPOST/cinema/adaptation-plans/{plan_id}/breakdown-shots· scope cinema:writeBreakdown ShotsAI-drafts a real, structured scene-by-scene, shot-by-shot breakdown from an approved adaptation_plan's own beat_sheet, grounded strictly in that text - never invents a location/character/event beyond what the plan already states. Every drafted scene/shot lands as a real row via the exact same create_scene/create_shot functions the manual Scenes & Shots UI already uses (called in-process, matching …GET/cinema/assets· scope cinema:readList AssetsPOST/cinema/assets· scope cinema:writeUpload AssetPOST/cinema/assets/generate-concept-art· scope cinema:writeGenerate Concept ArtReal FLUX.1-dev text-to-image generation (flux_image_engine.py) - not a Pillow typography card. Blocks on the real ComfyUI job (a single FLUX frame is fast, ~10-20s on this hardware, unlike video), then stores the result as a real cinema_asset - same enhance_image() pipeline every other image upload in this platform goes through.GET/cinema/assets/{asset_id}/download· scope cinema:readDownload AssetGET/cinema/audio-tracks· scope cinema:readList Audio TracksPOST/cinema/audio-tracks· scope cinema:writeCreate Audio TrackGET/cinema/available-manuscripts· scope cinema:readList Available ManuscriptsReal manuscripts the caller can actually see, via Publishers' own RLS - no separate cross-pillar permission check needed, the row-level policy on `manuscript` already scopes this correctly.GET/cinema/avatar-projects· scope cinema:readList Avatar ProjectsPOST/cinema/avatar-projects· scope cinema:writeCreate Avatar ProjectPOST/cinema/character-looks· scope cinema:writeCreate Character LookA look is a named, reusable COMBINATION of items - assembled once by a director, applied to many shots, exactly the reuse the Emperor described ("should be able to change or add clothes... behave like they are living in real life") rather than re-picking every item on every single shot.DELETE/cinema/character-looks/{look_id}· scope cinema:writeDelete Character LookGET/cinema/characters· scope cinema:readList CharactersPOST/cinema/characters· scope cinema:writeCreate CharacterPATCH/cinema/characters/{character_id}· scope cinema:writeUpdate CharacterLinks a character to an already-uploaded reference photo (a real cinema_asset - honest reference/moodboard image for the art department, NOT an AI-video identity-lock mechanism, see migration 073's own note) and/or a voice consent record. RLS scopes both id checks below - a non-staff attempt to link an asset/consent from a different project simply won't find the row.GET/cinema/characters/{character_id}/looks· scope cinema:readList Character LooksGET/cinema/delivery-packages· scope cinema:readList Delivery PackagesPOST/cinema/delivery-packages· scope cinema:writeCreate Delivery PackageDelivery readiness requires approval and validation (spec section 27) - real-enforced here: refuses to create a package until at least one review_cycle on this project has reached 'approved', matching the server-enforced gate pattern already proven for Publishers' production status gate and Education's certificate gate.DELETE/cinema/dubs/{dub_id}· scope cinema:writeDelete DubGET/cinema/dubs/{dub_id}/artifact· scope cinema:readDownload Dub ArtifactPATCH/cinema/dubs/{dub_id}/mark-reviewed· scope cinema:writeMark Dub ReviewedA human explicitly confirming the dubbed draft has been reviewed - never automatic, matching manuscript_translation's exact same pattern (and for the same reason: MT quality plus an unverified accent- adaptation claim means this can never be treated as finished without a human actually watching/listening to it first).GET/cinema/integrations· scope cinema:readList IntegrationsGET/cinema/projects· scope cinema:readList ProjectsPOST/cinema/projects· scope cinema:writeCreate ProjectGET/cinema/projects/{project_id}· scope cinema:readGet ProjectGET/cinema/projects/{project_id}/adaptation-plans· scope cinema:readList Adaptation PlansPOST/cinema/projects/{project_id}/adaptation-plans/propose· scope cinema:writePropose Adaptation PlanBook-to-Movie bridge (spec section 10): pulls a real Publishers manuscript this project is linked to (title/synopsis/chapters) and asks the AI to draft a logline/synopsis/treatment/beat-sheet grounded in the actual book content - never invents plot beyond what the manuscript contains, and never touches canon or rights status. Lands as a draft adaptation_plan for a human to review/edit/approve - th…PATCH/cinema/projects/{project_id}/status· scope cinema:writeTransition ProjectGET/cinema/projects/{project_id}/voice-consent-records· scope cinema:readList Consent RecordsPOST/cinema/projects/{project_id}/voice-consent-records· scope cinema:writeCreate Consent RecordReal consent capture - typed full legal name + the exact statement agreed to, timestamped, plus an optional uploaded signed document. Whoever's voice is being cloned must be the one whose name and consent this record carries - a platform owner or project creator cannot consent on behalf of a real third person's voice, only for their own or with that person's own documented authorization.GET/cinema/provenance· scope cinema:readList ProvenancePOST/cinema/provenance· scope cinema:writeCreate ProvenanceSigning status stays 'unsigned' by default - real C2PA signing is a separately-gated real infrastructure decision (spec section 26/37), same discipline already used for Media/Publishers provenance.GET/cinema/render-attempts/{attempt_id}/artifact· scope cinema:readDownload ArtifactPOST/cinema/render-attempts/{attempt_id}/dubs· scope cinema:writeCreate DubGET/cinema/render-attempts/{attempt_id}/dubs· scope cinema:readList DubsGET/cinema/render-jobs· scope cinema:readList Render JobsPOST/cinema/render-jobs· scope cinema:writeCreate Render JobGET/cinema/render-jobs/{job_id}/attempts· scope cinema:readList Render AttemptsPOST/cinema/render-jobs/{job_id}/run-assemble· scope cinema:writeRun Scene AssemblyReal ffmpeg-based timeline assembly (see cinema_video_engine.assemble_ scene_shots) - stitches a scene's already-COMPLETED shot renders into one continuous crossfaded video. This is the real answer to "Cinema generates individual clips but nothing stitches them together" - see CLAUDE.md's entry on the DaVinci Resolve question for the full real reasoning on why this is ffmpeg-based rather than Reso…POST/cinema/render-jobs/{job_id}/run-mock· scope cinema:writeRun Mock RenderDrives a queued render job through a real mock render attempt - running -> completed (real ffmpeg-generated MP4 artifact) or failed (real ffmpeg error captured, retry-able by re-queuing the job).POST/cinema/render-jobs/{job_id}/run-real· scope cinema:writeRun Real RenderDrives a queued render job through a REAL local AI video generation (ComfyUI + LTX-2.3-22B, running on The Forge's own GPU) - not the ffmpeg mock placeholder above. Same job/attempt lifecycle as run-mock; blocks synchronously while ComfyUI renders (a single 5s 720p clip took ~20-45s on this hardware during verification), matching the mock renderer's own synchronous shape rather than adding a separ…PATCH/cinema/render-jobs/{job_id}/status· scope cinema:writeTransition Render JobGET/cinema/reviews· scope cinema:readList ReviewsPOST/cinema/reviews· scope cinema:writeCreate ReviewPATCH/cinema/reviews/{review_id}· scope cinema:writeUpdate ReviewGET/cinema/scenes· scope cinema:readList ScenesPOST/cinema/scenes· scope cinema:writeCreate ScenePOST/cinema/scenes/{scene_id}/batch-render· scope cinema:writeBatch Render SceneReal batch render: queues and runs one real render_job per shot in this scene, in shot-number order, consuming real Imperial Fuel credits from the linked Publisher house's real fuel account as it goes (the exact same balance-check + house_fuel_ledger-insert shape publishers_services_budget.py's own checkout_cart already uses). Honest, not all-or-nothing: if the house can't afford every shot, it re…GET/cinema/scenes/{scene_id}/batch-render-cost-estimate· scope cinema:readBatch Render Cost EstimateReal, non-fabricated cost preview - genuinely computed from the scene's real current shot count times the real, documented per-shot fuel cost for the chosen engine (never a hash-based/random placeholder number). Also reports the house's real current fuel balance so a user can see up front whether they can afford it, without spending anything - the same "check before you commit" shape as tools_fuel…GET/cinema/scenes/{scene_id}/continuity-check· scope cinema:readCheck Scene Wardrobe ContinuityThe real 'continuity department' feature the Emperor explicitly asked for ("actively track wardrobe continuity across a scene"). Fetches this scene's real shot-look assignments (RLS-scoped, same as every other read in this module) and runs the actual pure-logic checker from wardrobe_engine.py - see that module for the real, independently-verified algorithm.GET/cinema/scenes/{scene_id}/shot-look-assignments· scope cinema:readList Scene Shot Look AssignmentsThe real current assignments for a scene's shots - what the continuity checker itself reads, exposed separately so the UI can show what's actually assigned, not just flagged issues.GET/cinema/scripts· scope cinema:readList ScriptsPOST/cinema/scripts· scope cinema:writeCreate ScriptPOST/cinema/scripts/{script_id}/versions· scope cinema:writeSave Script VersionSnapshots every scene's content under this script's project into one JSON version - same pattern as Publishers' manuscript_version.POST/cinema/series-bibles/{bible_id}/movie-series· scope cinema:writePropose Movie SeriesSeries -> Movie series bridge. For every book in the series (Part 0) that doesn't already have a linked cinema_project, creates a new cinema_project (source_manuscript_id + series_bible_id set) via the exact same insert shape create_project already uses, then seeds its first adaptation_plan draft via the existing, unmodified propose_adaptation_plan - grounded in that specific book only, never a mi…GET/cinema/series-bibles/{bible_id}/movie-series· scope cinema:readList Movie Series ProjectsPUT/cinema/shot-look-assignments· scope cinema:writeSet Shot Look AssignmentUpsert on (shot_id, character_id) - a director re-assigning a character's look on a shot they already set replaces it, matching the real UNIQUE constraint on shot_look_assignment.GET/cinema/shots· scope cinema:readList ShotsPOST/cinema/shots· scope cinema:writeCreate ShotPOST/cinema/studios· scope cinema:writeCreate StudioGET/cinema/studios/mine· scope cinema:readGet My StudioGET/cinema/trailers· scope cinema:readList TrailersPOST/cinema/trailers· scope cinema:writeCreate TrailerGET/cinema/voice-calibration-scripts· scope cinema:readList Calibration ScriptsGET/cinema/voice-consent-records/{record_id}/document· scope cinema:readDownload Consent DocumentPATCH/cinema/voice-consent-records/{record_id}/revoke· scope cinema:writeRevoke Consent RecordGET/cinema/voice-consent-records/{record_id}/samples· scope cinema:readList Voice SamplesPOST/cinema/voice-consent-records/{record_id}/samples· scope cinema:writeUpload Voice SampleA real microphone recording (or uploaded sound byte) of the person reading a calibration passage. Can ONLY be attached to a consent record that is still ACTIVE, checked here - the real mechanical enforcement of "no voice clone without live consent".POST/cinema/voice-consent-records/{record_id}/synthesize· scope cinema:writeSynthesize NarrationReal DramaBox voice-cloned narration - the actual synthesis engine, gated the only way this platform ever gates voice cloning: an ACTIVE consent record and a real captured sample of THAT SAME consented person's voice, checked here, not just implied by the UI. Lands as a real cinema_asset (asset_type='audio') + an audio_track row, reusing the existing asset download path rather than a separate audi…POST/cinema/voice-consent-records/{record_id}/train-voice-lora· scope cinema:writeTrain Voice LoraReal per-voice LoRA training (VoxCPM2) against every real captured sample for this consent record - higher fidelity than DramaBox's zero-shot cloning, confirmed via direct testing against a real recorded voice. Blocks synchronously while training runs (real GPU time, can take several minutes - the underlying node's own docs warn "this process takes time"), matching this platform's existing synchro…DELETE/cinema/voice-samples/{sample_id}· scope cinema:writeDelete Voice SampleGET/cinema/voice-samples/{sample_id}/file· scope cinema:readGet Voice Sample FileGET/cinema/wardrobe-items· scope cinema:readList Wardrobe ItemsPOST/cinema/wardrobe-items· scope cinema:writeCreate Wardrobe ItemItem source is real, per the Emperor's own explicit answer: both AI-generated (via wardrobe_engine's FLUX.1 call) and tenant-uploaded are supported side by side - `generate_reference_image=true` triggers the former, an actual `reference_file` upload is the latter. Neither is required; an item can exist as pure text with no reference image at all.DELETE/cinema/wardrobe-items/{item_id}· scope cinema:writeDelete Wardrobe ItemGET/cinema/wardrobe-items/{item_id}/reference-image· scope cinema:readGet Wardrobe Item Image
Games - ७३ doors - games:read, games:write
Game worlds, levels and assets, and the studios that build and sell them.
PATCH/games/assets/{asset_id}· scope games:writeReview AssetGET/games/assets/{asset_id}/file· scope games:readDownload Asset FilePOST/games/bundles· scope games:writeCreate BundleSeveral worlds, ONE store product (the Education mixed-bundle idea carried over). Every world must be the caller's own; the product rides the same marketplace machinery - and therefore marketplace's own pay-to-open gate.GET/games/bundles· scope games:readList BundlesPOST/games/bundles/{bundle_id}/redeem· scope games:writeRedeem BundleThe single-world redeem's proof, verbatim: the REAL Medusa order must be THIS user's and must contain the bundle's product - then every bundled world grants its entitlement. Idempotent per world.PATCH/games/choices/{choice_id}· scope games:writeEdit ChoiceDELETE/games/choices/{choice_id}· scope games:writeDelete ChoiceRemoves the choice. What players already chose stays in their history - 228 snapshots the text at the moment of choosing and the history's link simply goes empty.PATCH/games/compute-jobs/{job_id}· scope games:writeUpdate Compute JobPATCH/games/entities/{entity_id}· scope games:writeReview EntityGET/games/entities/{entity_id}/looks· scope games:readList Entity LooksPOST/games/entity-looks· scope games:writeCreate Entity LookDELETE/games/entity-looks/{look_id}· scope games:writeDelete Entity LookGET/games/hardware-telemetry· scope games:readGet Hardware TelemetryDeliberately synthetic - spec §22 locks real GPU/VRAM telemetry to a separate runtime approval this Core pass doesn't have. These numbers are representative placeholders, not a live reading of The Forge.GET/games/integrations· scope games:readList IntegrationsWhat a studio can and cannot reach from this pillar, in its own words - never a list of product names Fitinty has not configured (LG15's walk, carried across).GET/games/my/worlds· scope games:readMy WorldsTHE PLAYER'S OWN WORLDS (P5, 2026-09-24). Every world this account holds an entitlement to, with the order that granted it and the door that opens it - the first customer-side view this pillar has had: a buyer saw the store's order and nothing that said which world was theirs. Read on the PLAYER'S OWN cursor through the entitlement's self arm (260) - the wall answers who the caller is, and nothing…POST/games/narrative-sources/{source_id}/extract· scope games:writeExtract EntitiesPOST/games/playtest/{token}/claim· scope games:writeClaim PlaytestA signed-in tester claims the SENT link; play opens for the invite's life. The claim insert runs on the system cursor (the tester is not an org member - the E8 token-as-credential posture), fenced by the live invite itself.POST/games/playtest/{token}/feedback· scope games:writeLeave FeedbackThe tester's verdict lands on the studio's desk. Only a claimant of a live or recently-ended invite may file; one row per filing (a tester may file more than once as the build changes - each is a real reading).POST/games/playtests· scope games:writeMint Playtest InviteGET/games/playtests· scope games:readList PlaytestsThe desk: every invite with its claims and its feedback so far.GET/games/playtests/feedback· scope games:readPlaytest FeedbackPOST/games/playtests/{invite_id}/close· scope games:writeClose PlaytestEnds the playtest now: every claim stops opening the world the moment the invite expires. Feedback already filed stays - it was a real reading.DELETE/games/playthroughs/{playthrough_id}· scope games:writeRestart PlaythroughPOST/games/playthroughs/{playthrough_id}/choose· scope games:writeMake ChoiceGET/games/playthroughs/{playthrough_id}/history· scope games:readPlaythrough HistoryGET/games/projects/{project_id}· scope games:readGet ProjectPATCH/games/projects/{project_id}· scope games:writeUpdate ProjectGET/games/projects/{project_id}/assets· scope games:readList AssetsPOST/games/projects/{project_id}/assets· scope games:writeCreate AssetGET/games/projects/{project_id}/compute-jobs· scope games:readList Compute JobsPOST/games/projects/{project_id}/compute-jobs· scope games:writeCreate Compute JobGET/games/projects/{project_id}/cross-pillar-links· scope games:readList Cross Pillar LinksPOST/games/projects/{project_id}/cross-pillar-links· scope games:writeCreate Cross Pillar LinkGET/games/projects/{project_id}/entities· scope games:readList EntitiesGET/games/projects/{project_id}/narrative-sources· scope games:readList Narrative SourcesPOST/games/projects/{project_id}/narrative-sources· scope games:writeCreate Narrative SourceGET/games/projects/{project_id}/provenance· scope games:readList ProvenancePOST/games/projects/{project_id}/provenance· scope games:writeCreate ProvenanceGET/games/projects/{project_id}/worlds· scope games:readList WorldsPOST/games/projects/{project_id}/worlds· scope games:writeCreate WorldPUT/games/scene-look-assignments· scope games:writeSet Scene Look AssignmentUpsert on (scene_definition_id, entity_id) - records which look an entity wears in a given scene's static preview, matching the real UNIQUE constraint on scene_look_assignment.GET/games/scenes/{scene_definition_id}/look-assignments· scope games:readList Scene Look AssignmentsPOST/games/scenes/{scene_id}/generate-synthetic-preview· scope games:writeGenerate Synthetic PreviewGET/games/scenes/{scene_id}/level· scope games:readGet LevelPOST/games/scenes/{scene_id}/level· scope games:writeWrite LevelA level written by a person, with no model involved - add its choices next.PATCH/games/scenes/{scene_id}/level· scope games:writeEdit LevelPOST/games/scenes/{scene_id}/level/choices· scope games:writeAdd ChoicePOST/games/scenes/{scene_id}/level/generate· scope games:writeGenerate LevelPOST/games/series-bibles/{bible_id}/game-series· scope games:writePropose Game SeriesSeries -> Game series bridge. For every book in the series (Part 0) that doesn't already have a linked game_project, creates a new game_project with the REAL series_bible_id/source_manuscript_id FK columns set (the fix migration 038's own comment deferred), then seeds it with a real narrative_source built from that specific book's own chapters and runs the existing, unmodified entity-extraction AI…GET/games/series-bibles/{bible_id}/game-series· scope games:readList Game Series ProjectsPOST/games/studios· scope games:writeCreate StudioGET/games/studios/mine· scope games:readGet My Studio EndpointGET/games/studios/{studio_id}/lore-voice-profiles· scope games:readList Lore Voice ProfilesPOST/games/studios/{studio_id}/lore-voice-profiles· scope games:writeCreate Lore Voice ProfileGET/games/studios/{studio_id}/projects· scope games:readList ProjectsPOST/games/studios/{studio_id}/projects· scope games:writeCreate ProjectGET/games/wardrobe-items· scope games:readList Wardrobe ItemsPOST/games/wardrobe-items· scope games:writeCreate Wardrobe ItemDELETE/games/wardrobe-items/{item_id}· scope games:writeDelete Wardrobe ItemGET/games/wardrobe-items/{item_id}/reference-image· scope games:readGet Wardrobe Item ImageGET/games/world-listings/by-product/{product_id}· scope games:readWorld Listing By ProductOrder page: 'this line item is a game world -> Play now'. Any signed-in user may look up which world a product unlocks (gwml_any_user_read); playing still requires the redeem step to prove the order is theirs.PATCH/games/worlds/{world_id}· scope games:writeUpdate WorldGET/games/worlds/{world_id}/access· scope games:readMy World AccessWhat the play UI needs in one call: can I play, is it for sale, at what product.GET/games/worlds/{world_id}/leaderboard· scope games:readWorld LeaderboardREAL completed playthroughs, fastest first - finishing is worth something. Open to anyone who may play the world (member, buyer, or live playtester).GET/games/worlds/{world_id}/level-map· scope games:readLevel MapThe studio's view of every level in the world, with what would stop a player finishing.POST/games/worlds/{world_id}/marketplace-listing· scope games:writeList World On MarketplaceGET/games/worlds/{world_id}/marketplace-listing· scope games:readGet World ListingThe world page's 'Sell full access' panel state (null = not listed).GET/games/worlds/{world_id}/playthrough· scope games:readMy PlaythroughPOST/games/worlds/{world_id}/playthrough/start· scope games:writeStart PlaythroughPOST/games/worlds/{world_id}/redeem· scope games:writeRedeem World PurchaseTurn a real purchase into play access. The order is fetched from Medusa with the admin token and checked: it must be THIS user's (email match) and must contain the world's listed product. Idempotent.GET/games/worlds/{world_id}/scenes· scope games:readList ScenesPOST/games/worlds/{world_id}/scenes· scope games:writeCreate SceneGET/games/worlds/{world_id}/wishlist· scope games:readWorld WishlistThe studio's own list, with conversion MEASURED: which wishers went on to own the world. Emails of wishers are the tenant's audience - exclusive, never resold; the conversion join is a count, not a surveillance feed.
Marketing - १४३ doors - marketing:read, marketing:write
Campaigns, content and the scouting that feeds them.
GET/marketing/acquisition· scope marketing:readAcquisition GaugeTHE MACHINE'S GAUGE - the Emperor's screen. Every figure a measurement; platform-wide, so it answers only to the owner role.GET/marketing/action-log· scope marketing:readList Action LogA LOG IS THE ONE LIST NOBODY PAGES BACK THROUGH, and the one most often read as complete - "that rule never fired" is a sentence people say about a log that simply stopped.POST/marketing/action-log· scope marketing:writeRecord ActionPOST/marketing/ad-bridge· scope marketing:writeCreate Ad PackagePOST/marketing/ad-bridge/{pkg_id}/consent· scope marketing:writeConsent Ad PackageConsent is WORDS the human typed - what they agreed this campaign may spend per day - recorded on the row with their name (the G2 law for money).POST/marketing/ad-bridge/{pkg_id}/export· scope marketing:writeExport Ad PackageThe hand-off: the package as plain text to paste into the ad platform.POST/marketing/ad-bridge/{pkg_id}/launch· scope marketing:writeLaunch Ad PackageGET/marketing/ad-bridge/{pkg_id}/reading· scope marketing:readAd ReadingDeclared vs recorded, and what the closed loop can honestly say. Zeros are real; what cannot be measured is STATED, never estimated.POST/marketing/ad-bridge/{pkg_id}/record-spend· scope marketing:writeRecord SpendBookkeeping of what YOU spent out there - a measurement of the past, never an instruction to spend. No money moves through this door.POST/marketing/ads/spend-gate/authorize· scope marketing:writeAuthorize AdsHalf of the advertising lock. AD_SPEND_MODE, the profile's own gate, a cap above zero, the recorded consent words and a credential are the rest - this opens none of them.POST/marketing/ads/spend-gate/revoke· scope marketing:writeRevoke AdsGET/marketing/affiliate-compliance· scope marketing:readList Affiliate DisclosuresPOST/marketing/affiliate-compliance· scope marketing:writeCreate Affiliate DisclosurePATCH/marketing/affiliate-compliance/{disclosure_id}/approve· scope marketing:writeApprove Affiliate DisclosureGET/marketing/affiliate-desk· scope marketing:readAffiliate DeskThe whole channel system on one read: every network's standing (credential state, last sync, latest reported money), the playbook's next action in words, offers with their real click counts, and what the agents have to work with.POST/marketing/affiliate-desk/import-report· scope marketing:writeImport ReportPUT/marketing/affiliate-program· scope marketing:writeSet Affiliate ProgramPOST/marketing/affiliate/accrue· scope marketing:writeAccrue Affiliate EarningsRead-through minting: one earning per REAL billing event of an org whose member was referred, idempotent by event id. No daemon - the Emperor's hand (or the screen that shows him the desk) runs it.GET/marketing/affiliate/earnings· scope marketing:readMy Affiliate EarningsThe affiliate's own desk (RLS scopes: owners see all, others see theirs).POST/marketing/affiliate/earnings/{earning_id}/record-payment· scope marketing:writeRecord Affiliate PaymentThe Rule-3 wall, publishers-royalty shape: a human paid outside; this door records that fact and nothing else.POST/marketing/agent-runs· scope marketing:writeRun Multi Agent AmpGET/marketing/agent-runs· scope marketing:readList Agent RunsGET/marketing/attribution· scope marketing:readAttributionGET/marketing/audience· scope marketing:readMy AudienceThe seller's book: counts and the latest members. A None count is a dead read, never a confident zero.GET/marketing/audience-segments· scope marketing:readList Audience SegmentsPOST/marketing/audience-segments· scope marketing:writeCreate Audience SegmentDELETE/marketing/audience/{member_id}· scope marketing:writeRemove MemberThe removal right: hard delete, because a person asking to be forgotten is not asking to be archived.GET/marketing/brand-listening· scope marketing:readList Brand MentionsPOST/marketing/brand-listening· scope marketing:writeCreate Brand MentionPOST/marketing/brand-listening/{mention_id}/draft-response· scope marketing:writeDraft ResponseAI drafts a recommended response only - never posted automatically, matching this project's 'AI drafts, never authoritative' pattern used everywhere else.POST/marketing/campaign-plan· scope marketing:writeAdd Plan ItemPOST/marketing/campaign-plan/items/{item_id}/status· scope marketing:writeSet Plan Item StatusGET/marketing/campaign-plan/{campaign_id}· scope marketing:readCampaign PlanGET/marketing/campaigns· scope marketing:readList CampaignsPOST/marketing/campaigns· scope marketing:writeCreate CampaignGET/marketing/campaigns/{campaign_id}· scope marketing:readGet CampaignPATCH/marketing/campaigns/{campaign_id}/status· scope marketing:writeSet Campaign StatusGET/marketing/capability-gates· scope marketing:readList GatesPOST/marketing/capability-gates/relock· scope marketing:writeRelock GatePOST/marketing/capability-gates/unlock· scope marketing:writeUnlock GatePOST/marketing/content-factory· scope marketing:writeGenerate ContentGET/marketing/content-factory· scope marketing:readList Content AssetsGET/marketing/content-factory/{asset_id}/image· scope marketing:readDownload Content ImagePATCH/marketing/content-factory/{asset_id}/status· scope marketing:writeSet Content StatusGET/marketing/cross-pillar-links· scope marketing:readList Cross Pillar LinksPOST/marketing/cross-pillar-links· scope marketing:writeCreate Cross Pillar LinkGET/marketing/email/drafts· scope marketing:readList Email DraftsPOST/marketing/email/drafts· scope marketing:writeCreate Email DraftPATCH/marketing/email/drafts/{draft_id}/status· scope marketing:writeSet Email Draft StatusPOST/marketing/forge/friction· scope marketing:writeCreate FrictionGET/marketing/forge/friction· scope marketing:readList FrictionPOST/marketing/forge/friction/{friction_id}/generate-solution· scope marketing:writeGenerate SolutionGET/marketing/forge/solutions· scope marketing:readList SolutionsPOST/marketing/forge/solutions/{solution_id}/match· scope marketing:writeMatch SolutionGET/marketing/forge/solutions/{solution_id}/matches· scope marketing:readList MatchesPOST/marketing/forge/vassals· scope marketing:writeCreate VassalGET/marketing/forge/vassals· scope marketing:readList VassalsGET/marketing/frequency-caps· scope marketing:readList Frequency CapsPOST/marketing/frequency-caps· scope marketing:writeSet Frequency CapPOST/marketing/gated-execution· scope marketing:writeRequest Gated ExecutionThe real enforcement point: checks the gate's actual current status and either blocks (real, not decorative) or produces a real simulated outcome. Every attempt - blocked or simulated - lands as a real, permanent audit row, regardless of outcome.GET/marketing/gated-execution· scope marketing:readList Gated ExecutionsGET/marketing/growth-command· scope marketing:readGrowth CommandGET/marketing/guardrails· scope marketing:readList GuardrailsPOST/marketing/guardrails· scope marketing:writePropose GuardrailEvery guardrail is versioned - a new proposal is a new row with an incrementing version, matching Tools' own resource_policy pattern.PATCH/marketing/guardrails/{guardrail_id}/toggle· scope marketing:writeToggle GuardrailGET/marketing/hook-lab/tests· scope marketing:readList Hook TestsPOST/marketing/hook-lab/tests· scope marketing:writeCreate Hook TestPOST/marketing/hook-lab/tests/{hook_test_id}/complete· scope marketing:writeComplete Hook TestDeclares a winner by real conversion-rate comparison, refusing to declare one from insufficient data - identical discipline to Media's own Hook Lab experiment completion.POST/marketing/hook-lab/tests/{hook_test_id}/start· scope marketing:writeStart Hook TestGET/marketing/hook-lab/tests/{hook_test_id}/variants· scope marketing:readList Hook VariantsPOST/marketing/hook-lab/variants· scope marketing:writeCreate Hook VariantPATCH/marketing/hook-lab/variants/{variant_id}/record· scope marketing:writeRecord Variant MetricsGET/marketing/integrations· scope marketing:readList IntegrationsPOST/marketing/knowledge-chunks· scope marketing:writeCreate Knowledge ChunkGET/marketing/knowledge-chunks· scope marketing:readList Knowledge ChunksPOST/marketing/knowledge-chunks/search· scope marketing:writeSemantic SearchReal pgvector cosine-distance search - never a keyword LIKE match dressed up as 'AI search'. Grounds the multi-agent AMP core in real stored facts.GET/marketing/loyalty/balances· scope marketing:readLoyalty BalancesEvery customer's points, COMPUTED from captured money on the rails at read minus recorded redemptions - a mirror of money drifts, a computation cannot.PUT/marketing/loyalty/program· scope marketing:writePut ProgramPOST/marketing/loyalty/redeem· scope marketing:writeRecord RedemptionThe tenant hands over a reward and RECORDS it - never automatic, never a charge; over-redemption is refused against the computed balance.POST/marketing/mail/drain· scope marketing:writeDrainPOST/marketing/mail/nurture/enroll· scope marketing:writeEnroll NurturePOST/marketing/mail/queue· scope marketing:writeQueue CampaignExpand an APPROVED draft against the org's consented audience. Rows are born 'held' - the mail_outbox law - and nothing transmits here. Unsubscribed and opted-out addresses are never minted a row; the response says how many.GET/marketing/mail/sends· scope marketing:readList SendsThe truth of one draft's sends - counts by status, never dressed up.GET/marketing/migration-plans· scope marketing:readList Migration PlansPOST/marketing/migration-plans· scope marketing:writeCreate Migration PlanGET/marketing/narrative-foundational-template· scope marketing:readFoundational TemplateThe CORE_DECREE's own foundational-narrative text, to pre-fill a new source.GET/marketing/narrative-seeds· scope marketing:readList SeedsPOST/marketing/narrative-seeds/generate· scope marketing:writeGenerate SeedPOST/marketing/narrative-sources· scope marketing:writeCreate SourceGET/marketing/narrative-sources· scope marketing:readList SourcesPATCH/marketing/narrative-sources/{source_id}· scope marketing:writeUpdate SourceGET/marketing/objection-dojo/scenarios· scope marketing:readList Objection ScenariosPOST/marketing/objection-dojo/scenarios· scope marketing:writeCreate Objection ScenarioPATCH/marketing/objection-dojo/scenarios/{scenario_id}/review· scope marketing:writeReview Objection ScenarioGET/marketing/pioneer-sequences· scope marketing:readList Pioneer SequencesPOST/marketing/pioneer-sequences· scope marketing:writeCreate Pioneer SequenceGET/marketing/playbooks· scope marketing:readList PlaybooksPOST/marketing/playbooks· scope marketing:writeCreate PlaybookPATCH/marketing/playbooks/{playbook_id}/status· scope marketing:writeSet Playbook StatusPUT/marketing/press-kit· scope marketing:writeUpdate Press KitPOST/marketing/profile· scope marketing:writeCreate ProfileGET/marketing/profile/mine· scope marketing:readGet My Profile EndpointGET/marketing/profile/{profile_id}/members· scope marketing:readList MembersPOST/marketing/profile/{profile_id}/members· scope marketing:writeAdd MemberGET/marketing/provenance· scope marketing:readList Provenance RecordsPOST/marketing/provenance· scope marketing:writeCreate Provenance RecordGET/marketing/routing-policies· scope marketing:readList Routing PoliciesPOST/marketing/routing-policies· scope marketing:writeCreate Routing PolicyLocal-first and zero-recurring-cost policies remain preferred where feasible (spec section 25) - no model/external API is activated without review, so every new policy lands unapproved until a separate approve call, matching Tools' own resource_policy approval gate.POST/marketing/routing-policies/{policy_id}/approve· scope marketing:writeApprove Routing PolicyGET/marketing/scout/hypotheses· scope marketing:readList Trend HypothesesPOST/marketing/scout/hypotheses· scope marketing:writeCreate Trend HypothesisGET/marketing/scout/observations· scope marketing:readList Scout ObservationsPOST/marketing/scout/observations· scope marketing:writeCreate Scout ObservationGET/marketing/scout/sources· scope marketing:readList Scout SourcesPOST/marketing/scout/sources· scope marketing:writeCreate Scout SourceGET/marketing/seo-geo/briefs· scope marketing:readList Landing Page BriefsPOST/marketing/seo-geo/briefs· scope marketing:writeCreate Landing Page BriefPOST/marketing/seo-geo/briefs/{brief_id}/generate· scope marketing:writeGenerate Landing PageReal AI-drafted pSEO/GEO content: page copy + real schema.org JSON-LD structured data, grounded in the real knowledge base (Research, reused from the Content Factory pass) and the brief's own linked topic cluster. Never auto-published - lands as a draft the same as every other AI- generated artifact in this pillar; rendering it as a real, public, crawlable page is a separate, deliberately-locked s…GET/marketing/seo-geo/clusters· scope marketing:readList Seo ClustersPOST/marketing/seo-geo/clusters· scope marketing:writeCreate Seo ClusterGET/marketing/seo-geo/storefront-pages· scope marketing:readStorefront Seo PagesTHE SEO PREVIEW READS THE REAL STOREFRONT (2026-09-11) - the profile's storefront as a search engine reads it (`storefront_seo.reading`). It takes NO address: the pages are the sitemap's own list for this profile's business, so nobody can point the reader at anything else.GET/marketing/social-accounts· scope marketing:readList AccountsPOST/marketing/social-accounts/manual· scope marketing:writeConnect ManualPaste-a-token: the road that works TODAY, before any OAuth app review. The token is encrypted the moment it arrives and never travels back out.GET/marketing/social-accounts/oauth/{network}/start· scope marketing:readOauth StartGET/marketing/social-accounts/summary· scope marketing:readFooter SummaryWhat the dashboard footer shows: the caller's orgs' connected networks and footer links, plus the connect door's path.DELETE/marketing/social-accounts/{account_id}· scope marketing:writeDisconnectPUT/marketing/social-autopilot· scope marketing:writeSet AutopilotPUT/marketing/social-footer· scope marketing:writeSet Footer LinksThe org's footer row, replaced wholesale - what you save is exactly what every footer prints.POST/marketing/social-posts/{post_id}/mark-posted· scope marketing:writeMark PostedPOST/marketing/social-posts/{post_id}/transmit· scope marketing:writeTransmit Social PostThe machine's posting hand - which exists now, and answers to three locks. With any lock shut it refuses in plain words and posts NOTHING; mark-posted (the human hand) remains the everyday road.GET/marketing/social-queue· scope marketing:readList Social PostsPOST/marketing/social-queue· scope marketing:writeCreate Social PostPATCH/marketing/social-queue/{post_id}/status· scope marketing:writeSet Social Post StatusPOST/marketing/social/send-gate/authorize· scope marketing:writeAuthorize SocialHalf of the social lock. SOCIAL_POST_MODE is the other half and this does not touch it.POST/marketing/social/send-gate/revoke· scope marketing:writeRevoke SocialPOST/marketing/social/transmit-due· scope marketing:writeTransmit DueTHE AGENTS' POSTING HAND - drain-shaped, never a timer in this codebase (wiring it to the droplet's doctor timer is a launch-checklist line that waits for the gate to open anyway). Same three locks as the transmit door; scope is the CALLER'S orgs by RLS, each org needing autopilot AND its outreach gate.GET/marketing/strategy· scope marketing:readGet StrategyPUT/marketing/strategy· scope marketing:writePut StrategyPOST/marketing/testimonials· scope marketing:writeSubmit TestimonialA tenant offers words about Fitinty. The consent words - what THEY agreed to, e.g. that this may appear publicly with their name - are recorded on the row, the G2 law. Nothing shows publicly until the Emperor approves.POST/marketing/testimonials/{t_id}/approve· scope marketing:writeApprove TestimonialPOST/tenant-referrals· scope marketing:writeMint CodeAny signed-in person may carry a Fitinty link - tenants recommending the platform ARE the platform's oldest acquisition channel.GET/tenant-referrals· scope marketing:readMy CodesThe referrer's desk: counts only, never people - who signed up is not the referrer's to know. Conversion = the referred user now holds a PAID or FOUNDING org, computed at read on the system cursor.POST/tenant-referrals/claim· scope marketing:writeClaim ReferralThe signup's one-time claim, called by the app shell after a referred signup. UNIQUE(user_id) makes attribution a decision, not an argument; a user who already claimed (or was never referred) is a quiet no-op.
Tools - ५२ doors - tools:read, tools:write
Shared machinery: compute, translation, rendering and the fuel they burn.
GET/tools/alert-rules· scope tools:readList Alert RulesPOST/tools/alert-rules· scope tools:writeCreate Alert RulePATCH/tools/alert-rules/{rule_id}· scope tools:writeUpdate Alert RuleGET/tools/alerts· scope tools:readList AlertsPOST/tools/alerts/{alert_id}/acknowledge· scope tools:writeAcknowledge AlertGET/tools/backups· scope tools:readList BackupsPOST/tools/backups· scope tools:writeRecord BackupPOST/tools/backups/{backup_id}/verify-restore· scope tools:writeVerify RestoreA restore-TEST record, not an actual restore execution - marks the backup as proven recoverable. Matches spec section 23: 'A backup is not accepted until restore is tested.'GET/tools/cross-pillar-map· scope tools:readCross Pillar MapThe spec asks Tools to visualize demand from Cinema, Media, Education, Investors, Services, Offices, Publishers, Marketplace, Manufacturing, Logistics, Farms - none of those pillars produce a real compute-demand signal yet (no job queue of their own feeds into this platform's queue_job table), so this reports real BUILD status per pillar (built vs not-yet-built) rather than fabricating demand perc…GET/tools/fabrication· scope tools:readList Fabrication ProjectsPOST/tools/fabrication· scope tools:writeCreate Fabrication ProjectGET/tools/fuel/account· scope tools:readGet AccountGet-or-create the caller's fuel account (grants 50 Pioneer credits on first touch).GET/tools/fuel/accounts· scope tools:readList AccountsPOST/tools/fuel/check· scope tools:writeCheck FuelThe 'no free labor' pre-flight gate: enough Credit_Balance for this task? No debit.POST/tools/fuel/consume· scope tools:writeConsume FuelDebit fuel for an authorized heavy-compute task. Blocks (402) if insufficient.GET/tools/fuel/cost-table· scope tools:readCost TablePOST/tools/fuel/grant· scope tools:writeGrant FuelGET/tools/fuel/ledger· scope tools:readGet LedgerGET/tools/idle-factory/simulations· scope tools:readList Idle Factory SimulationsPOST/tools/idle-factory/simulations· scope tools:writeRun Idle Factory SimulationReal logic against the platform's own latest approved resource policy: available capacity is 100% minus the policy's reserved_pct (falls back to a conservative 30% reserve if no policy is approved yet). A job that would exceed available capacity is preempted, never silently allowed - matching the spec's own preemption-order rule (section 19: no rental workload outranks platform safety).GET/tools/integrations· scope tools:readList IntegrationsGET/tools/inventory· scope tools:readList InventoryPOST/tools/inventory· scope tools:writeCreate Inventory ItemPOST/tools/inventory/forecasts· scope tools:writeCreate ForecastA real, deterministic forecast rule (not a hand-picked number): project 30 days of usage from the gap between on_hand and reorder_level, propose replenishment only if the projected level would fall below the reorder point. Confidence is fixed at 0.5 (synthetic-data honesty, not a real ML forecast) until a real usage-history model exists.GET/tools/inventory/{inventory_item_id}/forecasts· scope tools:readList ForecastsGET/tools/maintenance· scope tools:readList MaintenancePOST/tools/maintenance· scope tools:writePropose MaintenancePOST/tools/maintenance/{task_id}/approve· scope tools:writeApprove MaintenanceGET/tools/omniforge/projects· scope tools:readList Omniforge ProjectsPOST/tools/omniforge/projects· scope tools:writeCreate Omniforge ProjectGET/tools/policy· scope tools:readList PoliciesPOST/tools/policy· scope tools:writePropose PolicyNo production policy is activated until benchmarked and approved (spec section 10) - a proposed policy always lands unapproved; use the separate approve endpoint to activate it. This is the same propose-then-approve shape as maintenance_task below.POST/tools/policy/{policy_id}/approve· scope tools:writeApprove PolicyGET/tools/queue· scope tools:readList QueuePOST/tools/queue/synthetic· scope tools:writeCreate Synthetic JobEvery job created here is explicitly synthetic - no real compute is ever dispatched, matching the spec's own `running_mock`/`completed_mock` status naming (section 11).PATCH/tools/queue/{job_id}· scope tools:writeTransition JobGET/tools/registry· scope tools:readList ToolsPOST/tools/registry· scope tools:writeRegister ToolDELETE/tools/registry/{tool_id}· scope tools:writeDelete ToolPOST/tools/render-calibration· scope tools:writeRecord CalibrationOne REAL measured run - the controlled test's own numbers, by the owner's hand. The note must say what was rendered; a calibration row with no story is a number nobody can audit.GET/tools/render-calibration· scope tools:readList CalibrationGET/tools/render-calibration/estimate· scope tools:readEstimateThe Emperor's pricing arithmetic: what a feature of this length would burn at the measured median rate, and what that burn costs at the pack shelf's own per-minute prices. Labeled for what it is - a cost basis, not advice.GET/tools/resource-estimates· scope tools:readList EstimatesPOST/tools/resource-estimates· scope tools:writeCreate EstimateGET/tools/security-policies· scope tools:readList Security PoliciesPOST/tools/security-policies· scope tools:writeCreate Security PolicyGET/tools/telemetry/history· scope tools:readTelemetry HistoryGET/tools/telemetry/latest· scope tools:readCapture TelemetryCaptures a fresh sample on every call rather than serving a cached row - telemetry is meant to be live. Real GPU read (nvidia-smi) and real host read (psutil) are attempted independently and merged; whatever can't be read for real falls back to clearly-labeled synthetic values for that field only, and `is_synthetic` is true only if EVERY field came from the synthetic generator (a partial real read…GET/tools/vram-reserve· scope tools:readVram Reserve StateLive Sovereign VRAM Reserve + thermal state (read-only monitoring).POST/tools/vram-reserve/admit· scope tools:writeVram Reserve AdmitAdmission control: would allocating `requested_gb` for a `lane`-class task breach the Sovereign VRAM Reserve? Returns the honest decision + math. 'omega' seizure is owner-only (L426).POST/tools/vram-reserve/enforce· scope tools:writeVram Reserve EnforceRun one enforcement pass (owner/Emperor authority). Reads live state; if thermally throttling or in VRAM Imperial-Red, pauses non-critical SYNTHETIC queue jobs (running_mock -> paused) - never real GPU processes - and records a durable vram_reserve_event either way (the decree's audit trail; a RED-FLAG event if the reserve was breached).GET/tools/vram-reserve/events· scope tools:readVram Reserve Events
Offices - १२५ doors - offices:read, offices:write
Places of business: domains, delivery and the operations behind an office.
GET/offices· scope offices:readList OfficesPOST/offices· scope offices:writeCreate OfficePOST/offices/agreement-templates· scope offices:writeCreate Agreement TemplatePOST/offices/agreements· scope offices:writeCreate AgreementPOST/offices/agreements/{agreement_id}/assemble· scope offices:writeAssemble AgreementFills the template's placeholders from the RECORD (practice, client, terms) and the practice's answers. What cannot be resolved stays a placeholder and is LISTED - never guessed. Draft agreements only. Metered: Try refused in words, one use per document.POST/offices/agreements/{agreement_id}/balance· scope offices:writePay BalancePOST/offices/agreements/{agreement_id}/deposit· scope offices:writePay DepositPOST/offices/agreements/{agreement_id}/deposit-terms· scope offices:writeSet Deposit TermsThe practice's dial: "30% to start, the balance on completion" is 3000 bps.POST/offices/agreements/{agreement_id}/instalments· scope offices:writeSchedule InstalmentsThe practice's hand schedules N monthly parts - each a pending row with its own pay door; the agreement funds when the LAST one captures. Refused once any money moved.POST/offices/agreements/{agreement_id}/pay· scope offices:writeStart Agreement CheckoutThe CLIENT'S pay door on their own agreement. Stripe hosts the page; the client pays the PRACTICE; Fitinty's cut rides Stripe's own split. One purchase per agreement - a cancelled checkout leaves a pending row this door REUSES rather than doubles.GET/offices/agreements/{agreement_id}/payments· scope offices:readAgreement PaymentsEvery part on the rail for one agreement - the practice or the named client.POST/offices/agreements/{agreement_id}/set-terms· scope offices:writeSet Agreement TermsThe practice names the price and the client - the two facts the pay door charges by. amount_placeholder stays untouched display history.PATCH/offices/agreements/{agreement_id}/status· scope offices:writeSet Agreement StatusPOST/offices/bookings· scope offices:writeCreate BookingGET/offices/bookings/mine· scope offices:readList My BookingsPOST/offices/bookings/{booking_id}/pay· scope offices:writePay ConsultationOF8 gap 1 - THE CONSULTATION PAY DOOR. A priced consultation type was stored and shown and never charged. One more KIND on the one rail: client-only, the booking's own price (read from its consultation type at the moment of paying), pending-before-Stripe on the practice's account, reused never doubled, flipped by the one webhook - and a paid consultation CONFIRMS a requested booking (the flip says…PATCH/offices/bookings/{booking_id}/status· scope offices:writeSet Booking StatusGET/offices/client-workspaces/{workspace_id}/journey· scope offices:readGet Client JourneyReal Customer Journey Viewer (Workspace tools manifest id 60) - stitches every real touchpoint a client_workspace already has (intake, bookings, agreements, projects, deliverables, delivery reviews) into one chronological timeline. No new schema - every row here is real data already produced by an existing Offices flow, just never presented as a single view before. Relies entirely on each source t…POST/offices/consultations· scope offices:writeCreate ConsultationPATCH/offices/consultations/{session_id}· scope offices:writeUpdate ConsultationPOST/offices/deliverables· scope offices:writeUpload DeliverableGET/offices/deliverables/{deliverable_id}/download· scope offices:readDownload DeliverableGET/offices/deliverables/{deliverable_id}/reviews· scope offices:readList Delivery ReviewsPOST/offices/delivery-reviews· scope offices:writeCreate Delivery ReviewAlways a real human reviewer_user_id (the calling user) - never an AI decision, matching the spec's explicit "AI cannot approve its own output" requirement.GET/offices/documents/{document_id}/download· scope offices:readDownload DocumentPATCH/offices/documents/{document_id}/sharing· scope offices:writeSet Document SharingThe practice's hand: a document attached to a room is shared unless it says otherwise.POST/offices/handoff-packages· scope offices:writeCreate Handoff PackagePOST/offices/intake-forms· scope offices:writeCreate Intake FormPOST/offices/intake-submissions· scope offices:writeCreate Intake SubmissionPATCH/offices/intake-submissions/{submission_id}/status· scope offices:writeSet Intake StatusGET/offices/integrations· scope offices:readList IntegrationsGET/offices/intel· scope offices:readPractice Intel ReportFitinty's metered instrument: one report, one use. Try refused in words.GET/offices/invoices/mine· scope offices:readMy InvoicesThe client's own invoices across every practice, with their pay doors.POST/offices/invoices/{invoice_id}/pay· scope offices:writePay InvoiceThe client's pay door on their own invoice - a pending part on the ONE rail, reused never doubled; the webhook flips bookkeeping's row to paid.POST/offices/leads· scope offices:writeAdd Manual LeadGET/offices/leads· scope offices:readList LeadsThe desk: every lead with its DERIVED band and reasons, plus conversion MEASURED from what really happened - never estimated.GET/offices/leads/reading· scope offices:readLead ReadingConversion by source and by response speed - measured from the desk's own rows.POST/offices/leads/sync-intakes· scope offices:writeSync Intake LeadsTHE ONE MINT DOOR from intakes: every submission becomes a lead exactly once (unique on source_ref), fenced to the caller's own practice. A repeat press mints nothing and says so.POST/offices/leads/{lead_id}/contacted· scope offices:writeMark Lead ContactedTHE RESPONSE CLOCK stops here, once: the first time the practice reached out. A second press changes nothing - the first contact is the one measured.POST/offices/leads/{lead_id}/merge-into/{survivor_id}· scope offices:writeMerge LeadThe practice's HAND merges a duplicate: the merged lead closes with the reason written, points at the survivor, and is excluded from every count. Nothing is deleted, nothing is merged by a machine.POST/offices/leads/{lead_id}/refer-to/{office_id}· scope offices:writeRefer Lead OutA practice passes a lead it cannot serve to another Fitinty practice, with the client's recorded consent words. Once per (lead, practice). The lead itself stays where it is.GET/offices/leads/{lead_id}/screen· scope offices:readLead ScreenOne lead, fully vetted: the band with its words, the conflict screen with its findings, the duplicates stated. All derived now; nothing stored.PATCH/offices/leads/{lead_id}/status· scope offices:writeSet Lead StatusOF2: closing takes a REASON in words (a decline that teaches the desk nothing is wasted), and the first move to 'engaged' starts the response clock if nothing did.POST/offices/meeting-records· scope offices:writeCreate Meeting RecordPOST/offices/meeting-records/{record_id}/summarize· scope offices:writeSummarize MeetingAI drafts a summary + decision log from the uploaded notes/transcript only, same "drafts, never authoritative" pattern as every other AI feature in this project - a human reviews and can edit before it's treated as the real record.GET/offices/mine· scope offices:readGet My Office EndpointPOST/offices/mobile-kits· scope offices:writeRegister Mobile KitPATCH/offices/mobile-kits/{kit_id}/check· scope offices:writeCheck Mobile KitGET/offices/network· scope offices:readNetwork DeskGET/offices/network/directory· scope offices:readNetwork DirectoryPractices a lead can be passed to: those that switched their page on (the same visibility the storefront honours), never the caller's own. Names, categories, regions - nothing more.GET/offices/network/reading· scope offices:readNetwork ReadingPOST/offices/network/{referral_id}/accept· scope offices:writeAccept Network ReferralThe receiving practice's hand: the lead is born on its own desk (source 'network'), once. The source lead's facts are read on the system cursor - the consent words recorded on the referral are exactly what authorises that.POST/offices/network/{referral_id}/decline· scope offices:writeDecline Network ReferralPOST/offices/payments/webhook· scope offices:writeOffices Stripe WebhookThe pillar's own verified door, Services' shape exactly: signature or refusal, captured events only, idempotent by the pending->paid guard plus the fee ledger's unique payment id.POST/offices/payments/{payment_id}/pay· scope offices:writePay PaymentThe generic client door for a pending part (instalment, invoice) the client is named on.POST/offices/projects· scope offices:writeCreate ProjectGET/offices/projects/{project_id}· scope offices:readGet ProjectGET/offices/projects/{project_id}/deliverables· scope offices:readList DeliverablesPATCH/offices/projects/{project_id}/due-date· scope offices:writeSet Project Due DateOF9 - the deadline as a date the agent can measure. NULL clears it (stated, never invented).POST/offices/projects/{project_id}/expenses· scope offices:writeAdd ExpensePOST/offices/projects/{project_id}/invoice· scope offices:writeMint InvoiceUninvoiced billable time x rate + billable expenses + a fixed fee -> ONE ar_invoice (bookkeeping's row, 'sent', due by the practice's terms) with the office_invoice link. Entries and expenses are stamped with the invoice so they can never be billed twice. Refused in words when there is nothing to bill.GET/offices/projects/{project_id}/invoices· scope offices:readList Matter InvoicesPATCH/offices/projects/{project_id}/status· scope offices:writeSet Project StatusGET/offices/projects/{project_id}/tasks· scope offices:readList TasksPOST/offices/projects/{project_id}/time· scope offices:writeAdd TimeGET/offices/projects/{project_id}/time· scope offices:readList TimePOST/offices/projects/{project_id}/time/{entry_id}/stop· scope offices:writeStop TimePOST/offices/retainers· scope offices:writeCreate RetainerA DRAFT tenant_plan (pillar 'offices') + the link naming the ONE client. Publishing passes recurring billing's own pay-to-open gate; subscribing is the named client's door.GET/offices/retainers· scope offices:readList RetainersPOST/offices/retainers/{retainer_id}/publish· scope offices:writePublish RetainerPOST/offices/retainers/{retainer_id}/subscribe· scope offices:writeSubscribe RetainerThe NAMED client's door - recurring billing's checkout on the practice's own rail. Anyone else is refused; the plan never appears on the public storefront list.GET/offices/room· scope offices:readMy RoomsEvery room the signed-in person holds, across practices - their own cursor, the tables' own client arms; nothing here is read on the practice's behalf.POST/offices/room/{workspace_id}/appointments/{booking_id}/cancel· scope offices:writeCancel My AppointmentOF9 - the client cancels their own appointment from the room. A paid consultation is cancelled too, and the sentence is honest: nothing is refunded by a machine - the practice decides and refunds by hand on its own account.POST/offices/room/{workspace_id}/appointments/{booking_id}/reschedule· scope offices:writeReschedule My AppointmentOF9 - a new time must be one the practice actually offers (the ONE slot arithmetic). An unpaid appointment goes back to 'requested' for the practice to confirm; a paid one stays confirmed - the money was the yes.GET/offices/room/{workspace_id}/appointments/{booking_id}/slots· scope offices:readMy Appointment SlotsThe times the practice offers for this appointment's type on a day - so the room can reschedule onto a real slot, from the ONE slot arithmetic.POST/offices/room/{workspace_id}/messages· scope offices:writePost MessageA human writes - the client of the room, or the practice's staff. The insert policy is the fence; the author_kind is decided here from who the caller is, never claimed.GET/offices/room/{workspace_id}/messages· scope offices:readRoom MessagesPOST/offices/room/{workspace_id}/refer· scope offices:writeRefer A PersonTHE REFERRAL DESK. The client's words about the person's consent are recorded as the fact they are; the practice gets ONE lead (source 'referral') through the one writer.POST/offices/room/{workspace_id}/satisfaction· scope offices:writeRateAfter the work: one score per matter (accepted or closed), the client's own hand. This is the last link of the moat's measurement - lead -> meeting -> funded -> satisfied.POST/offices/room/{workspace_id}/tasks/{task_id}/done· scope offices:writeFinish Owed TaskThe client finishes something they owed. Fenced in code (their room, a task owed by the client, on a matter of that room); the write rides the system cursor because the task table's update arm belongs to the practice.POST/offices/status-updates· scope offices:writePost Status UpdatePOST/offices/tasks· scope offices:writeCreate TaskPATCH/offices/tasks/{task_id}/status· scope offices:writeSet Task StatusGET/offices/verticals· scope offices:readList Practice VerticalsThe catalog, one authoritative source - the web mirror is generated from the same file, and a guard fails when it drifts.GET/offices/{office_id}· scope offices:readGet OfficeGET/offices/{office_id}/agreement-templates· scope offices:readList Agreement TemplatesGET/offices/{office_id}/agreements· scope offices:readList AgreementsGET/offices/{office_id}/bookings· scope offices:readList BookingsPATCH/offices/{office_id}/category· scope offices:writeSet Practice CategoryThe picker: one of the verticals (the DB CHECK widened WITH this list in 521). The kit, the band reader and the storefront label all follow it.GET/offices/{office_id}/client-workspaces· scope offices:readList Client WorkspacesPOST/offices/{office_id}/client-workspaces· scope offices:writeCreate Client WorkspaceOF6 walk: the desk opens a room by the client's EMAIL (the hub's uuid field stays for callers that hold one). A client must already hold a Fitinty account - stated in words.GET/offices/{office_id}/client-workspaces/mine· scope offices:readGet My Client WorkspaceGET/offices/{office_id}/consultation-types· scope offices:readList Consultation TypesPOST/offices/{office_id}/consultation-types· scope offices:writeCreate Consultation TypePATCH/offices/{office_id}/consultation-types/{type_id}· scope offices:writeUpdate Consultation TypeGET/offices/{office_id}/consultations· scope offices:readList ConsultationsGET/offices/{office_id}/dns-plan· scope offices:readDns PlanPOST/offices/{office_id}/dns-provision· scope offices:writeDns ProvisionPOST/offices/{office_id}/dns-verify· scope offices:writeDns VerifyGET/offices/{office_id}/documents· scope offices:readList DocumentsPOST/offices/{office_id}/documents· scope offices:writeUpload DocumentGET/offices/{office_id}/domain-configuration· scope offices:readGet Domain ConfigPATCH/offices/{office_id}/domain-configuration· scope offices:writeUpdate Domain ConfigGET/offices/{office_id}/email-configuration· scope offices:readGet Email ConfigPATCH/offices/{office_id}/email-configuration· scope offices:writeUpdate Email ConfigPOST/offices/{office_id}/email-verify· scope offices:writeEmail VerifyGET/offices/{office_id}/handoff-packages· scope offices:readList Handoff PackagesGET/offices/{office_id}/hours· scope offices:readGet HoursPUT/offices/{office_id}/hours· scope offices:writeSet HoursReplaced wholesale - a week is one statement, not a diff a later reader has to replay. OF10 - with ?professional_user_id= it is THAT member's week (and makes them bookable); an empty list removes it (they leave the public picker).GET/offices/{office_id}/intake-forms· scope offices:readList Intake FormsGET/offices/{office_id}/intake-submissions· scope offices:readList Intake SubmissionsGET/offices/{office_id}/kit· scope offices:readPractice KitPOST/offices/{office_id}/kit/mint· scope offices:writeMint Practice KitMints the vertical's kit as DRAFTS: an intake form, consultation types (inactive), an engagement-letter template with the disclosure. Building is free; switching any door on is pay-to-open's business. Skips what already exists by title, so minting twice doubles nothing.GET/offices/{office_id}/lobby· scope offices:readGet LobbyPATCH/offices/{office_id}/lobby· scope offices:writeUpdate LobbyGET/offices/{office_id}/meeting-records· scope offices:readList Meeting RecordsGET/offices/{office_id}/members· scope offices:readList Office MembersPOST/offices/{office_id}/members· scope offices:writeAdd Office MemberGET/offices/{office_id}/mobile-kits· scope offices:readList Mobile KitsGET/offices/{office_id}/professionals· scope offices:readList ProfessionalsThe roster with who is BOOKABLE (has their own week) - the desk's picker.GET/offices/{office_id}/profile· scope offices:readGet Office ProfilePATCH/offices/{office_id}/profile· scope offices:writeUpdate Office ProfileGET/offices/{office_id}/projects· scope offices:readList ProjectsGET/offices/{office_id}/slots· scope offices:readDesk SlotsThe desk's view of open times - the same arithmetic the public door offers.GET/offices/{office_id}/status-updates· scope offices:readList Status Updates
Bank - ५४ doors - bank:read, bank:write
Accounts, ledgers and transactions - the books beneath the money.
POST/bank· scope bank:writeCreate ProfileGET/bank/currencies· scope bank:readList CurrenciesPOST/bank/disputes· scope bank:writeCreate DisputeGET/bank/disputes/evidence/{evidence_id}/download· scope bank:readDownload Dispute EvidenceGET/bank/disputes/{case_id}/evidence· scope bank:readList Dispute EvidencePOST/bank/disputes/{case_id}/evidence· scope bank:writeUpload Dispute EvidencePATCH/bank/disputes/{case_id}/status· scope bank:writeUpdate Dispute StatusPOST/bank/fee-policies· scope bank:writeCreate Fee PolicyPATCH/bank/fee-policies/{policy_id}/approve· scope bank:writeApprove Fee PolicyPATCH/bank/identity-gates/{gate_id}· scope bank:writeUpdate Identity GateRefuses, and names the one door. A second place to say "verified" is a second answer, and two answers to a regulator's question is the defect CO4 was written to end.GET/bank/integrations· scope bank:readList IntegrationsPOST/bank/ledger/journals/synthetic· scope bank:writeCreate Synthetic JournalPOST/bank/ledger/journals/{journal_id}/reverse· scope bank:writeReverse JournalPosted entries are immutable (spec section 11) - a reversal is a brand new journal whose entries swap debit/credit from the original, referencing the original via reverses_entry_id. The original journal/entries are never edited or deleted.POST/bank/locations· scope bank:writeCreate Merchant LocationGET/bank/mine· scope bank:readGet My Profile EndpointGET/bank/payments/authorization· scope bank:readGet AuthorizationPOST/bank/payments/authorization· scope bank:writeRecord AuthorizationOwner-only. Records the Emperor's attestation that legal counsel has confirmed real-money go-live. This is the ONLY way the legal gate is ever set — no automated path creates it.POST/bank/payments/authorization/revoke· scope bank:writeRevoke AuthorizationOwner-only. Revokes any active go-live authorization (real payments revert to not-authorized).GET/bank/payments/readiness· scope bank:readReadinessPOST/bank/payouts· scope bank:writeCreate PayoutPATCH/bank/payouts/{payout_id}/advance· scope bank:writeAdvance PayoutAdvances a payout one real step through its state machine, enforcing the actual KYC/tax gate states along the way - no real payout instruction is ever sent (spec section 18: 'No real payout instruction'), this only tracks the mock lifecycle.PATCH/bank/payouts/{payout_id}/status· scope bank:writeSet Payout StatusPOST/bank/pos-profiles· scope bank:writeCreate Pos ProfilePOST/bank/qr-payments/simulate· scope bank:writeSimulate Qr PaymentPATCH/bank/qr-payments/{qr_id}/status· scope bank:writeUpdate Qr Payment StatusPOST/bank/reconciliation/run· scope bank:writeRun ReconciliationPOST/bank/settlements/calculate· scope bank:writeCalculate SettlementPOST/bank/transactions/simulate· scope bank:writeSimulate TransactionPATCH/bank/transactions/{txn_id}/status· scope bank:writeUpdate Transaction StatusPATCH/bank/trust-scores/{snapshot_id}/appeal· scope bank:writeAppeal Trust ScorePATCH/bank/trust-scores/{snapshot_id}/review-appeal· scope bank:writeReview Trust Score AppealPOST/bank/webhooks/simulate· scope bank:writeSimulate WebhookGET/bank/{profile_id}· scope bank:readGet ProfileGET/bank/{profile_id}/disputes· scope bank:readList DisputesGET/bank/{profile_id}/fee-policies· scope bank:readList Fee PoliciesGET/bank/{profile_id}/identity-gates· scope bank:readList Identity GatesReads the ONE gate, for the organization this merchant belongs to.GET/bank/{profile_id}/ledger/accounts· scope bank:readList Ledger AccountsGET/bank/{profile_id}/ledger/journals· scope bank:readList Ledger JournalsGET/bank/{profile_id}/locations· scope bank:readList Merchant LocationsGET/bank/{profile_id}/members· scope bank:readList MembersPOST/bank/{profile_id}/members· scope bank:writeAdd MemberGET/bank/{profile_id}/payouts· scope bank:readList PayoutsGET/bank/{profile_id}/pos-profiles· scope bank:readList Pos ProfilesGET/bank/{profile_id}/purse· scope bank:readGet PurseGET/bank/{profile_id}/qr-payments· scope bank:readList Qr PaymentsGET/bank/{profile_id}/reconciliation· scope bank:readList Reconciliation RunsGET/bank/{profile_id}/settlements· scope bank:readList SettlementsGET/bank/{profile_id}/transactions· scope bank:readList TransactionsGET/bank/{profile_id}/trust-policies· scope bank:readList Trust PoliciesGET/bank/{profile_id}/trust-scores· scope bank:readList Trust ScoresPOST/bank/{profile_id}/trust-scores/compute· scope bank:writeCompute Trust ScoreGET/bank/{profile_id}/wealth· scope bank:readList Wealth SnapshotsPOST/bank/{profile_id}/wealth/snapshot· scope bank:writeGenerate Wealth SnapshotGET/bank/{profile_id}/webhook-events· scope bank:readList Webhook Events
Treasury - १०७ doors - treasury:read, treasury:write
Reserves, payroll, settlement and reimbursement.
POST/treasury· scope treasury:writeCreate ProfilePOST/treasury/annual-exports· scope treasury:writeCreate Annual ExportPATCH/treasury/annual-exports/{pkg_id}/status· scope treasury:writeSet Annual Export StatusPOST/treasury/benefit-pools· scope treasury:writeCreate Benefit PoolPOST/treasury/benefit-pools/entries· scope treasury:writeCreate Benefit Pool EntryPOST/treasury/contractor-payouts/simulate· scope treasury:writeSimulate Contractor PayoutPATCH/treasury/contractor-payouts/{payout_id}/status· scope treasury:writeSet Contractor Payout StatusPOST/treasury/contractor-profiles· scope treasury:writeCreate Contractor ProfileGET/treasury/contractor-profiles/{profile_id}/1099/{year}· scope treasury:readSimulated 1099A SIMULATED 1099-shaped summary - `contractor_1099_figures`, the one arithmetic.POST/treasury/fee-policies· scope treasury:writeCreate Fee PolicyPATCH/treasury/fee-policies/{policy_id}/approve· scope treasury:writeApprove Fee PolicyGET/treasury/integrations· scope treasury:readList IntegrationsPOST/treasury/ledger/journals/synthetic· scope treasury:writeCreate Synthetic JournalPOST/treasury/ledger/journals/{journal_id}/reverse· scope treasury:writeReverse JournalPosted entries are immutable (spec section 11) - a reversal is a brand new journal whose entries swap debit/credit from the original, referencing the original via reverses_entry_id. The original journal/entries are never edited or deleted.POST/treasury/member-equity· scope treasury:writeCreate Member Equity EntryPOST/treasury/member-resolutions· scope treasury:writeCreate Member ResolutionPATCH/treasury/member-resolutions/{res_id}/adopt· scope treasury:writeAdopt Member ResolutionGET/treasury/mine· scope treasury:readGet My Profile EndpointGET/treasury/my-pay· scope treasury:readMy PayPOST/treasury/owner-draws/propose· scope treasury:writePropose Owner DrawReal reserve checks, not decorative text: a draw is proposed with tax_reserve_check and maintenance_reserve_check computed live from the tenant's own real approved reserve-policy balances against the requested amount - genuinely blocking, not just informational.PATCH/treasury/owner-draws/{draw_id}/status· scope treasury:writeSet Owner Draw StatusApproval is real record-keeping only - spec section 15/27: no real transfer ever occurs.POST/treasury/payouts· scope treasury:writeCreate PayoutPATCH/treasury/payouts/{payout_id}/advance· scope treasury:writeAdvance PayoutAdvances a payout one real step through its state machine, enforcing the tenant's actual identity and tax gates through the platform's ONE definition of them (CO4, 563). This pillar read Compliance's system of record from the start rather than keeping a Treasury copy; Bank kept a copy, the two disagreed on live data, and 563 ended that. No real payout instruction is ever sent.PATCH/treasury/payouts/{payout_id}/status· scope treasury:writeSet Payout StatusPOST/treasury/payroll-items· scope treasury:writeCreate Payroll ItemReal, hand-verifiable net-pay math: base pay is either hours x rate (hourly workers) or the salary placeholder directly, gross = base + bonus, net = gross - tax - benefit - genuinely computed, never a fabricated number.POST/treasury/payroll-items/{item_id}/pull-benefits· scope treasury:writePull Benefit DeductionsBenefits flow into net FROM THE ELECTIONS THAT EXIST: the linked employee's elected plans with a numeric cost are summed into the deduction; a plan whose cost is only prose is NAMED as not deducted - never guessed from text.GET/treasury/payroll-items/{item_id}/stub· scope treasury:readPay StubThe pay stub, in words and numbers, stamped SIMULATED. Every figure is the row it came from - gross, each named tax line, the benefit deduction, net.POST/treasury/payroll-items/{item_id}/tax-lines· scope treasury:writeSet Tax LinesTHE ONE WRITER for an item's tax shape. Replaces the lines wholesale, keeps the item's tax_placeholder equal to their sum, and recomputes net - so every old reader (the run totals, the synthetic journal) stays honest without knowing about lines. An unknown kind is refused (422): sources are data, readers are code, and a kind with no reader would be a silent zero.POST/treasury/payroll-profiles· scope treasury:writeCreate Payroll ProfilePOST/treasury/payroll-profiles/{payroll_profile_id}/rail-sync· scope treasury:writePost Sync WorkerPATCH/treasury/payroll-profiles/{profile_id}/link-employee· scope treasury:writeLink Profile EmployeeThe bridge: the simulated profile names the REAL employee it mirrors. The employee must belong to the same organization as the treasury profile - a stub attached to a stranger would be a fabrication.GET/treasury/payroll-profiles/{profile_id}/w2/{year}· scope treasury:readSimulated W2A SIMULATED W-2-shaped summary of the year - `w2_figures`, the one arithmetic.POST/treasury/payroll-runs· scope treasury:writeCreate Payroll RunPATCH/treasury/payroll-runs/{run_id}/status· scope treasury:writeSet Payroll Run StatusAdvancing to posted_mock posts a real, hand-verifiable double-entry journal: debit Payroll Expense for the full gross total, credit Payroll Liabilities for the same amount, then a second entry moving the withheld-tax portion from Payroll Liabilities into Tax Reserve - no real employee payment ever occurs (spec section 12).POST/treasury/payroll-runs/{run_id}/transmit· scope treasury:writePost TransmitPOST/treasury/protection-pools· scope treasury:writeCreate Protection PoolPOST/treasury/protection-pools/entries· scope treasury:writeCreate Protection Pool EntryPOST/treasury/reconciliation/run· scope treasury:writeRun ReconciliationPOST/treasury/reimbursements· scope treasury:writeCreate ReimbursementGET/treasury/reimbursements/{req_id}/receipt· scope treasury:readDownload ReceiptPATCH/treasury/reimbursements/{req_id}/status· scope treasury:writeSet Reimbursement StatusMarking paid_mock posts a real ledger journal (debit Reimbursements expense, credit Cash) - still no real money movement, just the honest bookkeeping record of the mock payment (spec section 14).POST/treasury/reports/monthly· scope treasury:writeCreate Financial Reportgross_profit and net_income are always genuinely computed server-side from the caller's own revenue/cogs/expense inputs, never accepted as client-supplied totals.PATCH/treasury/reports/{report_id}/finalize· scope treasury:writeFinalize Financial ReportPOST/treasury/reserve-policies· scope treasury:writeCreate Reserve PolicyPATCH/treasury/reserve-policies/{policy_id}/approve· scope treasury:writeApprove Reserve PolicyPOST/treasury/reserves/simulate· scope treasury:writeSimulate Reserve AllocationPOST/treasury/settlements/calculate· scope treasury:writeCalculate SettlementPOST/treasury/tax-reserves· scope treasury:writeCreate Tax ReservePOST/treasury/webhooks/simulate· scope treasury:writeSimulate WebhookGET/treasury/{profile_id}· scope treasury:readGet ProfileGET/treasury/{profile_id}/annual-exports· scope treasury:readList Annual ExportsGET/treasury/{profile_id}/assets· scope treasury:readGet AssetsPOST/treasury/{profile_id}/assets· scope treasury:writePost AssetPOST/treasury/{profile_id}/assets/{asset_id}/dispose· scope treasury:writePost DisposeGET/treasury/{profile_id}/benefit-pools· scope treasury:readList Benefit PoolsGET/treasury/{profile_id}/close/{period}· scope treasury:readRead ClosePOST/treasury/{profile_id}/close/{period}· scope treasury:writeClose PeriodPOST/treasury/{profile_id}/close/{period}/reopen· scope treasury:writeReopen PeriodGET/treasury/{profile_id}/closes· scope treasury:readList ClosesGET/treasury/{profile_id}/contractor-payouts· scope treasury:readList Contractor PayoutsGET/treasury/{profile_id}/contractor-profiles· scope treasury:readList Contractor ProfilesGET/treasury/{profile_id}/fee-policies· scope treasury:readList Fee PoliciesGET/treasury/{profile_id}/feed· scope treasury:readGet FeedRead-through on open: reads the provider, posts what is new, then reads the books.POST/treasury/{profile_id}/feed/sync· scope treasury:writePost FeedGET/treasury/{profile_id}/inflows· scope treasury:readGet InflowsRead-through on open: brings the books up to date, then reads them. The age is stated.POST/treasury/{profile_id}/inflows/sync· scope treasury:writePost InflowsPOST/treasury/{profile_id}/investments· scope treasury:writePost InvestmentPOST/treasury/{profile_id}/investments/{investment_id}/mark· scope treasury:writePost MarkPOST/treasury/{profile_id}/investments/{investment_id}/sell· scope treasury:writePost SellGET/treasury/{profile_id}/ledger/accounts· scope treasury:readList Ledger AccountsGET/treasury/{profile_id}/ledger/journals· scope treasury:readList Ledger JournalsGET/treasury/{profile_id}/member-equity· scope treasury:readList Member EquityGET/treasury/{profile_id}/member-equity/summary· scope treasury:readMember Equity SummaryGET/treasury/{profile_id}/member-resolutions· scope treasury:readList Member ResolutionsGET/treasury/{profile_id}/members· scope treasury:readList MembersPOST/treasury/{profile_id}/members· scope treasury:writeAdd MemberGET/treasury/{profile_id}/nightly· scope treasury:readTenant NightlyA member reads when the timer last opened THEIR treasury and what it posted - nothing else.GET/treasury/{profile_id}/owner-draws· scope treasury:readList Owner DrawsGET/treasury/{profile_id}/payouts· scope treasury:readList PayoutsGET/treasury/{profile_id}/payroll-profiles· scope treasury:readList Payroll ProfilesGET/treasury/{profile_id}/payroll-rail· scope treasury:readGet RailPOST/treasury/{profile_id}/payroll-rail/employer· scope treasury:writePost EmployerGET/treasury/{profile_id}/payroll-runs· scope treasury:readList Payroll RunsGET/treasury/{profile_id}/protection-pools· scope treasury:readList Protection PoolsGET/treasury/{profile_id}/reconciliation· scope treasury:readList Reconciliation RunsGET/treasury/{profile_id}/reimbursements· scope treasury:readList ReimbursementsGET/treasury/{profile_id}/reports· scope treasury:readGet ReportsPOST/treasury/{profile_id}/reports· scope treasury:writePost ReportGET/treasury/{profile_id}/reports/monthly· scope treasury:readList Financial ReportsGET/treasury/{profile_id}/reports/{report_id}· scope treasury:readGet OneGET/treasury/{profile_id}/reports/{report_id}/export/{fmt}· scope treasury:readGet ExportR4 - the report as a file: csv, xlsx (native charts), pdf (the droplet's OnlyOffice), json.POST/treasury/{profile_id}/reports/{report_id}/finalize· scope treasury:writePost FinalizeGET/treasury/{profile_id}/reports/{report_id}/html· scope treasury:readGet HtmlPUT/treasury/{profile_id}/reports/{report_id}/narrative· scope treasury:writePut WordsPOST/treasury/{profile_id}/reports/{report_id}/narrative/approve· scope treasury:writePost Words ApprovePOST/treasury/{profile_id}/reports/{report_id}/narrative/draft· scope treasury:writePost Words DraftPOST/treasury/{profile_id}/reports/{report_id}/narrative/remove· scope treasury:writePost Words RemovePOST/treasury/{profile_id}/reports/{report_id}/withdraw· scope treasury:writePost WithdrawGET/treasury/{profile_id}/reserve-accounts· scope treasury:readList Reserve AccountsGET/treasury/{profile_id}/reserve-policies· scope treasury:readList Reserve PoliciesGET/treasury/{profile_id}/settlements· scope treasury:readList SettlementsGET/treasury/{profile_id}/statements· scope treasury:readStatementsGET/treasury/{profile_id}/statements/export/{fiscal_year}· scope treasury:readExport Fiscal YearThe CPA package: the four statements, the trial balance, the aging and the full ledger of one fiscal year, in one document, with the method beside every figure.GET/treasury/{profile_id}/statements/ledger· scope treasury:readLedger DetailPUT/treasury/{profile_id}/statements/settings· scope treasury:writeSet FiscalGET/treasury/{profile_id}/tax-reserves· scope treasury:readList Tax ReservesGET/treasury/{profile_id}/webhook-events· scope treasury:readList Webhook Events
Tax - ९१ doors - tax:read, tax:write
Filings, forms, reserves and the identity behind them.
POST/tax· scope tax:writeCreate ProfilePOST/tax/annual-packages· scope tax:writeCreate Annual Packagepnl_net_income is always genuinely computed server-side, never accepted as a client- supplied total. receipt_index_count and the JSON-TX manifest's included_reports are both counted live from the tenant's own real data for this tax_year.PATCH/tax/annual-packages/{package_id}/status· scope tax:writeSet Annual Package StatusPOST/tax/calendar· scope tax:writeCreate Calendar EventPATCH/tax/calendar/{event_id}/status· scope tax:writeSet Calendar Event StatusPOST/tax/forms/prepare· scope tax:writePrepare FormPATCH/tax/forms/{form_id}/review· scope tax:writeReview FormPOST/tax/identity-requirements· scope tax:writeCreate Identity RequirementPATCH/tax/identity-requirements/{req_id}/status· scope tax:writeSet Identity Requirement StatusGET/tax/integrations· scope tax:readList IntegrationsGET/tax/mine· scope tax:readGet My Profile EndpointPOST/tax/receipts· scope tax:writeCreate ReceiptGET/tax/receipts/{receipt_id}/file· scope tax:readDownload Receipt FilePATCH/tax/receipts/{receipt_id}/review· scope tax:writeReview ReceiptPOST/tax/reconciliation/run· scope tax:writeRun ReconciliationTX11 - real rows against real rows; the lines stored as they stood (tax_reconciliation.run).POST/tax/reserve-policies· scope tax:writeCreate Reserve PolicyPATCH/tax/reserve-policies/{policy_id}/approve· scope tax:writeApprove Reserve PolicyPOST/tax/reserves/simulate· scope tax:writeSimulate ReservePOST/tax/sales-tax-policies· scope tax:writeCreate Sales Tax PolicyPATCH/tax/sales-tax-policies/{policy_id}/settings· scope tax:writeSet Sales Tax SettingsTX9 - the jurisdiction code a policy's captures match (US-TX, GB, NG) and the dial that asks the payment provider's own tax calculation at checkout, on the TENANT'S account (tenant_payments.checkout_tax). This module names no rail: the Stop 15 guard forbids it, and the dial is only a fact on the row.PATCH/tax/sales-tax-policies/{policy_id}/toggle-collection· scope tax:writeToggle Sales Tax CollectionPOST/tax/state-nexus· scope tax:writeCreate Nexus ProfilePATCH/tax/state-nexus/{nexus_id}/review· scope tax:writeReview Nexus ProfilePOST/tax/tips-overtime· scope tax:writeCreate Tip OvertimePATCH/tax/tips-overtime/{record_id}/review· scope tax:writeReview Tip OvertimePOST/tax/transactions· scope tax:writeCreate Tax TransactionPOST/tax/vault· scope tax:writeUpload Vault ItemGET/tax/vault/{item_id}/file· scope tax:readDownload Vault ItemPATCH/tax/vault/{item_id}/review· scope tax:writeReview Vault ItemPATCH/tax/verification/simulate· scope tax:writeSimulate VerificationPOST/tax/withholding-policies· scope tax:writeCreate Withholding PolicyPATCH/tax/withholding-policies/{policy_id}/approve· scope tax:writeApprove Withholding PolicyPOST/tax/withholding/simulate· scope tax:writeSimulate WithholdingReal payout-tax gate simulation (spec section 14) - the outcome is genuinely computed from the tenant's own real tax_verification_state and whether any tax_identity_requirement has actually been approved, not a decorative status field: 1. No approved W-9/W-8 on file -> 100% hold simulated (spec: "If W-9/W-8 missing, simulate 100% hold where configured"). 2. Verification is verified_placeholder …GET/tax/{profile_id}· scope tax:readGet ProfileGET/tax/{profile_id}/annual-packages· scope tax:readList Annual PackagesPATCH/tax/{profile_id}/basis· scope tax:writePatch BasisTX6 - the method the return follows. The books stay accrual; the tax desk reads this.GET/tax/{profile_id}/calendar· scope tax:readList Calendar EventsPATCH/tax/{profile_id}/entity-type· scope tax:writePatch Entity TypeTX7 - the dial that chooses the estimate's shape. NULL clears it (estimates stop, in words).PATCH/tax/{profile_id}/estimate-rate· scope tax:writePatch RatePOST/tax/{profile_id}/estimates/{estimate_id}/record-payment· scope tax:writePost PaymentGET/tax/{profile_id}/filings· scope tax:readGet FilingsPOST/tax/{profile_id}/filings/prepare· scope tax:writePost PreparePOST/tax/{profile_id}/filings/{filing_id}/approve· scope tax:writePost ApprovePOST/tax/{profile_id}/filings/{filing_id}/transmit· scope tax:writePost TransmitPOST/tax/{profile_id}/filings/{filing_id}/withdraw· scope tax:writePost WithdrawGET/tax/{profile_id}/form-copies· scope tax:readGet DeskPATCH/tax/{profile_id}/form-copies/{copy_id}/revoke· scope tax:writePatch RevokeGET/tax/{profile_id}/forms· scope tax:readList Form PreparationsGET/tax/{profile_id}/forms/desk· scope tax:readGet DeskPOST/tax/{profile_id}/forms/prepare-year· scope tax:writePost Prepare YearPOST/tax/{profile_id}/forms/{form_id}/copy· scope tax:writePost CopyPOST/tax/{profile_id}/forms/{form_id}/correct· scope tax:writePost CorrectGET/tax/{profile_id}/forms/{form_id}/export/{fmt}· scope tax:readGet Form ExportGET/tax/{profile_id}/forms/{form_id}/html· scope tax:readGet Form HtmlPOST/tax/{profile_id}/forms/{form_id}/request-identity· scope tax:writePost Request Identity For FormPATCH/tax/{profile_id}/forms/{form_id}/void· scope tax:writePatch VoidGET/tax/{profile_id}/identity-requirements· scope tax:readList Identity RequirementsPOST/tax/{profile_id}/identity-requirements/{req_id}/request· scope tax:writePost Request IdentityPOST/tax/{profile_id}/identity-requirements/{req_id}/tin-attested· scope tax:writePost AttestGET/tax/{profile_id}/inputs· scope tax:readGet InputsRead-through on open: captures become tax transactions, then the year is read from the books.POST/tax/{profile_id}/inputs/sync· scope tax:writePost SyncGET/tax/{profile_id}/journals· scope tax:readGet DeskRead-through on open: what is due posts, then the desk is read.POST/tax/{profile_id}/journals/run· scope tax:writePost RunGET/tax/{profile_id}/jurisdictions· scope tax:readGet JurisdictionsPOST/tax/{profile_id}/jurisdictions· scope tax:writePost JurisdictionPATCH/tax/{profile_id}/jurisdictions/{reg_id}· scope tax:writePatch JurisdictionGET/tax/{profile_id}/lines· scope tax:readGet LinesPUT/tax/{profile_id}/lines/{account_id}· scope tax:writePut LineMap an account to a line of the desk's form by hand; NULL returns it to the default.GET/tax/{profile_id}/members· scope tax:readList MembersPOST/tax/{profile_id}/members· scope tax:writeAdd MemberGET/tax/{profile_id}/nexus· scope tax:readList Nexus ProfilesGET/tax/{profile_id}/packs· scope tax:readGet PacksPOST/tax/{profile_id}/packs· scope tax:writePost PackGET/tax/{profile_id}/receipts· scope tax:readList ReceiptsGET/tax/{profile_id}/receipts-desk· scope tax:readGet DeskPATCH/tax/{profile_id}/receipts/{receipt_id}/link· scope tax:writePatch LinkPOST/tax/{profile_id}/receipts/{receipt_id}/post· scope tax:writePost ReceiptPATCH/tax/{profile_id}/receipts/{receipt_id}/unlink· scope tax:writePatch UnlinkGET/tax/{profile_id}/reconciliation· scope tax:readList Reconciliation RunsGET/tax/{profile_id}/reconciliation/preview· scope tax:readPreviewThe read without a stored run - what a run would say now.PATCH/tax/{profile_id}/reconciliation/{run_id}/review· scope tax:writePatch ReviewGET/tax/{profile_id}/reserve-policies· scope tax:readList Reserve PoliciesGET/tax/{profile_id}/reserve-simulations· scope tax:readList Reserve SimulationsGET/tax/{profile_id}/sales-tax-policies· scope tax:readList Sales Tax PoliciesPOST/tax/{profile_id}/sales-tax/{policy_id}/record-remittance· scope tax:writePost RemitGET/tax/{profile_id}/tips-overtime· scope tax:readList Tips OvertimeGET/tax/{profile_id}/transactions· scope tax:readList Tax TransactionsGET/tax/{profile_id}/vault· scope tax:readList Vault ItemsGET/tax/{profile_id}/verification· scope tax:readGet Verification StateGET/tax/{profile_id}/withholding-policies· scope tax:readList Withholding PoliciesGET/tax/{profile_id}/withholding-simulations· scope tax:readList Withholding Simulations
Compliance - ८८ doors - compliance:read, compliance:write
Policy, evidence, incidents and review - what keeps the rest defensible.
POST/compliance· scope compliance:writeCreate ProfilePOST/compliance/approvals· scope compliance:writeCreate ApprovalPATCH/compliance/approvals/{approval_id}/revoke· scope compliance:writeRevoke ApprovalGET/compliance/authority/register· scope compliance:readGet AuthoritiesThe Emperor's alone: these rows carry counsel references and attestations for the whole platform, and every one of the tables behind them is owner-only by its own policy.POST/compliance/brand-rules· scope compliance:writeCreate Brand RulePOST/compliance/cross-pillar-requirements· scope compliance:writeCreate Cross Pillar RequirementPATCH/compliance/cross-pillar-requirements/{req_id}/status· scope compliance:writeUpdate Cross Pillar Requirement StatusPOST/compliance/data-classifications· scope compliance:writeCreate Data ClassificationPOST/compliance/data-subject-requests· scope compliance:writeCreate Data Subject RequestPATCH/compliance/data-subject-requests/{req_id}/status· scope compliance:writeUpdate Data Subject Request StatusPOST/compliance/delta-reports· scope compliance:writeCreate Delta ReportPATCH/compliance/delta-reports/{report_id}/close· scope compliance:writeClose Delta ReportPOST/compliance/disputes· scope compliance:writeCreate DisputeGET/compliance/disputes/{case_id}/evidence· scope compliance:readList Dispute EvidencePOST/compliance/disputes/{case_id}/evidence· scope compliance:writeUpload Dispute EvidencePATCH/compliance/disputes/{case_id}/status· scope compliance:writeUpdate Dispute StatusPOST/compliance/evidence· scope compliance:writeUpload EvidencePATCH/compliance/evidence-links/{link_id}/detach· scope compliance:writePatch DetachGET/compliance/evidence/{ev_id}/download· scope compliance:readDownload EvidencePATCH/compliance/evidence/{ev_id}/status· scope compliance:writeUpdate Evidence StatusPOST/compliance/evidence/{evidence_id}/links· scope compliance:writePost LinkGET/compliance/evidence/{evidence_id}/links· scope compliance:readGet What It EvidencesPOST/compliance/exceptions· scope compliance:writeCreate ExceptionPATCH/compliance/exceptions/{exc_id}/status· scope compliance:writeUpdate Exception StatusPOST/compliance/gate-check· scope compliance:writeGate CheckPOST/compliance/governance-records· scope compliance:writeCreate Governance RecordPATCH/compliance/governance-records/{rec_id}/adopt· scope compliance:writeAdopt Governance RecordPATCH/compliance/identity-gates/{gate_id}· scope compliance:writeUpdate Identity GatePOST/compliance/incidents· scope compliance:writeCreate IncidentPOST/compliance/incidents/{inc_id}/ai-draft-summary· scope compliance:writeAi Draft Incident SummaryAI drafts a plain-language incident summary from the recorded title/description - advisory only, never authoritative, matching this project's established pattern. Never triggers any automatic investigation, eviction, suspension, or disclosure (spec section 23).PATCH/compliance/incidents/{inc_id}/status· scope compliance:writeUpdate Incident StatusGET/compliance/integrations· scope compliance:readList IntegrationsGET/compliance/mine· scope compliance:readGet My Profile EndpointPOST/compliance/policies· scope compliance:writeCreate PolicyPOST/compliance/policies/{policy_id}/acknowledge· scope compliance:writePost AcknowledgeGET/compliance/policies/{policy_id}/acknowledgements· scope compliance:readGet StandingPOST/compliance/policies/{policy_id}/acknowledgements/record· scope compliance:writePost Record ForPATCH/compliance/policies/{policy_id}/status· scope compliance:writeUpdate Policy StatusGET/compliance/policies/{policy_id}/versions· scope compliance:readList Policy VersionsPOST/compliance/provenance-manifests· scope compliance:writeIngest Provenance ManifestPOST/compliance/provenance-manifests/{manifest_id}/validate· scope compliance:writeValidate Provenance ManifestReal structural validation - confirms the manifest actually has an asset_hash on file (i.e., a real asset was ingested), never a cryptographic C2PA signature check (spec section 18: production signing needs a certificate policy/key management/production approval, none of which exist in Core).GET/compliance/provenance-manifests/{manifest_id}/verifications· scope compliance:readList Provenance VerificationsPOST/compliance/red-flags· scope compliance:writeCreate Red FlagPATCH/compliance/red-flags/{rf_id}/status· scope compliance:writeUpdate Red Flag StatusPOST/compliance/reimbursements· scope compliance:writeCreate ReimbursementPOST/compliance/reviews· scope compliance:writeCreate ReviewGET/compliance/reviews/{case_id}/approvals· scope compliance:readList ApprovalsPOST/compliance/risks· scope compliance:writeCreate RiskPATCH/compliance/risks/{risk_id}· scope compliance:writeUpdate RiskGET/compliance/sanctions/board· scope compliance:readBoard DoorGET/compliance/sanctions/hits· scope compliance:readHits DoorPOST/compliance/sanctions/hits/{hit_id}/close· scope compliance:writeClose HitA false positive - or a true one a person has dealt with outside - is CLOSED WITH A REASON. Who may: the platform's owner, or a member of the business the party belongs to (the wall says who can read the hit; that is who may answer it). The write goes on the platform's cursor because the hit table takes no tenant write - the fact is checked on the cursor that may check it, the row written on the o…POST/compliance/sanctions/lists/register· scope compliance:writeRegister Lists DoorThe Emperor registers the shelf's four lists on the scout registry - kind `sanctions`, dial 0, idempotent. Nothing is fetched by this door.POST/compliance/sanctions/rescreen· scope compliance:writeRescreen DoorHis hand: every party against the lists as they stand. Opens cases; holds nothing.GET/compliance/{profile_id}· scope compliance:readGet ProfileGET/compliance/{profile_id}/brand-rules· scope compliance:readList Brand RulesGET/compliance/{profile_id}/consents· scope compliance:readGet ConsentsGET/compliance/{profile_id}/cross-pillar-requirements· scope compliance:readList Cross Pillar RequirementsGET/compliance/{profile_id}/dashboard· scope compliance:readGet DashboardGET/compliance/{profile_id}/data-classifications· scope compliance:readList Data ClassificationsGET/compliance/{profile_id}/data-subject-requests· scope compliance:readList Data Subject RequestsGET/compliance/{profile_id}/delta-reports· scope compliance:readList Delta ReportsGET/compliance/{profile_id}/disputes· scope compliance:readList DisputesGET/compliance/{profile_id}/evidence· scope compliance:readList EvidenceGET/compliance/{profile_id}/evidence-for/{kind}/{target_id}· scope compliance:readGet Evidence ForGET/compliance/{profile_id}/evidence-sweep· scope compliance:readGet SweepGET/compliance/{profile_id}/evidence-unlinked· scope compliance:readGet UnlinkedGET/compliance/{profile_id}/exceptions· scope compliance:readList ExceptionsGET/compliance/{profile_id}/gate-strikes· scope compliance:readList Gate StrikesGET/compliance/{profile_id}/governance-records· scope compliance:readList Governance RecordsGET/compliance/{profile_id}/identity-gates· scope compliance:readList Identity GatesGET/compliance/{profile_id}/incidents· scope compliance:readList IncidentsGET/compliance/{profile_id}/members· scope compliance:readList MembersPOST/compliance/{profile_id}/members· scope compliance:writeAdd MemberGET/compliance/{profile_id}/obligations· scope compliance:readGet RegisterPATCH/compliance/{profile_id}/obligations/{key}· scope compliance:writePatch ObligationGET/compliance/{profile_id}/packs· scope compliance:readGet PacksPOST/compliance/{profile_id}/packs· scope compliance:writePost PackGET/compliance/{profile_id}/policies· scope compliance:readList PoliciesGET/compliance/{profile_id}/privacy· scope compliance:readGet Privacy PreferencePATCH/compliance/{profile_id}/privacy· scope compliance:writeUpdate Privacy PreferenceGET/compliance/{profile_id}/provenance-manifests· scope compliance:readList Provenance ManifestsGET/compliance/{profile_id}/red-flags· scope compliance:readList Red FlagsGET/compliance/{profile_id}/reimbursements· scope compliance:readList ReimbursementsGET/compliance/{profile_id}/reviews· scope compliance:readList ReviewsGET/compliance/{profile_id}/risks· scope compliance:readList RisksGET/compliance/{profile_id}/sanctions· scope compliance:readTenant DoorThe business's own open cases, in words. The desk's row answers who the business is; the hit rows answer on the member arm.GET/compliance/{profile_id}/trail· scope compliance:readGet Trail
Investors - २२७ doors - investors:read, investors:write
Research, strategy and simulated execution. No live broker connection.
GET/credit-repair/compliance/authorization· scope investors:readGet AuthorizationPOST/credit-repair/compliance/authorization· scope investors:writeRecord AuthorizationOwner-only. Records the Emperor's attestation that real legal counsel has reviewed a real CROA-compliant contract, a real pre-signup disclosure exists, and billing genuinely never charges up front. This is the ONLY way any of the three gates is ever set — no automated path creates it, and setting it does not itself build or enable any dispute-filing feature.POST/credit-repair/compliance/authorization/revoke· scope investors:writeRevoke AuthorizationOwner-only. Revokes any active authorization (real automated credit-repair activity, if it ever existed, would revert to not-authorized).GET/credit-repair/compliance/readiness· scope investors:readReadinessPOST/investors· scope investors:writeCreate ProfileGET/investors/alerting/alerts· scope investors:readList OpenPOST/investors/alerting/alerts/{alert_id}/acknowledge· scope investors:writeAcknowledgeAcknowledge an alert. It stays quiet while the condition persists - and returns if it comes back after resolving, because the acknowledgement was about a situation that has since ended.GET/investors/alerting/badge· scope investors:readBadgeOne small number for a navigation item, plus the one thing that number cannot say. WHAT THE BADGE COUNTS IS THE DESIGN. Not "alerts" - alerts include the ones somebody has already acknowledged and is dealing with, and a badge that keeps counting those is a permanent red dot, which is wallpaper within a week. That is INV19's fatigue failure moved up into the interface, and it would undo the restra…GET/investors/alerting/channels· scope investors:readChannelsWhich channels exist and which of them are allowed to reach a person.POST/investors/alerting/channels/{channel}/signoff· scope investors:writeSign Off ChannelRecord a written sign-off for a channel that reaches real people. The Emperor's alone. WHAT THIS DOES NOT DO: turn the channel on (`enabled` is a separate switch), decide whether the sign-off is sufficient, or shorten what the gate asks for. A channel with a recorded sign-off and `enabled` false still refuses, and says so in the answer.POST/investors/alerting/channels/{channel}/signoff/revoke· scope investors:writeRevoke Channel SignoffWithdraw it. Immediate, and the withdrawal goes to the audit trail - which is where the authority register reads a lock's history from, because the row itself keeps no record of ever having been open.POST/investors/alpha-audit· scope investors:writeAppend Alpha Audit EntryPOST/investors/assignment/profiles/{profile_id}/assign· scope investors:writeAssign EarlyEarly assignment: a short position taken from the account before expiry. There is no approval parameter and there is not going to be one. American options are assignable at any moment, the most common cause is a dividend the day before it goes ex, and the account gets no say - so the endpoint that models it gets no say either.POST/investors/assignment/profiles/{profile_id}/exercise· scope investors:writeExerciseExercise a long option early. The one settlement on this platform that IS a decision.GET/investors/assignment/profiles/{profile_id}/exposure· scope investors:readRead ExposureWhat could be done to this account today, without anybody asking it first.POST/investors/assignment/profiles/{profile_id}/settle-expiry· scope investors:writeRun ExpiryGET/investors/assignment/profiles/{profile_id}/settlements· scope investors:readList SettlementsPOST/investors/backtests· scope investors:writeCreate BacktestGET/investors/backtests/{run_id}/report· scope investors:readBacktest ReportThe full evidence report: the verdict, every check, and every fold.POST/investors/breaker-trips/{trip_id}/reset· scope investors:writeReset TripClear a trip. Needs a named person and a real reason - both enforced in the database. The strategy is NOT un-halted by this. Clearing the breaker removes the block; deciding the strategy should trade again is a separate act with its own evidence gate (INV3), and collapsing the two would let a breaker reset quietly promote something.PUT/investors/broker/profiles/{profile_id}/connection· scope investors:writeConnectSeal a tenant's own broker credential. Fitinty never opens a brokerage account on anybody's behalf and never bills for broker access - the same rule as the affiliate networks and the tenant data feeds. What is different here is the sealing: once written, the application cannot read this back. Not "will not" - cannot. The column privilege does not exist.POST/investors/broker/profiles/{profile_id}/connection/revoke· scope investors:writeRevokeRevoke the connection. The sealed credential stays in the row and stays unreadable.POST/investors/broker/profiles/{profile_id}/place· scope investors:writePlaceThe real-order path. It exists, it is wired to the gate, and the gate says no. This is the function `is_live_execution_enabled` was written for and has never had. Having it here, refusing, is worth more than not having it: the refusal is now a tested behaviour rather than an absence somebody could fill in without noticing what they were bypassing.POST/investors/broker/profiles/{profile_id}/preflight· scope investors:writePreflightShape the order this intent would become, show it, and send nothing. The point is that a tenant can see their strategy producing sane orders long before any of them could be real - and that the shaping is reviewable rather than taken on trust.GET/investors/broker/profiles/{profile_id}/readiness· scope investors:readReadinessEvery gate, what it says, and everything that has been attempted.POST/investors/combos/profiles/{profile_id}· scope investors:writeCreate ComboBuild and evaluate a package. Refuses the whole thing or none of it.GET/investors/combos/profiles/{profile_id}· scope investors:readList CombosPOST/investors/combos/{combo_id}/fill· scope investors:writeFill ComboFill every leg, or none of them. All the legs go through `_apply_fill`, so the multiplier, the cash ledger and the fee model are INV14's and INV4's rather than a second implementation that drifts. One transaction: if any leg raises, the whole package rolls back, which is what all-or-nothing means when the alternative is being left holding the leg nobody wanted.POST/investors/correlation/profiles/{profile_id}/snapshot· scope investors:writeSnapshotMeasure the book's real concentration and record what it was measured from.GET/investors/correlation/profiles/{profile_id}/snapshots· scope investors:readHistoryPOST/investors/cross-asset· scope investors:writeCreate Cross Asset RelationshipGET/investors/divergence/profiles/{profile_id}· scope investors:readHistoryPOST/investors/divergence/profiles/{profile_id}/reconcile· scope investors:writeRun ReconcileCheck a strategy's claim against what the engine did, and record the attribution.POST/investors/dividends/calendar· scope investors:writeRecordEnter a dividend. There is no feed to do this automatically and the schema says so.PUT/investors/dividends/coverage· scope investors:writeAssert CoverageSay what is KNOWN about an instrument, so that silence stops being ambiguous.GET/investors/dividends/feed/freshness· scope investors:readFreshnessReadable by everyone, because 'is anybody checking' is not an operational secret. INV18's distinction: an empty list of problems and a checker that stopped running look identical on a screen and mean opposite things.GET/investors/dividends/feed/instruments/{symbol}· scope investors:readLatest For SymbolWhat the feed last concluded about one instrument, and why. Findings are readable by any authenticated user: that a company has never declared a dividend is a fact about the company, and knowing it says nothing about anybody's positions.POST/investors/dividends/feed/run· scope investors:writeTrigger RunAsk the source now. Idempotent in effect: re-clearing an instrument just refreshes its date.GET/investors/dividends/feed/runs· scope investors:readList RunsGET/investors/dividends/profiles/{profile_id}/coverage· scope investors:readRead CoverageWhat has NOT been checked, first. A dividend list cannot display an absence.GET/investors/dividends/profiles/{profile_id}/early-assignment· scope investors:readRead RiskPOST/investors/edge/concepts/{concept_id}/candidate· scope investors:writePropose CandidateTurn a concept into a strategy - `untested`, like any other idea. This is where the pipeline hands over. The strategy it creates has no privileges: it cannot propose trades until it is at least `backtested`, and it cannot reach `backtested` without surviving INV3's gate. A concept that came out of forty slices of our own journal is treated exactly like an idea somebody had in the shower, which is…POST/investors/edge/concepts/{concept_id}/discard· scope investors:writeDiscardDrop a concept, with a reason. The most valuable row in the table. An idea abandoned for a stated reason stops the next person - who on a monthly loop is you - spending a week rediscovering it.GET/investors/equity/my-holding· scope investors:readGet My HoldingGET/investors/execution/authorization· scope investors:readGet AuthorizationPOST/investors/execution/authorization· scope investors:writeRecord AuthorizationOwner-only. Records the Emperor's attestation that legal counsel AND a broker relationship confirm real-order go-live. This is the ONLY way the execution gate is set — no automated path creates it, and it never enables real orders on its own.POST/investors/execution/authorization/revoke· scope investors:writeRevoke AuthorizationOwner-only. Revokes any active execution authorization (real orders revert to not-authorized).GET/investors/execution/readiness· scope investors:readReadinessPOST/investors/features· scope investors:writeWrite FeatureStore a feature against the date it was knowable. Written once and never revised. Recomputing a 2019 feature with today's better data and overwriting it would make every backtest that read the old value unreproducible - and would improve the new one in a way that had nothing to do with the strategy.GET/investors/features· scope investors:readRead FeaturesFeatures up to a date - never past it. `as_of` is a ceiling rather than a filter, because that is the only reading that is safe inside a backtest: asking for features "as of 2019" and getting a 2020 value is precisely the leak this table exists to prevent.POST/investors/futures/contracts· scope investors:writeAdd ContractRegister a futures contract. Owner-only - a contract calendar is reference data.GET/investors/futures/contracts· scope investors:readList ContractsThe contract calendar, with what is expiring soonest first.POST/investors/futures/rolls· scope investors:writeRecord RollRecord a roll, deriving the factor from what the two contracts actually traded at. The factor is computed here rather than supplied, because a supplied factor is a number nobody can check against anything.GET/investors/futures/{root}/continuous· scope investors:readRead ContinuousThe continuous series as it would have appeared on a given date. Default `as_of` is today, which is the series everybody means when they say "the continuous contract" - and asking for an older date is how you check whether a backtest was reading prices that existed at the time.POST/investors/institutional-flows· scope investors:writeCreate Institutional FlowGET/investors/integrations· scope investors:readList IntegrationsGET/investors/intents/{intent_id}/decision· scope investors:readGet DecisionWhy this intent got the answer it got - every limit, including the ones it passed.POST/investors/intents/{intent_id}/execute· scope investors:writeExecute IntentTurn an approved intent into a paper order, a fill, and a journal row. The one path from a decision to a position. Everything it refuses, it refuses by name.GET/investors/kill-switch· scope investors:readRead Kill SwitchWhether platform trading is stopped, and why. Readable by anyone authenticated on purpose: being told why trading stopped is not privileged information, and a tenant who cannot see it will open a support ticket instead.POST/investors/kill-switch· scope investors:writeSet Kill SwitchThrow or clear the platform kill switch. Owner-only, named, and with a reason. A NEW ROW EACH TIME rather than a flag that flips. The switch is a log: what it was, who changed it, when and why - all of which the flag version throws away, and all of which is what somebody asks for afterwards. Nothing automatic can reach this. A kill switch thrown by code is a bug with a lever attached, and the ca…GET/investors/live/audit· scope investors:readRead AuditThe compliance trail: every action on the live path, allowed or refused. Kept apart from `audit_events` deliberately - that is application telemetry with a telemetry retention policy, and this is the record somebody asks for when it matters.GET/investors/live/disclosures· scope investors:readRead DisclosuresThe disclosures as they currently read, with the hash of each.GET/investors/live/gate· scope investors:readRead GateEvery part of the live gate, and what each says. Readable by anyone authenticated: whether this platform is permitted to trade live is not privileged information, and a tenant who cannot see it has to take it on trust.PUT/investors/live/profiles/{profile_id}/disclosures· scope investors:writeAcknowledgeRecord that a named person read the current text of one disclosure.GET/investors/live/profiles/{profile_id}/disclosures· scope investors:readRead Profile DisclosuresWhat this profile has acknowledged, and what is outstanding.POST/investors/live/signoff· scope investors:writeRecord SignoffRecord one party's sign-off, in their own words, under their own account. Owner-only, and the gate then requires the two rows to come from DIFFERENT accounts - so counsel needs an account of their own. "The owner records what counsel told them" deliberately does not open this.POST/investors/live/signoff/{signoff_id}/revoke· scope investors:writeRevoke SignoffWithdraw a sign-off. Closing the gate is always available to one person - only opening it needs two.GET/investors/macro· scope investors:readMacro SnapshotGET/investors/market-data-sources· scope investors:readList Market Data SourcesPOST/investors/market-data/add· scope investors:writeAdd InstrumentOwner-only: add ANY symbol on demand. Resolves the right free provider, fetches its bars immediately, and stores it as shared reference data so it appears everywhere (Live prices, charts, screener). DATA ONLY — no broker, no orders.POST/investors/market-data/backfill-crypto· scope investors:writeBackfill CryptoOwner-only: pull deep daily history for the crypto instruments from permissive venues. Market data is platform-curated reference data (migration 099: owner-only write). Every source used here is `redistribution = permitted` in migration 325 - so unlike the rest of our bars, these may actually be shown to a tenant.POST/investors/market-data/backfill-fx· scope investors:writeBackfill FxFill the FX gap from the ECB. Owner-only: market data is platform-curated reference data (migration 099). Unlike our other non-crypto sources this one is `redistribution = permitted`, so these bars may actually be shown to a tenant.GET/investors/market-data/chart-bars· scope investors:readChart BarsEvery instrument's full daily OHLCV series, ascending by date, keyed by symbol — shaped for TradingView Lightweight Charts. Read-only shared reference data (migration 099, select USING(true)). LICENCE-GATED (migration 325). Holding a bar and being allowed to show it to a tenant are different permissions, and this route is the one that hands data to a tenant. A bar whose source forbids redistribut…GET/investors/market-data/coverage· scope investors:readCoverageWhat this platform can honestly claim, by asset class and by licence. Built because "we have 11,306 bars" was a true sentence that answered no useful question. The number that matters is how many of them we are allowed to use, and for what.DELETE/investors/market-data/instruments/{instrument_id}· scope investors:writeRemove InstrumentOwner-only: remove a dynamically-added instrument (provider_symbol IS NOT NULL) and its bars. The original seeded core set can't be removed here.GET/investors/market-data/intraday· scope investors:readMarket IntradayOn-demand intraday OHLC for one symbol. Crypto = Alpaca 1-min (keyless); stocks/ETFs/FX = Twelve Data 5-min; futures/indices aren't free intraday (supported=false).GET/investors/market-data/latest· scope investors:readLatest PricesLatest close per instrument, with the real source label — readable by any authenticated user (market data is shared reference data, migration 099).GET/investors/market-data/quotes· scope investors:readMarket QuotesLatest price per instrument for the auto-refreshing Live Quotes panel. Crypto is real-time (Alpaca); everything else is its latest stored close (labeled EOD). Readable by any user.POST/investors/market-data/refresh· scope investors:writeRefresh Market DataOwner-only: pull real delayed EOD bars for the seeded instruments from the active provider and upsert them into market_bar. Market data is platform-curated reference data (migration 099: owner-only write).GET/investors/market-data/search· scope investors:readSearch SymbolsSearch the global stock/ETF universe (Twelve Data symbol_search). US listings (USD) add via Twelve Data; international listings (London/Tokyo/Frankfurt/…) add under their Yahoo symbol (VOD.L, 7203.T, …) — Fitinty is global. FX/crypto pairs are added by exact symbol via /add.GET/investors/market-instruments· scope investors:readList Market InstrumentsGET/investors/mine· scope investors:readGet My Profile EndpointPOST/investors/monte-carlo· scope investors:writeCreate Monte Carlo RunGET/investors/news· scope investors:readMarket NewsPOST/investors/options/contracts· scope investors:writeCreate ContractRegister an option contract, and the instrument row that carries it.GET/investors/options/profiles/{profile_id}/positions/{instrument_id}· scope investors:readRead PositionPUT/investors/options/profiles/{profile_id}/undefined-risk· scope investors:writeAuthorize Undefined RiskPermit this account to hold a position whose loss has no upper bound. Revoke-then-insert rather than upsert, so the previous authorization survives as a row. What was permitted, by whom, and until when is the question this table exists to answer later.GET/investors/options/profiles/{profile_id}/undefined-risk· scope investors:readRead Undefined RiskPOST/investors/options/structure· scope investors:writeAnalyse StructureWhat is the worst this combination can do, and does it have a floor at all?POST/investors/options/synthesise-chain· scope investors:writeSynthesise ChainBuild a clearly-labelled SYNTHETIC option chain, for the paper loop only. There is no permissively licensed options chain with implied volatility. Every retail feed that carries one licenses it for personal use, exactly as INV9 found for equities, and chains are priced higher than bars everywhere. So rather than leave the whole instrument class untestable, this generates one from Black-Scholes ov…POST/investors/paper-orders· scope investors:writeCreate Paper OrderPOST/investors/paper-orders/{order_id}/cancel· scope investors:writeCancel Paper OrderPOST/investors/paper-orders/{order_id}/continue-fill· scope investors:writeContinue Fill Paper OrderGET/investors/paper-orders/{order_id}/fills· scope investors:readList Paper FillsPOST/investors/paper-orders/{order_id}/submit· scope investors:writeSubmit Paper OrderPOST/investors/portfolio-positions· scope investors:writeUpsert Portfolio PositionPOST/investors/portfolios· scope investors:writeCreate PortfolioGET/investors/portfolios/{portfolio_id}/positions· scope investors:readList Portfolio PositionsPOST/investors/price-alerts· scope investors:writeCreate AlertPOST/investors/price-alerts/{alert_id}/cancel· scope investors:writeCancel AlertGET/investors/profiles/{profile_id}/book· scope investors:readRead BookPositions marked to the last close, beside what they cost - and what closing them would cost.GET/investors/profiles/{profile_id}/cash· scope investors:readRead CashThe cash balance, the ledger behind it, and whether the two agree. Reconciliation is a query rather than an argument: a balance that can drift from its own ledger is a balance nobody can check.PUT/investors/profiles/{profile_id}/circuit-breakers· scope investors:writeSet BreakersSet the thresholds. Append-only: the previous version stays readable, so what the breakers were when one tripped is always answerable.GET/investors/profiles/{profile_id}/circuit-breakers· scope investors:readRead BreakersThe thresholds, whether anything is currently tripped, and every trip on record.POST/investors/profiles/{profile_id}/circuit-breakers/sweep· scope investors:writeSweepEvaluate the account breakers now, without waiting for somebody to try to trade. Nothing depends on this having run - the same checks happen on every intent. This exists so a trip can be surfaced before the next attempt rather than at it.POST/investors/profiles/{profile_id}/costs· scope investors:writeAdd CostRecord work done on this profile's behalf that the platform did not measure itself.PUT/investors/profiles/{profile_id}/data-connections· scope investors:writeConnect Data FeedConnect this profile's OWN market-data subscription. The licence then sits between the tenant and their provider - we are a client they pointed at their own account, not a redistributor. That is the clean answer to the restriction that makes our platform-supplied feeds non-displayable, and it is also what a trader who already pays for data actually wants. The credential is stored so we can call …GET/investors/profiles/{profile_id}/data-connections· scope investors:readList Data FeedsThe profile's connections. Credentials are never included - only whether one is set.GET/investors/profiles/{profile_id}/edge· scope investors:readRead PipelineThe pipeline's state: what the journal seems to say, and what has been made of it.POST/investors/profiles/{profile_id}/edge/concepts· scope investors:writeSynthesiseTurn hints into a hypothesis - or refuse, when the hints do not carry one. The refusal is the feature. A pipeline that always produces a concept is a pipeline that produces confident descriptions of noise, and somebody will act on those.POST/investors/profiles/{profile_id}/edge/extract· scope investors:writeExtract HintsSlice the journal and record what it seems to say. Append-only and deliberately not deduplicated: running this a month apart produces two hints on one dimension with different sample sizes, and the pair says more than either. A slice that strengthens as n grows is a different object from one that evaporates.PUT/investors/profiles/{profile_id}/evidence-policy· scope investors:writeSet Evidence PolicySet this profile's evidence thresholds. The bounds are deliberate: a deflated Sharpe below 0.5 or a PBO above 0.5 would be a gate that admits results indistinguishable from chance, and a threshold that cannot refuse anything is not a threshold.GET/investors/profiles/{profile_id}/journal· scope investors:readRead JournalEvery fill, with the rules that caused it and the ruling that permitted it.POST/investors/profiles/{profile_id}/journal/attribute· scope investors:writeAttributeAttach a regime label to every fill that has none. Only rows without one: relabelling would make the attribution behind a past review unreproducible, and a review whose numbers move underneath it is not evidence of anything. The label is set through a SECURITY DEFINER function that can touch nothing else on the row, because an UPDATE grant on the journal would let some future code path amend a re…GET/investors/profiles/{profile_id}/journal/attribution· scope investors:readRead AttributionWhere the realised P&L went, by cause, over the journal's own round trips (IN7).PUT/investors/profiles/{profile_id}/machine-rate· scope investors:writeSet RateSay what an hour of your machine is worth. Until you do, cost is reported in seconds only.PUT/investors/profiles/{profile_id}/paper-account· scope investors:writeOpen Paper AccountDeclare how big this paper account is. Every percentage limit is measured against it. Declared rather than defaulted: a starting capital nobody chose would end up inside every risk calculation this profile ever makes, and nobody would remember it was a guess.POST/investors/profiles/{profile_id}/reviews· scope investors:writeWrite ReviewRecord a period review, with the tear sheet as it stood when it was written. Append-only on purpose: the point of a review is to be able to read back what somebody believed BEFORE they knew how the next month went. A review that can be edited afterwards records only the opinion that turned out well.GET/investors/profiles/{profile_id}/reviews· scope investors:readList ReviewsPast reviews, newest first - including the ones whose hypothesis turned out wrong.GET/investors/profiles/{profile_id}/risk-decisions· scope investors:readList DecisionsPUT/investors/profiles/{profile_id}/risk-limits· scope investors:writeSet LimitsSet the fence. The previous version is superseded, never overwritten.GET/investors/profiles/{profile_id}/risk-limits· scope investors:readGet LimitsGET/investors/profiles/{profile_id}/tear-sheet· scope investors:readTear SheetThe performance report. Equally usable by somebody deciding to allocate and somebody deciding to shut the thing down - if it reads better for one of those, it is selling.GET/investors/profiles/{profile_id}/trading-strategies· scope investors:readList TradingThe profile's strategies, seen through what they are allowed to DO. Deliberately a different view from investors_research's list, not a second copy of it: that one answers "what ideas do we have", this one answers "what is trading, and how".PUT/investors/profiles/{profile_id}/tradingview-symbols· scope investors:writeMap SymbolMap a TradingView ticker to one of our instruments. TradingView's spelling is not always ours - BTCUSD there, BTC-USD here - and an unmapped ticker is refused rather than guessed.GET/investors/profiles/{profile_id}/tradingview-symbols· scope investors:readList Symbol MapGET/investors/profiles/{profile_id}/true-pnl· scope investors:readTrue PnlTrading profit, minus what it cost to produce it. The number INV7's tear sheet does not compute, and the one that decides whether a strategy is worth running at all.POST/investors/research-notes· scope investors:writeCreate Research NotePOST/investors/research-notes/{note_id}/ai-draft· scope investors:writeAi Draft Research NoteAI drafts model_outputs (a synthesis of the note's own thesis/evidence/counter-evidence) - advisory only, always labeled ai_generated, review_status stays 'draft' until a human explicitly marks it reviewed. Matches this project's 'AI drafts, never authoritative' pattern used everywhere else.PATCH/investors/research-notes/{note_id}/review· scope investors:writeReview Research NotePOST/investors/risk-policies· scope investors:writeCreate Risk PolicyPOST/investors/sentiment-observations· scope investors:writeCreate Sentiment ObservationLLM-assisted sentiment labeling - advisory only, always ai_generated=true and human_reviewed=false until a reviewer explicitly confirms it. Matches spec section 20: 'LLM sentiment is advisory.'PATCH/investors/sentiment-observations/{obs_id}/review· scope investors:writeReview Sentiment ObservationPOST/investors/sequencer/combos/{combo_id}/sequence· scope investors:writeRun SequenceWalk a legged package one leg at a time. Resumes a partial one from where it stopped.POST/investors/sequencer/combos/{combo_id}/unwind· scope investors:writeRun UnwindGET/investors/sequencer/stuck· scope investors:readStuckEverything halfway through becoming something else. The query somebody runs when they suspect a sequencer died. It reports elapsed time rather than a verdict, because a sequencer that is genuinely mid-walk and one whose process died look identical from the database and only the clock separates them.POST/investors/signals· scope investors:writeCreate SignalPATCH/investors/signals/{signal_id}/status· scope investors:writeUpdate Signal StatusGET/investors/sizer/profiles/{profile_id}/proposals· scope investors:readHistoryPOST/investors/sizer/profiles/{profile_id}/propose· scope investors:writeProposeHow much, and why that much. Records the proposal, because it is advice with a timestamp.POST/investors/strategies· scope investors:writeCreate StrategyPOST/investors/strategies/{sid}/backtest· scope investors:writeRun Evidence BacktestRun the strategy's spec under walk-forward validation and judge the result. This is the only thing that can make `backtested` true. It is a trial whether it passes or not - the row is written before the verdict is known and cannot be deleted, which is what stops the trial count from quietly becoming the count of runs somebody liked.GET/investors/strategies/{sid}/evidence· scope investors:readStrategy EvidenceEvery evidence run this strategy has, newest first, and whether any of them still stands. The workspace needs both halves of that. A strategy with four failed runs and one pass is in a different position from one with a single pass, and a strategy whose pass sits on a spec that has since been edited looks identical to a passing one until you ask.POST/investors/strategies/{sid}/instruments· scope investors:writeSet InstrumentsDeclare what this strategy trades. Required before it may propose anything. Declared, never inferred: a strategy that claims to trade anything has been tested on nothing, and a later backtest is only an honest test of the claim if the claim was made first.POST/investors/strategies/{sid}/intents· scope investors:writeSubmit IntentThe strategy proposes a trade. Nothing else happens. The intent lands `pending` and stays there. That is the shape the system is built to, not an unfinished feature: the risk authority is the only thing that may approve, size or refuse, and it arrives in INV2.GET/investors/strategies/{sid}/intents· scope investors:readList IntentsGET/investors/strategies/{sid}/passport· scope investors:readPassportThe whole life of a strategy, in order, with names on it.PUT/investors/strategies/{sid}/spec· scope investors:writeSet SpecGive a strategy a machine-runnable spec beside its prose. The prose stays authoritative for what the strategy MEANS - a reviewer approved words, not JSON. The spec is what actually runs. Keeping them separate is why a passing backtest cannot quietly redefine the strategy somebody signed off. Superseding a spec invalidates any promotion that rested on it, because evidence is about the thing that …POST/investors/strategies/{sid}/trade-mode· scope investors:writeSet Trade ModeSwitch a proven strategy between manual and auto, at will. No ceremony: no reason required, no approval, flip it as often as the day demands - that is what "at will" has to mean or people work around it. But never silent, because "who turned this on, and when" is the first question anybody asks after a bad day, and a switch with no answer to that is a switch nobody will trust enough to use. One …POST/investors/strategies/{sid}/trading-state· scope investors:writeSet Trading StateMove a strategy's trading state - by a named person, with a reason, on the record. `backtested` and `paper` are no longer claims. INV3 built the gate, so moving into either now requires a walk-forward run that survived the profile's evidence thresholds - and the passport records WHICH run, so the promotion can be re-read against the evidence that justified it. `halted` and `untested` stay claim-…POST/investors/strategies/{sid}/webhook· scope investors:writeCreate WebhookIssue a TradingView webhook token for this strategy. The token is returned ONCE. It is stored hashed and there is nowhere to read it from afterwards - if it is lost, rotate rather than recover. Rotating supersedes the old one immediately, so an alert still configured with it starts failing rather than quietly continuing to work.GET/investors/strategies/{sid}/webhook· scope investors:readRead WebhookThe webhook's state and its recent deliveries - including everything it refused.POST/investors/strategies/{sid}/webhook/revoke· scope investors:writeRevoke WebhookTurn the webhook off. Alerts still configured with it are refused and recorded.PATCH/investors/strategies/{strategy_id}· scope investors:writeUpdate StrategyGET/investors/strategies/{strategy_id}/versions· scope investors:readList Strategy VersionsPOST/investors/sweep/run· scope investors:writeTriggerRun a sweep now. The timer runs the same function; this exists for a person in a hurry.GET/investors/sweep/status· scope investors:readStatusWhen did the heartbeat last beat, and what did it find. THE ALERT IS THE STALENESS. No threshold is stored anywhere - the caller knows what it considers too long far better than this schema does, and a threshold in a table is one more thing to configure wrongly and then trust. THE HEARTBEAT IS PUBLIC AND THE FINDINGS ARE NOT. Anyone authenticated may learn that the sweep ran, because a tenant wh…PUT/investors/tenant-zero· scope investors:writeDesignateDesignate the profile Fitinty's own fund runs on. This grants nothing. It is a label, and the attestation is typed rather than defaulted so that somebody proposing an exemption later has to read what they are contradicting.GET/investors/tenant-zero· scope investors:readRead Tenant ZeroThe designation, and every gate answering for it. Readable by anyone authenticated on purpose. A platform that trades its own fund on the rails it sells should say which profile that is; one that hides it is making a different claim.POST/investors/universe/adopt· scope investors:writeAdoptListed -> core, by the owner's hand, and the first bars fetched NOW through the standing fetchers. Refused in words when no licensed bars come back: a core instrument with no data would sit in every picker as a blank.GET/investors/universe/agents/board· scope investors:readRead BoardGET/investors/universe/agents/{symbol}· scope investors:readRead OneGET/investors/universe/alpha-score· scope investors:readRead AlphaGET/investors/universe/autopilot· scope investors:readRead AutopilotPUT/investors/universe/budget/{source_id}· scope investors:writeSet BudgetThe dial. A budget is a limit on what the PLATFORM spends; it never buys anything.GET/investors/universe/candidates· scope investors:readRead CandidatesPOST/investors/universe/debate/{symbol}· scope investors:writeDebate NowA workspace admin asks for a debate over their own dial's candidate. Once a day per instrument.GET/investors/universe/debates· scope investors:readList DebatesGET/investors/universe/events· scope investors:readCalendarGET/investors/universe/features/{symbol}· scope investors:readRead FeaturesThe newest feature row per name for one instrument - what an agent would read tonight.GET/investors/universe/feed· scope investors:readRead FeedThe Whale Signals feed - one signal day, however many reads.GET/investors/universe/fundamentals/core· scope investors:readCore FundamentalsEvery core equity's headline figures as knowable on the as-of date (today by default).GET/investors/universe/fundamentals/{symbol}· scope investors:readOne FundamentalsGET/investors/universe/listed· scope investors:readSearch ListedSearch what the platform KNOWS but has not adopted. Reading only; adoption is a door.GET/investors/universe/money· scope investors:readRead MoneyPOST/investors/universe/run· scope investors:writeRun NowRun the nightly now, by the owner's hand. The timer runs the same function inside the sweep.GET/investors/universe/runs· scope investors:readList RunsGET/investors/universe/scoring-policy· scope investors:readRead PolicyPUT/investors/universe/scoring-policy· scope investors:writeSet My PolicyPUT/investors/universe/scoring-policy/platform· scope investors:writeSet Platform PolicyPUT/investors/universe/signal-subscription· scope investors:writeSet SubscriptionThe workspace opts in to the nightly digest, naming its channels and its consent in its own words. A channel that leaves the platform delivers only once the Emperor's counsel sign-off stands on it - the opt-in records the wish, the channel's gate decides.DELETE/investors/universe/signal-subscription· scope investors:writeClear SubscriptionGET/investors/universe/summary· scope investors:readRead SummaryPOST/investors/watchlist-items· scope investors:writeAdd Watchlist ItemPATCH/investors/watchlist-items/{item_id}/status· scope investors:writeUpdate Watchlist Item StatusPOST/investors/watchlists· scope investors:writeCreate WatchlistGET/investors/watchlists/{watchlist_id}/items· scope investors:readList Watchlist ItemsPOST/investors/whale-observations· scope investors:writeCreate Whale ObservationGET/investors/{profile_id}· scope investors:readGet ProfileGET/investors/{profile_id}/alpha-audit· scope investors:readList Alpha Audit EntriesGET/investors/{profile_id}/backtests· scope investors:readList BacktestsGET/investors/{profile_id}/cross-asset· scope investors:readList Cross Asset RelationshipsGET/investors/{profile_id}/disclosure-policies· scope investors:readList Disclosure PoliciesGET/investors/{profile_id}/institutional-flows· scope investors:readList Institutional FlowsGET/investors/{profile_id}/members· scope investors:readList MembersPOST/investors/{profile_id}/members· scope investors:writeAdd MemberGET/investors/{profile_id}/monte-carlo· scope investors:readList Monte Carlo RunsGET/investors/{profile_id}/paper-orders· scope investors:readList Paper OrdersGET/investors/{profile_id}/paper-positions· scope investors:readList Paper PositionsGET/investors/{profile_id}/portfolios· scope investors:readList PortfoliosGET/investors/{profile_id}/price-alerts· scope investors:readList AlertsGET/investors/{profile_id}/research-notes· scope investors:readList Research NotesGET/investors/{profile_id}/risk-policies· scope investors:readList Risk PoliciesGET/investors/{profile_id}/sentiment-observations· scope investors:readList Sentiment ObservationsGET/investors/{profile_id}/signals· scope investors:readList SignalsGET/investors/{profile_id}/strategies· scope investors:readList StrategiesGET/investors/{profile_id}/war-room· scope investors:readGet War RoomGET/investors/{profile_id}/watchlists· scope investors:readList WatchlistsGET/investors/{profile_id}/whale-observations· scope investors:readList Whale ObservationsPOST/personal-credit/coach/analyze· scope investors:writeAnalyzeFull educational walkthrough of the latest sandbox pull: factor standings + legitimate improvement actions. Persisted so the user can revisit it.POST/personal-credit/coach/ask· scope investors:writeAskOne educational question, grounded on the latest sandbox pull. Persisted like analyses.GET/personal-credit/coach/sessions· scope investors:readList SessionsGET/personal-credit/dispute-sandbox/cases· scope investors:readList CasesPOST/personal-credit/dispute-sandbox/cases· scope investors:writeCreate CaseAccuracy-first triage BEFORE anything else exists on the case. An item the agent assesses as accurate gets an honest educational explanation and a closed case - no letter path.GET/personal-credit/dispute-sandbox/cases/{case_id}· scope investors:readGet CasePOST/personal-credit/dispute-sandbox/cases/{case_id}/draft-letter· scope investors:writeDraft LetterPOST/personal-credit/dispute-sandbox/cases/{case_id}/responses· scope investors:writeUpload ResponseThe consumer pastes what came back (in the sandbox: any simulated bureau reply). The agent classifies the outcome and decides the next move, with its rationale stored verbatim.POST/personal-credit/dispute-sandbox/letters/{letter_id}/simulate-mailing· scope investors:writeSimulate MailingStamps the simulated consumer-mailed moment. Nothing is sent - in the future real program, this step is the consumer's own real action (or a counsel-approved send path).GET/personal-credit/monitoring/authorization· scope investors:readGet AuthorizationPOST/personal-credit/monitoring/authorization· scope investors:writeRecord AuthorizationOwner-only. Records the Emperor's attestation that legal counsel AND a real provider relationship confirm real personal-credit pulls going live. This is the ONLY way the authorization gate is ever set — no automated path creates it, and it never enables real pulls on its own.POST/personal-credit/monitoring/authorization/revoke· scope investors:writeRevoke AuthorizationOwner-only. Revokes any active provider authorization (real pulls, if they ever existed, revert to not-authorized).GET/personal-credit/monitoring/my-consent· scope investors:readGet My ConsentPOST/personal-credit/monitoring/my-consent· scope investors:writeGrant My ConsentThe real FCRA 'permissible purpose' consent click — a real person consenting to a real review of THEIR OWN credit file, and nothing else (purpose is DB-fixed to 'self'). Never bulk-applied; one user's own action, every time.POST/personal-credit/monitoring/my-consent/revoke· scope investors:writeRevoke My ConsentGET/personal-credit/monitoring/my-pulls· scope investors:readList My PullsGET/personal-credit/monitoring/readiness· scope investors:readReadinessPOST/personal-credit/monitoring/sandbox-pull· scope investors:writeRequest Sandbox PullRequires an active real consent record first — the same 'no real action without consent' gate every other personal-data flow in this codebase enforces. Always runs the internal mock (see _run_mock_pull's own docstring for exactly why), regardless of whether a provider is configured — this never contacts a real bureau, live mode or not.
Farms - १४१ doors - farms:read, farms:write
Land, sensors and what grows on them.
GET/farms· scope farms:readList FarmsPOST/farms· scope farms:writeCreate FarmPOST/farms/consignments/{consignment_id}/accept· scope farms:writeAccept ConsignmentThe HUB's word: its retail price, and the lot on ITS store. Only the hub's staff may accept.POST/farms/consignments/{consignment_id}/decline· scope farms:writeDecline ConsignmentPOST/farms/consignments/{consignment_id}/withdraw· scope farms:writeWithdraw ConsignmentThe FARM's hand, at will: what is unsold comes back; the hub's product carries the rest or comes down.POST/farms/crew-members/{member_id}/leave· scope farms:writeMember LeavesPOST/farms/crews/{crew_id}/close· scope farms:writeClose CrewPOST/farms/crews/{crew_id}/members· scope farms:writeAdd MemberA crew member IS an employee of the farm's business - the one roster, never a farm copy.POST/farms/crop-cycles· scope farms:writeCreate Crop CyclePATCH/farms/crop-cycles/{cycle_id}· scope farms:writeUpdate Crop CyclePOST/farms/devices· scope farms:writeRegister Farm DevicePOST/farms/devices/{device_id}/telemetry/synthetic· scope farms:writeCapture Synthetic TelemetryGenerates one real synthetic telemetry reading for a registered device - deterministic randomized values within realistic ranges per measurement type, matching the spec's own 'mock-first, separately gated' posture (section 11: real sensor deployment stays out of Core).POST/farms/digital-twin· scope farms:writeCreate Digital Twin AssetPOST/farms/fields· scope farms:writeCreate FieldPATCH/farms/fields/{field_id}· scope farms:writeUpdate FieldPOST/farms/harvest-plans· scope farms:writeCreate Harvest PlanPOST/farms/harvest-plans/{plan_id}/ai-propose· scope farms:writeAi Propose Harvest ReadinessAI drafts a harvest-readiness assessment from the linked crop cycle's real recorded observations/health notes - advisory only, never auto-approved, matching this project's 'AI drafts, never authoritative' pattern used everywhere else.PATCH/farms/harvest-plans/{plan_id}/status· scope farms:writeSet Harvest Plan StatusGET/farms/hubs/list· scope farms:readHubsGET/farms/integrations· scope farms:readList IntegrationsPOST/farms/inventory· scope farms:writeCreate Inventory ItemPATCH/farms/inventory/{item_id}· scope farms:writeUpdate Inventory ItemPATCH/farms/leads/{lead_id}· scope farms:writeSet Lead Statusengaged starts the response clock ONCE (first_contact_at); closed needs a reason; the moat measures from these words, so a status is a fact the farm states, never a guess.POST/farms/livestock· scope farms:writeCreate LivestockPATCH/farms/livestock/{livestock_id}· scope farms:writeUpdate LivestockPOST/farms/logistics-plans· scope farms:writeCreate Farm To Table PlanPOST/farms/logistics-plans/{plan_id}/preview-route· scope farms:writePreview RouteProduces a real, deterministic route-preview summary from the plan's own recorded fields - explicitly a preview/simulation, never a real dispatch (spec section 18: 'It may not dispatch real vehicles or promise delivery').PATCH/farms/logistics-plans/{plan_id}/status· scope farms:writeSet Farm To Table Plan StatusPOST/farms/maintenance· scope farms:writeCreate Maintenance RecordPATCH/farms/maintenance/{record_id}/status· scope farms:writeSet Maintenance StatusGET/farms/marketplace-bridge-check· scope farms:readMarketplace Bridge CheckGET/farms/mine· scope farms:readGet My Farm EndpointGET/farms/my/asks· scope farms:readMy Asks DoorGET/farms/my/orders· scope farms:readMy OrdersPOST/farms/my/region-asks· scope farms:writeMy Region AskGET/farms/my/region-asks· scope farms:readMy Region AsksGET/farms/my/rooms· scope farms:readGet My RoomsGET/farms/my/shares· scope farms:readMy SharesGET/farms/my/standing-orders· scope farms:readMy Standing OrdersPOST/farms/my/standing-orders· scope farms:writeCreate Standing OrderThe buyer's own row (their cursor, the buyer arm), and the farm's lead for it - once.PATCH/farms/my/standing-orders/{order_id}· scope farms:writeSet Standing Order StatusGET/farms/my/visits· scope farms:readMy VisitsEvery open offer with its next real slots, and my own bookings.POST/farms/offers/{offer_id}/withdraw· scope farms:writeWithdraw OfferPOST/farms/orders/{order_id}/pay· scope farms:writePay OrderThe buyer's pay door: a Stripe Checkout Session ON THE FARM'S OWN ACCOUNT, Fitinty's cut as the application fee from the one ladder (the farms rate), pending-before-Stripe.POST/farms/partner-hubs· scope farms:writeCreate Partner HubPOST/farms/payments/webhook· scope farms:writeFarms Stripe WebhookThe pillar's own verified door, the Services shape exactly: signature or refusal, captured events only, idempotent by the pending->paid guard + the fee ledger's unique payment id.POST/farms/pickup-points/{point_id}/active· scope farms:writeSet Pickup Point ActivePOST/farms/produce-batches· scope farms:writeCreate Produce BatchPATCH/farms/produce-batches/{batch_id}· scope farms:writeUpdate Produce BatchPOST/farms/produce-batches/{batch_id}/custody· scope farms:writeRecord CustodyPOST/farms/produce-batches/{batch_id}/list· scope farms:writeList BatchThe farm's own bridge: this lot becomes a real product on the farm's store - a DRAFT unless `publish` is asked, in which case the go-live gate is asked first.POST/farms/produce-batches/{batch_id}/lot-events· scope farms:writeRecord Lot Event DoorA person records custody. `withdrawn` and `recalled` need a reason and take the quantity off the lot (all of it when none is given); a listed product comes down and its stock follows - the store never sells a recalled lot. `adjusted` SETS what is available, and says why.POST/farms/produce-batches/{batch_id}/publish· scope farms:writePublish BatchPOST/farms/produce-batches/{batch_id}/recall· scope farms:writeRecall LotFM3's own recall (the reason required, the quantity off, the product DOWN) plus the notice DRAFTED from the chain. Nothing is sent from here.POST/farms/produce-batches/{batch_id}/unlist· scope farms:writeUnlist BatchPOST/farms/quality-records· scope farms:writeCreate Quality RecordPATCH/farms/quality-records/{record_id}/confirm· scope farms:writeConfirm Quality RecordPOST/farms/quotes/accept/{token}· scope farms:writeAccept QuoteSIGNED-IN acceptance: the order is born on the ACCEPTOR'S OWN CURSOR (farm_order's WITH CHECK demands buyer_user_id = the caller - a signed-in person is ordering for themself); the quote and the ask then flip on the system cursor (the buyer cannot update the farm's rows, rightly).POST/farms/quotes/{quote_id}/send· scope farms:writeSend QuoteSENT, and the accept link handed back - sending it stays the farm's hand. The ask is engaged: the farm answered.POST/farms/quotes/{quote_id}/withdraw· scope farms:writeWithdraw QuotePOST/farms/recall-notices/{notice_id}/sent-by-hand· scope farms:writeNotice Sent By HandGET/farms/seasons/kits· scope farms:readSeason KitsPOST/farms/seasons/{season_id}/status· scope farms:writeSet Season StatusThe person's word: APPROVED stamps who; DISCARDED archives the season's cycles and cancels its open tasks (never deletes); CLOSED ends an approved season.POST/farms/share-plans/{plan_id}/join· scope farms:writeJoin ShareSIGNED IN. A Stripe subscription session ON THE FARM'S OWN ACCOUNT, Fitinty's cut as a percent of each invoice from the one ladder; the farm that cannot charge is refused in words BEFORE any row is born.POST/farms/share-plans/{plan_id}/status· scope farms:writeSet Plan StatusTHE FARM'S GO-LIVE for shares: OPEN puts the plan in front of every buyer's sourcing desk, so the ONE pay-to-open gate is asked here, in the body, before the flip. CLOSED stops new joins; the shares already held keep running until their holders end them.POST/farms/shares/{share_id}/cancel· scope farms:writeCancel ShareThe holder ends their own share: the subscription is cancelled on the farm's account where one exists (the webhook's hand ends the record; it is ended here too, idempotently), a pending one just ends.POST/farms/showcase· scope farms:writeUpload Showcase AssetGET/farms/showcase/{asset_id}/download· scope farms:readDownload Showcase AssetPATCH/farms/showcase/{asset_id}/status· scope farms:writeSet Showcase Asset StatusStop 18 - PUBLISHING is the pillar's go-live: a published showcase asset is readable by anyone (showcase_asset_select, 089), so the transition to 'published' takes the ONE pay-to-open gate in the function body (the standing lesson: a Depends misses plain-Python callers). Drafting stays free; the tenant's drafts are theirs and they keep.POST/farms/storage-locations· scope farms:writeCreate Storage LocationPOST/farms/tasks· scope farms:writeCreate Farm TaskPATCH/farms/tasks/{task_id}/assign· scope farms:writeAssign Farm TaskFM14: the ONE door that puts a person on a task or takes them off - the crew-scheduling kind and its reverse both come through here, so an assignment is never a row written beside the desk. A task already started keeps its person (a hand finishes what it began).PATCH/farms/tasks/{task_id}/status· scope farms:writeSet Farm Task StatusPOST/farms/visit-bookings/{booking_id}/cancel· scope farms:writeCancel VisitThe booker's own cancel. A paid visit stays a farm's refund by hand, said in words.POST/farms/visit-bookings/{booking_id}/host-cancel· scope farms:writeHost CancelThe farm cancels a booking WITH A REASON (the booker reads it); a paid one is refunded by the farm's own hand, said.POST/farms/visit-bookings/{booking_id}/pay· scope farms:writePay VisitThe booker's pay door: a Stripe Checkout Session ON THE FARM'S OWN ACCOUNT, the fee from the one ladder at the farms rate.POST/farms/visits/{offer_id}/book· scope farms:writeBook VisitGET/farms/visits/{offer_id}/slots· scope farms:readVisit Slots DoorThe open starts of one offer over the coming days - readable by anyone signed in or not: a time is not a person (the handover windows are public by design too).POST/farms/visits/{offer_id}/status· scope farms:writeSet Visit StatusTHE FARM'S GO-LIVE for visits: OPEN puts the offer on every buyer's sourcing desk, so the one pay-to-open gate is asked here, in the body, before the flip. CLOSED stops new bookings; the bookings already made stand.DELETE/farms/windows/{window_id}· scope farms:writeDelete WindowGET/farms/{farm_id}· scope farms:readGet FarmPATCH/farms/{farm_id}· scope farms:writeUpdate FarmPATCH/farms/{farm_id}/benchmark-consent· scope farms:writeSet Benchmark ConsentConsent is the exact words, signed; withdrawal blanks them and the next reading forgets the farm.GET/farms/{farm_id}/community· scope farms:readFarm CommunityPOST/farms/{farm_id}/community/circle· scope farms:writeOpen CircleThe buyers' circle: the storefront's open room, born through the organ's one writer.POST/farms/{farm_id}/community/exchange· scope farms:writeOpen ExchangeThe growers' exchange - knowledge, equipment, bulk buys - joined by anyone on a farm's membership.POST/farms/{farm_id}/community/share-room· scope farms:writeOpen Share RoomThe CSA members' room pins to one of THIS farm's plans; one live room per plan.POST/farms/{farm_id}/consignments· scope farms:writeConsignThe farm's offer, with the consent words RECORDED; born 'offered' on the farm's own cursor.GET/farms/{farm_id}/consignments· scope farms:readConsignmentsBoth sides on one door: what this farm consigned (and is owed), what this hub received (and owes).POST/farms/{farm_id}/costs· scope farms:writeRecord CostPOST/farms/{farm_id}/crews· scope farms:writeCreate CrewGET/farms/{farm_id}/crews· scope farms:readCrewsThe crews with their people (the roster's names, their training as the roster's own door reads it), the work records (capped), the period's pay lines.GET/farms/{farm_id}/crop-cycles· scope farms:readList Crop CyclesGET/farms/{farm_id}/devices· scope farms:readList Farm DevicesGET/farms/{farm_id}/digital-twin· scope farms:readList Digital Twin AssetsPATCH/farms/{farm_id}/directory· scope farms:writeSet DirectoryGET/farms/{farm_id}/events· scope farms:readFarm EventsThe farm's events, newest first, with the seats taken - and the list says when it stops.POST/farms/{farm_id}/events· scope farms:writeCreate Farm EventPOST/farms/{farm_id}/events/{event_id}/publish· scope farms:writePublish Farm EventPublishing IS the farm's go-live for an event: the pay-to-open gate in the body, then FitinEvents' own publish and open-to-public doors, so the seat's door is the platform's one ticket rail.GET/farms/{farm_id}/fields· scope farms:readList FieldsPOST/farms/{farm_id}/handoffs· scope farms:writeRecord HandoffGET/farms/{farm_id}/handoffs· scope farms:readHandoffsGET/farms/{farm_id}/harvest-plans· scope farms:readList Harvest PlansGET/farms/{farm_id}/inventory· scope farms:readList InventoryGET/farms/{farm_id}/leads· scope farms:readFarm LeadsGET/farms/{farm_id}/livestock· scope farms:readList LivestockGET/farms/{farm_id}/logistics-plans· scope farms:readList Farm To Table PlansGET/farms/{farm_id}/lot-events· scope farms:readList Lot EventsGET/farms/{farm_id}/maintenance· scope farms:readList Maintenance RecordsGET/farms/{farm_id}/members· scope farms:readList Farm MembersPOST/farms/{farm_id}/members· scope farms:writeAdd Farm MemberGET/farms/{farm_id}/moat· scope farms:readMoatGET/farms/{farm_id}/money· scope farms:readFarm MoneyWhat this farm has taken on its own rail - read from the ONE fee ledger, never typed.POST/farms/{farm_id}/offers· scope farms:writeMint Offer DoorThe hand's offer: a lead, a lot (or the best-matching listed one), the next three REAL open times.GET/farms/{farm_id}/open-slots· scope farms:readOpen Slots DoorGET/farms/{farm_id}/partner-hubs· scope farms:readList Partner HubsGET/farms/{farm_id}/pickup-points· scope farms:readPickup PointsPOST/farms/{farm_id}/pickup-points· scope farms:writeCreate Pickup PointGET/farms/{farm_id}/produce-batches· scope farms:readList Produce BatchesGET/farms/{farm_id}/quality-records· scope farms:readList Quality RecordsPOST/farms/{farm_id}/quotes· scope farms:writeCreate QuoteThe farm's priced answer, born a DRAFT. Every line names a lot of this farm; the amount is the sum.GET/farms/{farm_id}/quotes· scope farms:readList QuotesGET/farms/{farm_id}/recall-notices· scope farms:readRecall NoticesGET/farms/{farm_id}/regional-demand· scope farms:readRegional Demand DoorGET/farms/{farm_id}/roster· scope farms:readRosterThe farm's people are the business's employees - hris_employee, never a farm copy.GET/farms/{farm_id}/run-sheet· scope farms:readRun Sheet DoorGET/farms/{farm_id}/seasons· scope farms:readFarm SeasonsEvery season with its cycles' PLAN vs HARVEST (the batches that name the cycle), its costs by kind, its open tasks; and the farm's costs (capped). Revenue is the one money read, not here.POST/farms/{farm_id}/seasons/propose· scope farms:writePropose SeasonA season DRAFT from the segment's kit: the cycles and the tasks with their dates from the kit's offsets. Deterministic from the data - a person approves, edits or discards it.POST/farms/{farm_id}/share-plans· scope farms:writeCreate PlanA share plan, born a DRAFT. The window it names is one of THIS farm's handover windows.GET/farms/{farm_id}/shares· scope farms:readFarm SharesThe plans, the members holding a share, and the cycles paid - the farm's own rows.GET/farms/{farm_id}/showcase· scope farms:readList Showcase AssetsGET/farms/{farm_id}/standing-orders· scope farms:readFarm Standing OrdersGET/farms/{farm_id}/storage-locations· scope farms:readList Storage LocationsGET/farms/{farm_id}/tasks· scope farms:readList Farm TasksGET/farms/{farm_id}/telemetry· scope farms:readList TelemetryGET/farms/{farm_id}/trace· scope farms:readTraceA recall is a query: by lot code, or by product over harvest dates.GET/farms/{farm_id}/visit-bookings· scope farms:readFarm Visit BookingsThe offers and the bookings coming up - the farm's own rows.POST/farms/{farm_id}/visits· scope farms:writeCreate VisitA visit offer, born a DRAFT. Price 0 is a free visit, said as such.POST/farms/{farm_id}/windows· scope farms:writeAdd WindowPOST/farms/{farm_id}/work-records· scope farms:writeRecord WorkQuantity x rate, earned computed ONCE here and never re-derived; the rate is the member's unless a different one is stated for this record (a different task pays a different rate).POST/farms/{hub_farm_id}/payouts· scope farms:writeRecord PayoutRule 3: the hub RECORDS what it paid a farm by its own hand; no transfer is made from here.
Manufacturing - १५५ doors - manufacturing:read, manufacturing:write
Products, plans and production runs.
GET/manufacturing· scope manufacturing:readList FactoriesPOST/manufacturing· scope manufacturing:writeCreate FactoryPOST/manufacturing/bom-items· scope manufacturing:writeCreate Bom ItemDELETE/manufacturing/bom-items/{item_id}· scope manufacturing:writeDelete Bom ItemPOST/manufacturing/boms· scope manufacturing:writeCreate BomGET/manufacturing/boms/{bom_id}/explode· scope manufacturing:readExplodeGET/manufacturing/boms/{bom_id}/items· scope manufacturing:readList Bom ItemsPOST/manufacturing/boms/{bom_id}/revise· scope manufacturing:writeRevise BomGET/manufacturing/boms/{bom_id}/revisions· scope manufacturing:readList Bom RevisionsPATCH/manufacturing/boms/{bom_id}/status· scope manufacturing:writeSet Bom StatusPOST/manufacturing/capacity/{listing_id}/list· scope manufacturing:writeList CapacityTHE GO-LIVE: the declaration reaches the public page and the directory, so the ONE pay-to-open gate is asked here, in the body, before the word 'listed'.POST/manufacturing/capacity/{listing_id}/withdraw· scope manufacturing:writeWithdraw CapacityPOST/manufacturing/cost-energy-estimates· scope manufacturing:writeCreate Cost Energy EstimatePOST/manufacturing/crew-members/{member_id}/leave· scope manufacturing:writeMember LeavesPOST/manufacturing/crews/{crew_id}/close· scope manufacturing:writeClose CrewPOST/manufacturing/crews/{crew_id}/members· scope manufacturing:writeAdd MemberA crew member IS an employee of the factory's business - the one roster, never a factory copy.POST/manufacturing/digital-twins· scope manufacturing:writeCreate Digital Twin ProjectPATCH/manufacturing/digital-twins/{twin_id}/status· scope manufacturing:writeSet Twin StatusPOST/manufacturing/documents· scope manufacturing:writeUpload DocumentGET/manufacturing/documents/{doc_id}/download· scope manufacturing:readDownload DocumentPATCH/manufacturing/documents/{doc_id}/status· scope manufacturing:writeSet Document StatusPOST/manufacturing/documents/{doc_id}/versions· scope manufacturing:writeAdd Document VersionA new file is a new version row; the document's current fields point at it; the old version stays a row (append-only) and its file stays on disk.GET/manufacturing/documents/{doc_id}/versions· scope manufacturing:readList Document VersionsPATCH/manufacturing/documents/{document_id}/custody· scope manufacturing:writeSet CustodyWhose drawing this is, and how long it is kept.POST/manufacturing/documents/{document_id}/licenses· scope manufacturing:writeWrite LicenseThe terms in the maker's words for one licensee, on a file that exists; born a DRAFT with no link and no price.POST/manufacturing/documents/{document_id}/read-geometry· scope manufacturing:writeRead GeometryRead a design file already in this factory's own vault. It writes a READING and nothing else - no part, no bill, no routing until a person asks for them.POST/manufacturing/engineering-changes· scope manufacturing:writeCreate EcrPOST/manufacturing/engineering-changes/{ecr_id}/affected· scope manufacturing:writeAdd AffectedThe request names a row of its own factory - read on the caller's cursor, so a row that is not the factory's is 'not found', never linked.GET/manufacturing/engineering-changes/{ecr_id}/affected· scope manufacturing:readList AffectedDELETE/manufacturing/engineering-changes/{ecr_id}/affected/{item_row_id}· scope manufacturing:writeRemove AffectedPATCH/manufacturing/engineering-changes/{ecr_id}/effective· scope manufacturing:writeSet EffectiveA date a person sets, on an approved request only.PATCH/manufacturing/engineering-changes/{ecr_id}/status· scope manufacturing:writeSet Ecr StatusPOST/manufacturing/geometry/{geometry_id}/propose· scope manufacturing:writePropose From GeometryDRAFT a part, a bill line and a routing from what the file said - each through the pillar's own existing writers, each a draft, none of them approved by this door.GET/manufacturing/integrations· scope manufacturing:readList IntegrationsPOST/manufacturing/inventory· scope manufacturing:writeCreate Inventory ItemPOST/manufacturing/inventory-locations· scope manufacturing:writeCreate Inventory LocationPATCH/manufacturing/leads/{lead_id}· scope manufacturing:writeSet Lead Statusengaged starts the response clock ONCE (first_contact_at); closed needs a reason. The moat measures from these words, so a status is a fact the factory states, never a guess.POST/manufacturing/licenses/{license_id}/offer· scope manufacturing:writeOffer LicenseTHE GO-LIVE: the link is minted and the terms can be read by whoever holds it, so the ONE pay-to-open gate is asked here, in the body, before the word 'offered'.POST/manufacturing/licenses/{license_id}/void· scope manufacturing:writeVoid LicensePOST/manufacturing/lots/{lot_id}/hand-off· scope manufacturing:writeHand Off LotTHE LAST LINK OF THE CHAIN: what was made LEFT this factory, and into whose hands. `handed_over` was a declared kind of this chain from MF5 with NO door that could write it - the trace could say what a lot was made from and never that it had gone. A recall answers "who has it" out of exactly these rows, so the gap was the one that mattered. IT RECORDS AND DOES NOT DISPATCH. Under his MF5 ruling …POST/manufacturing/lots/{lot_id}/recall· scope manufacturing:writeRecall LotA recall takes the store product DOWN through MF3's own bridge - never a second writer - marks every lot this one reached, and DRAFTS the notice. NOTHING IS SENT: the words stand here and a person carries them out by their own hand.GET/manufacturing/lots/{lot_id}/trace· scope manufacturing:readTraceWHAT WENT INTO THIS, and WHAT THIS WENT INTO. Both walks are queries over the genealogy rows; nothing is cached, so the answer is always the chain as it stands.POST/manufacturing/manufacturability-reviews· scope manufacturing:writeCreate Manufacturability ReviewPOST/manufacturing/manufacturability-reviews/{review_id}/ai-assist· scope manufacturing:writeAi Assist Manufacturability ReviewAI drafts manufacturability findings from the review's own recorded criteria notes and the linked product's real fields - advisory only, a qualified reviewer must still approve, matching this project's 'AI drafts, never authoritative' pattern.PATCH/manufacturing/manufacturability-reviews/{review_id}/status· scope manufacturing:writeSet Manufacturability Review StatusGET/manufacturing/mine· scope manufacturing:readGet My Factory EndpointGET/manufacturing/my/asks· scope manufacturing:readMy Asks DoorA person's own asks across makers - ownership decided in code from the account email, per row (the ONE ownership rule); the lead table itself stays the factory's alone.GET/manufacturing/my/orders· scope manufacturing:readMy OrdersPOST/manufacturing/my/region-asks· scope manufacturing:writeOpen Region AskGET/manufacturing/my/region-asks· scope manufacturing:readMy Region AsksThe buyer's own asks and where each one got to. NO MAKER'S ROWS are in this answer.POST/manufacturing/orders/{order_id}/pay· scope manufacturing:writePay OrderThe buyer's pay door: a Stripe Checkout Session ON THE FACTORY'S OWN ACCOUNT, Fitinty's cut as the application fee from the one ladder (the manufacturing rate), pending-before-Stripe.POST/manufacturing/partner-connections/{connection_id}/disconnect· scope manufacturing:writeDisconnectDISCONNECT ERASES. A flag on a kept secret is not a disconnection.POST/manufacturing/partner-connections/{connection_id}/read· scope manufacturing:writeRead Through DoorTHE TRANSPORT IS NOT A DOOR PARAMETER. FastAPI reads an endpoint's arguments as the request's own, and a Callable has no schema - so the whole of /openapi.json answered 500 the first time this was written that way, which takes every screen down, not one. The act is a plain function (540's shape) and the guard injects a transport there with no network at all.POST/manufacturing/partner-orders/{order_id}/approve· scope manufacturing:writeApprove OrderA PERSON'S WORD, with what it costs stated back to them. Approving still sends nothing.POST/manufacturing/partner-orders/{order_id}/transmit· scope manufacturing:writeTransmit Order DoorThe only act that reaches outside, behind the lock. Same shape as the read above: the door takes no transport, so the schema stays buildable.POST/manufacturing/partner-rfqs/{rfq_id}/answer· scope manufacturing:writeRecord AnswerTheir answer, as they gave it. A decline is an answer and is recorded as one - a partner who says no twice is a fact worth having.POST/manufacturing/partner-rfqs/{rfq_id}/sent· scope manufacturing:writeMark SentA PERSON MARKS IT SENT. This platform puts nothing in a stranger's inbox (the standing real-communications law), so `sent` is a fact somebody records, and `sent_how` says how.PATCH/manufacturing/partners/{partner_id}· scope manufacturing:writeUpdate PartnerPOST/manufacturing/partners/{partner_id}/certifications· scope manufacturing:writeAdd CertificationA CLAIM, AND WHETHER ANYBODY SAW THE PAPER. `seen` records that a person looked at the certificate itself - an unverified claim stays a claim, and the register says which it is, because a certification nobody checked is exactly the kind of thing an auditor asks about.POST/manufacturing/partners/{partner_id}/connect· scope manufacturing:writeConnect Partner513's SHAPE: the credential is encrypted the moment it arrives and never returned. Connecting reads nothing and commits nothing - it only makes the reads below possible.POST/manufacturing/parts· scope manufacturing:writeCreate PartPATCH/manufacturing/parts/{part_id}/cost· scope manufacturing:writeSet Part CostPOST/manufacturing/payments/webhook· scope manufacturing:writeManufacturing Stripe WebhookThe pillar's own verified door, the Services shape exactly: signature or refusal, captured events only, idempotent by the pending->paid guard + the fee ledger's unique payment id.POST/manufacturing/production-orders· scope manufacturing:writeCreate Production OrderPOST/manufacturing/production-orders/{order_id}/complete· scope manufacturing:writeComplete OrderWhat was actually made. The reservations become consumption and the stock comes off; the finished-good LOT is minted and every source lot is LINKED to it, which is the only reason a recall can be answered later.POST/manufacturing/production-orders/{order_id}/inspections· scope manufacturing:writeRecord InspectionA record at the routing's own inspection point. A fail or a rework states WHY - a verdict with no words teaches nobody anything, and it is the one somebody reads a year later.GET/manufacturing/production-orders/{order_id}/inspections· scope manufacturing:readList InspectionsGET/manufacturing/production-orders/{order_id}/labour· scope manufacturing:readOrder LabourGET/manufacturing/production-orders/{order_id}/materials· scope manufacturing:readList MaterialsWhat this order holds, and from which lot. A line with no lot named is shown as exactly that - the trace stops there, and the desk says so rather than implying a chain that does not exist.POST/manufacturing/production-orders/{order_id}/release· scope manufacturing:writeRelease OrderA person releases the order to their own floor. The bill is exploded, what exists is RESERVED, and what is short is NAMED - never hidden, never a refusal.POST/manufacturing/production-orders/{order_id}/simulate· scope manufacturing:writeSimulate Production OrderComputes a real simulated material/time/cost/energy summary from the order's own linked BOM items and routing steps - refuses to fabricate a number for data that doesn't exist, matching this project's 'no fake data' discipline.POST/manufacturing/production-orders/{order_id}/start· scope manufacturing:writeStart OrderPATCH/manufacturing/production-orders/{order_id}/status· scope manufacturing:writeSet Production Order StatusPOST/manufacturing/products· scope manufacturing:writeCreate ProductPATCH/manufacturing/products/{product_id}· scope manufacturing:writeUpdate ProductPATCH/manufacturing/products/{product_id}/cost-inputs· scope manufacturing:writeSet Product Cost InputsGET/manufacturing/products/{product_id}/cost-model· scope manufacturing:readRead Cost ModelPOST/manufacturing/products/{product_id}/cost-model/record· scope manufacturing:writeRecord Cost ModelThe computed model recorded as an estimate row told apart from a typed one (computed = true, the breakdown kept), so the desk shows both and the plan's readers can compare them.GET/manufacturing/products/{product_id}/export-line· scope manufacturing:readRead Export LineTHE DOWNLOAD DOOR'S OWN SENTENCE. The store delivers the file through its order-verified door; this is the words that belong beside it, which the seller sees on their own desk and the buyer sees on the page that hands over the file.POST/manufacturing/products/{product_id}/list· scope manufacturing:writeList ProductThe factory's own bridge: this product becomes a real product on the factory's store - a DRAFT unless `publish` is asked, in which case the go-live gate is asked first.POST/manufacturing/products/{product_id}/publish· scope manufacturing:writePublish ProductPATCH/manufacturing/products/{product_id}/statements· scope manufacturing:writeSet StatementsThe rights and export words on a product - a person's own, never derived and never drafted.POST/manufacturing/products/{product_id}/unlist· scope manufacturing:writeUnlist ProductPOST/manufacturing/prototypes· scope manufacturing:writeCreate Prototype ProjectPATCH/manufacturing/prototypes/{proto_id}/status· scope manufacturing:writeSet Prototype StatusGET/manufacturing/pulse/readiness· scope manufacturing:readPulse ReadinessWHETHER THERE IS ANYTHING TO SELL YET. The plan's Pulse subscription is a subscription to this feed; it arrives when the feed has rows behind it, and this says plainly how far from that we are. No capability, plan row or pack ships before then.POST/manufacturing/quotes/accept/{token}· scope manufacturing:writeAccept QuoteSIGNED-IN acceptance: the order is born on the ACCEPTOR'S OWN CURSOR (factory_order's WITH CHECK demands buyer_user_id = the caller); the quote and the ask then flip on the system cursor (the buyer cannot update the factory's rows, rightly).POST/manufacturing/quotes/{quote_id}/send· scope manufacturing:writeSend QuoteSENT, and the accept link handed back - sending it stays the factory's hand. The ask is engaged: the factory answered, and the moat's response clock stops on this stamp.POST/manufacturing/quotes/{quote_id}/withdraw· scope manufacturing:writeWithdraw QuotePATCH/manufacturing/recall-notices/{notice_id}/delivered· scope manufacturing:writeMark DeliveredThe factory records that IT delivered the notice, and how. Fitinty still sent nothing - this is a person writing down what they did outside the platform (the mark-posted shape, GL3).POST/manufacturing/routing-steps· scope manufacturing:writeCreate Routing StepPOST/manufacturing/routings· scope manufacturing:writeCreate RoutingPATCH/manufacturing/routings/{routing_id}/status· scope manufacturing:writeSet Routing StatusGET/manufacturing/routings/{routing_id}/steps· scope manufacturing:readList Routing StepsPOST/manufacturing/work-centers· scope manufacturing:writeCreate Work CenterPATCH/manufacturing/work-centers/{wc_id}/rates· scope manufacturing:writeSet Work Center RatesPATCH/manufacturing/work-order-materials/{material_id}/lot· scope manufacturing:writeName The LotWHICH lot this line is taking from. Naming it is what lets a recall walk forward from a bad part to every finished good it reached.GET/manufacturing/{factory_id}· scope manufacturing:readGet FactoryPATCH/manufacturing/{factory_id}· scope manufacturing:writeUpdate FactoryGET/manufacturing/{factory_id}/boms· scope manufacturing:readList BomsGET/manufacturing/{factory_id}/capacity· scope manufacturing:readCapacity DeskEvery declaration this factory made, with the centre's name, and what a stranger reads today.POST/manufacturing/{factory_id}/capacity· scope manufacturing:writeDeclare CapacityA declaration, born a DRAFT: nothing reaches the public until it is listed.GET/manufacturing/{factory_id}/community· scope manufacturing:readFactory CommunityPOST/manufacturing/{factory_id}/community/circle· scope manufacturing:writeOpen Makers CirclePOST/manufacturing/{factory_id}/community/customers-room· scope manufacturing:writeOpen Customers RoomOne live room per product, pinned to a product of THIS factory.POST/manufacturing/{factory_id}/community/exchange· scope manufacturing:writeOpen Engineers ExchangeGET/manufacturing/{factory_id}/cost-energy-estimates· scope manufacturing:readList Cost Energy EstimatesPOST/manufacturing/{factory_id}/crews· scope manufacturing:writeCreate CrewGET/manufacturing/{factory_id}/crews· scope manufacturing:readCrewsThe crews, their people, and the period's pay lines - what the payroll run would take.GET/manufacturing/{factory_id}/digital-twins· scope manufacturing:readList Digital Twin ProjectsPATCH/manufacturing/{factory_id}/directory· scope manufacturing:writeSet DirectoryThe dial. No second gate: being FOUND needs something LISTED, and listing is the go-live the factory already paid for (623) - a paywall on the dial would charge twice for one door.GET/manufacturing/{factory_id}/documents· scope manufacturing:readList DocumentsGET/manufacturing/{factory_id}/engineering-changes· scope manufacturing:readList EcrsGET/manufacturing/{factory_id}/floor· scope manufacturing:readFloorEvery work order with what it reserved and what it was inspected at, and the stock behind it - available computed, never stored.GET/manufacturing/{factory_id}/forge· scope manufacturing:readForge For FactoryTHE DEMAND ORGAN, IMPORTED. The Inventor Forge lives in Marketing and stays there; this door reads it beside the factory that could answer it, and says which solutions were actually built.POST/manufacturing/{factory_id}/forge/build· scope manufacturing:writeBuild A SolutionONE DOOR: a Forge solution becomes a product of this factory. The solution row keeps its origin and gains a pointer; the product names the friction point it answers, the way MF12's products name a need. Nothing is moved, copied or deleted in Marketing's organ.POST/manufacturing/{factory_id}/forge/vassal· scope manufacturing:writeName The FactoryA vassal in the Forge's roster IS this factory. Naming it turns a list of names into a list of tenants somebody can actually ask for a quote.GET/manufacturing/{factory_id}/geometry· scope manufacturing:readList GeometryGET/manufacturing/{factory_id}/inventory· scope manufacturing:readList InventoryGET/manufacturing/{factory_id}/inventory-locations· scope manufacturing:readList Inventory LocationsGET/manufacturing/{factory_id}/leads· scope manufacturing:readFactory LeadsGET/manufacturing/{factory_id}/licenses· scope manufacturing:readLicenses DeskGET/manufacturing/{factory_id}/lots· scope manufacturing:readList LotsPOST/manufacturing/{factory_id}/lots/receive· scope manufacturing:writeReceive LotMaterial ARRIVED, with the supplier's own lot code on it. This is where a trace starts: without it, a recall can say which order a part went into but never where the part came from.GET/manufacturing/{factory_id}/manufacturability-reviews· scope manufacturing:readList Manufacturability ReviewsGET/manufacturing/{factory_id}/members· scope manufacturing:readList Factory MembersPOST/manufacturing/{factory_id}/members· scope manufacturing:writeAdd Factory MemberGET/manufacturing/{factory_id}/moat· scope manufacturing:readMoatGET/manufacturing/{factory_id}/money· scope manufacturing:readFactory MoneyWhat this factory has taken on its own rail - read from the ONE fee ledger, never typed.POST/manufacturing/{factory_id}/partner-orders· scope manufacturing:writeDraft OrderA purchase order on paper. The amount is the SUM OF THE LINES, never typed.GET/manufacturing/{factory_id}/partner-orders· scope manufacturing:readList OrdersPOST/manufacturing/{factory_id}/partner-rfqs· scope manufacturing:writeCreate RfqA quote request, born a DRAFT. Nothing leaves this platform when it is written.GET/manufacturing/{factory_id}/partner-rfqs· scope manufacturing:readList RfqsPOST/manufacturing/{factory_id}/partners· scope manufacturing:writeCreate PartnerA company outside Fitinty, written down by somebody who deals with them.GET/manufacturing/{factory_id}/partners· scope manufacturing:readList PartnersThe partners this factory keeps, each with what it is certified for and what it has answered.GET/manufacturing/{factory_id}/parts· scope manufacturing:readList PartsGET/manufacturing/{factory_id}/production-orders· scope manufacturing:readList Production OrdersGET/manufacturing/{factory_id}/products· scope manufacturing:readList ProductsGET/manufacturing/{factory_id}/prototypes· scope manufacturing:readList Prototype ProjectsGET/manufacturing/{factory_id}/pulse· scope manufacturing:readRead PulseThe nights this factory has behind it, newest first - and what the trend of them says, which is only a trend once there are at least two.GET/manufacturing/{factory_id}/quote-basis· scope manufacturing:readQuote BasisWHAT A LINE SHOULD COST, from this factory's own rows - MF2's computed model at this quantity, offered as the figure a price starts from. Never a price: the factory decides what it charges.POST/manufacturing/{factory_id}/quotes· scope manufacturing:writeCreate QuoteThe factory's priced answer, born a DRAFT. The amount is the sum of the lines, never typed.GET/manufacturing/{factory_id}/quotes· scope manufacturing:readList QuotesGET/manufacturing/{factory_id}/recall-notices· scope manufacturing:readList NoticesGET/manufacturing/{factory_id}/roster· scope manufacturing:readRosterThe factory's people are the business's employees - `hris_employee`, never a factory copy.GET/manufacturing/{factory_id}/routings· scope manufacturing:readList RoutingsPATCH/manufacturing/{factory_id}/segment· scope manufacturing:writeSet SegmentWhat kind of maker this is. A KIT, never a fork - and the industry key the compliance register reads, so the duties a processor is shown differ from the ones an electronics shop is shown.POST/manufacturing/{factory_id}/twin-render· scope manufacturing:writeRun Twin RenderTHE DOOR THAT REFUSES. It exists so the refusal is a sentence somebody reads rather than a missing feature they guess at - and so the day it opens, nothing has to be invented.GET/manufacturing/{factory_id}/twin-render/readiness· scope manufacturing:readTwin Render ReadinessTWO GATES, BOTH NAMED. The render is metered and priced; it runs when the controlled test is recorded and he says go, and not one minute sooner. THE CALIBRATION IS READ ON THE SYSTEM CURSOR, and it must be: `render_calibration` is owner-only (one policy, `is_owner`), so a factory's own cursor reads ZERO runs through the wall and this door would answer "the gate is shut, 0 of 3 runs" on the day it…GET/manufacturing/{factory_id}/work-centers· scope manufacturing:readList Work CentersPOST/manufacturing/{factory_id}/work-records· scope manufacturing:writeRecord WorkHours x an hourly rate, or quantity x a rate a piece. EARNED IS COMPUTED ONCE HERE and never re-derived; the rate is the member's own unless this record states a different one (a different task pays a different rate).GET/needs· scope manufacturing:readList NeedsThe catalogue: open needs first, newest signal first. Every signed-in person reads it (an aggregate with no person in it - the registry's disclosure shape).POST/needs· scope manufacturing:writeMint NeedA person writes a need (origin 'hand', in their name), or adopts a Forge friction point they can read (origin 'forge', linked to it - the demand organ imported, never copied).GET/needs/signals· scope manufacturing:readList SignalsThe latest signals in the window: what is being asked for and how often, inside and outside.PATCH/needs/{need_id}/status· scope manufacturing:writeSet Need StatusIts author or the Emperor moves it. A dismissal states why (422 without); 'answered' names what answered it in the reason when a person knows.
Logistics - १४३ doors - logistics:read, logistics:write
Dispatch, routes and last-mile delivery.
GET/last-mile/drivers· scope logistics:readList DriversPOST/last-mile/drivers· scope logistics:writeCreate DriverPATCH/last-mile/drivers/{driver_id}· scope logistics:writeUpdate DriverDELETE/last-mile/drivers/{driver_id}· scope logistics:writeDelete DriverGET/last-mile/jobs· scope logistics:readList JobsPOST/last-mile/jobs· scope logistics:writeCreate JobGET/last-mile/jobs/{job_id}· scope logistics:readGet JobDELETE/last-mile/jobs/{job_id}· scope logistics:writeDelete JobPOST/last-mile/jobs/{job_id}/assign· scope logistics:writeAssign DriverPOST/last-mile/jobs/{job_id}/cancel· scope logistics:writeCancel JobPOST/last-mile/jobs/{job_id}/deliver· scope logistics:writeMark DeliveredPOST/last-mile/jobs/{job_id}/fail· scope logistics:writeMark FailedPOST/last-mile/jobs/{job_id}/in-transit· scope logistics:writeMark In TransitPOST/last-mile/jobs/{job_id}/pickup· scope logistics:writeMark Picked UpGET/last-mile/my-organizations· scope logistics:readMy OrganizationsGET/logistics· scope logistics:readList OperatorsPOST/logistics· scope logistics:writeCreate OperatorPOST/logistics/adapters/{connection_id}/disconnect· scope logistics:writeDisconnectDISCONNECT ERASES. A flag on a kept secret is not a disconnection - and the row's CHECK refuses one.POST/logistics/adapters/{connection_id}/read· scope logistics:writeRead Through DoorTHE TRANSPORT IS NOT A DOOR PARAMETER (MF13). The act is a plain function; the door only turns a shut lock into words.POST/logistics/claims· scope logistics:writeCreate ClaimPATCH/logistics/claims/{claim_id}/status· scope logistics:writeSet Claim StatusPOST/logistics/crew-members/{member_id}/leave· scope logistics:writeMember LeavesPOST/logistics/crews/{crew_id}/close· scope logistics:writeClose CrewPOST/logistics/crews/{crew_id}/members· scope logistics:writeAdd MemberA crew member IS an employee of the carrier's own business - the one roster, never a copy.POST/logistics/deliveries/synthetic· scope logistics:writeCreate Delivery JobPATCH/logistics/deliveries/{job_id}/status· scope logistics:writeSet Delivery StatusPOST/logistics/fleets· scope logistics:writeCreate FleetPOST/logistics/fuel-rebate-signals· scope logistics:writeCreate Fuel Rebate SignalPOST/logistics/gateways· scope logistics:writeRegister GatewayPOST/logistics/handoffs· scope logistics:writeCreate HandoffPOST/logistics/handoffs/ask· scope logistics:writeAskGET/logistics/handoffs/asked· scope logistics:readAskedThe origin's own asks. Read on the CALLER'S cursor through 656's origin arm; the carrier's name, the quote it SENT and the job's work word are the other side's walled rows, so they are read on the system cursor for EXACTLY the ids this caller's own rows name (591's law).GET/logistics/handoffs/words· scope logistics:readWordsPATCH/logistics/handoffs/{handoff_id}/status· scope logistics:writeSet Handoff StatusPOST/logistics/hubs· scope logistics:writeCreate HubGET/logistics/integrations· scope logistics:readList IntegrationsPOST/logistics/jobs/{job_id}/cancel· scope logistics:writeCancelPOST/logistics/jobs/{job_id}/could-not-deliver· scope logistics:writeCould Not DeliverPOST/logistics/jobs/{job_id}/deliver· scope logistics:writeDeliverPOST/logistics/jobs/{job_id}/driver· scope logistics:writeName The DriverA RECORD of who drives. Nobody is told; the driver sees the run on their own page. A licence that is expired, or that nobody has seen, is THE CARRIER'S DECISION (LG6's rule for an overdue vehicle) - and the record SAYS SO rather than deciding.POST/logistics/jobs/{job_id}/pay· scope logistics:writePay JobThe shipper's pay door: a Checkout Session ON THE CARRIER'S OWN ACCOUNT, Fitinty's cut as the application fee from the one ladder, pending-before-Stripe. THE BROKERAGE LINE IS ASKED HERE, IN THE BODY, before anything is created. LG5b: the charge is a PAYMENT ROW of the kind the job is owed next (`plan_payment`, the one reading - deposit first where the quote asked one, the balance after, else the …POST/logistics/jobs/{job_id}/repeat· scope logistics:writeRepeat Job"REPEAT THIS RUN": the shipper's standing yes, in the one definition's words, recorded on the row. The route is born on the SHIPPER'S cursor (WITH CHECK demands the buyer arm). Nothing is minted here - the next job appears when THIS one is delivered, and nothing is charged until they pay it.POST/logistics/jobs/{job_id}/schedule· scope logistics:writeScheduleA day, and optionally one of the carrier's OWN vehicles. A record of a person's decision.POST/logistics/jobs/{job_id}/start· scope logistics:writeStartPOST/logistics/jobs/{job_id}/stops· scope logistics:writeAdd StopPOST/logistics/jobs/{job_id}/tip· scope logistics:writeTip JobTHE SHIPPER'S ACT ONLY: a gratuity on a PAID job, on the carrier's own account, with NO application fee (the row's own rule). The brokerage line is asked first - a tip moves money on the same account.POST/logistics/lane-contracts/{contract_id}/agree· scope logistics:writeAgree Lane ContractTHE SHIPPER'S HALF of the two recorded yeses. The row names them, and the body asks again.POST/logistics/lane-contracts/{contract_id}/end· scope logistics:writeEnd Lane ContractEither side ends it with a reason; the row stays as history and a new one may follow.POST/logistics/lanes/{lane_id}/active· scope logistics:writeSet Lane ActivePATCH/logistics/leads/{lead_id}· scope logistics:writeSet Lead Statusengaged starts the response clock ONCE (first_contact_at); closed needs a reason. LG4's moat measures from these words, so a status is a fact the carrier states, never a guess.POST/logistics/loads/synthetic· scope logistics:writeCreate Synthetic LoadPATCH/logistics/loads/{load_id}/assign· scope logistics:writeAssign LoadProposes a synthetic dispatch assignment - a human-reviewed proposal, never a real reservation or brokerage commitment (spec section 14).PATCH/logistics/loads/{load_id}/status· scope logistics:writeSet Load StatusPOST/logistics/ltl-groups· scope logistics:writeCreate Ltl GroupGET/logistics/mine· scope logistics:readGet My Operator EndpointGET/logistics/my/asks· scope logistics:readMy Asks DoorA person's own asks across carriers - ownership decided in code from the account email, per row (the ONE ownership rule); the lead table itself stays the carrier's alone.GET/logistics/my/jobs· scope logistics:readMy JobsGET/logistics/my/money· scope logistics:readMy MoneyTHE SHIPPER'S OWN: what each job is owed next (the ONE reading), the payments made, the routes they started and the contracts proposed to them - the carriers named on the system cursor for exactly those rows, since the shipper's cursor reads nothing of a carrier.GET/logistics/my/runs· scope logistics:readMy RunsWhat is recorded ABOUT THIS PERSON, read on THEIR OWN cursor through 659's `_own` arms: their crews, their pay lines as written, their qualification file, their hours-of-service days. Their RUNS - the jobs that name them - are the carrier's walled rows, so those are read on the system cursor for EXACTLY this person's own employee ids, from a whitelist (no price, no shipper, no ask).POST/logistics/my/runs/{job_id}/deliver· scope logistics:writeDeliver My RunTHE ONE THING A DRIVER WRITES. The job is the carrier's walled row and the driver has no seat at the desk, so the wall is NOT widened: the body asks, on the driver's OWN cursor, which roster rows are theirs; the job is then read on the system cursor and must NAME one of them; and the delivery is written by LG6's ONE delivery writer, with the driver as the actor on the chain and in the trail.POST/logistics/offers/{offer_id}/withdraw· scope logistics:writeWithdraw OfferPOST/logistics/payments/webhook· scope logistics:writeLogistics Stripe WebhookThe pillar's own verified door, the Services shape exactly: signature or refusal, captured events only, idempotent by the pending->paid guard + the fee ledger's unique payment id.GET/logistics/pulse/readiness· scope logistics:readPulse ReadinessWHETHER THERE IS ANYTHING TO SELL YET. Counts only - the platform's own cursor, because no carrier may count another's nights.POST/logistics/qualifications/{qualification_id}/seen· scope logistics:writeSaw The DocumentThe caller says THEY saw the paper, today. It is their name on the record - nobody is named for them.POST/logistics/quotes/accept/{token}· scope logistics:writeAccept QuoteSIGNED-IN acceptance: the job is born on the ACCEPTOR'S OWN CURSOR (logistics_job's WITH CHECK demands buyer_user_id = the caller); the quote and the ask then flip on the system cursor.POST/logistics/quotes/{quote_id}/send· scope logistics:writeSend QuoteSENT, and the accept link handed back - sending it stays the carrier's hand. The ask is engaged: the carrier answered, and the moat's response clock stops on this stamp.POST/logistics/quotes/{quote_id}/withdraw· scope logistics:writeWithdraw QuotePOST/logistics/reliability-scores· scope logistics:writeCreate Reliability ScorePATCH/logistics/reliability-scores/{score_id}/appeal· scope logistics:writeAppeal Reliability ScorePOST/logistics/repair-events· scope logistics:writeCreate Repair EventPOST/logistics/repair-events/{event_id}/ask-a-shop· scope logistics:writeAsk A Repair ShopPATCH/logistics/repair-events/{event_id}/status· scope logistics:writeSet Repair Event StatusPOST/logistics/routes/preview· scope logistics:writeCreate Route PreviewComputes a real deadhead percentage from the operator's own real loaded/empty mileage inputs - no fabricated route geometry or distance, since no real mapping engine (OpenStreetMap/Valhalla) is wired in yet.POST/logistics/routes/{route_id}/stop· scope logistics:writeStop RouteEither side, with a reason. The row's own rule: a route that is off says who and why.GET/logistics/segments/kits· scope logistics:readSegments And KitsPOST/logistics/services· scope logistics:writeCreate ServicePATCH/logistics/services/{service_id}· scope logistics:writeEdit ServiceThe words of a service - a kit's draft is a starting point, never the carrier's own sentence.PATCH/logistics/services/{service_id}/status· scope logistics:writeSet Service Status'offered' IS THE GO-LIVE: the first moment a stranger can find this carrier. The one gate is asked HERE, in the body, before the write - a Depends misses a plain-Python caller.POST/logistics/stops/{stop_id}/outcome· scope logistics:writeStop OutcomePOST/logistics/tenders/{tender_id}/approve· scope logistics:writeApprove TenderPOST/logistics/tenders/{tender_id}/transmit· scope logistics:writeTransmit Tender DoorPOST/logistics/tenders/{tender_id}/withdraw· scope logistics:writeWithdraw TenderPOST/logistics/vehicle-documents/{document_id}/seen· scope logistics:writeSaw The PaperPOST/logistics/vehicles· scope logistics:writeCreate VehiclePATCH/logistics/vehicles/{vehicle_id}/health· scope logistics:writeUpdate Vehicle HealthPATCH/logistics/vehicles/{vehicle_id}/maintenance· scope logistics:writeUpdate Vehicle MaintenancePATCH/logistics/vehicles/{vehicle_id}/service-interval· scope logistics:writeSet Service IntervalPOST/logistics/vehicles/{vehicle_id}/services· scope logistics:writeRecord ServicePOST/logistics/vehicles/{vehicle_id}/telemetry/synthetic· scope logistics:writeCapture Synthetic TelemetryDELETE/logistics/windows/{window_id}· scope logistics:writeDelete WindowPOST/logistics/work-records/{record_id}/void· scope logistics:writeVoid WorkGET/logistics/{operator_id}· scope logistics:readGet OperatorPATCH/logistics/{operator_id}· scope logistics:writeUpdate OperatorPOST/logistics/{operator_id}/adapters· scope logistics:writeConnectGET/logistics/{operator_id}/adapters· scope logistics:readAdaptersGET/logistics/{operator_id}/capacity· scope logistics:readCapacityThe Calendar desk's reading: the windows, the next fortnight's open times day by day, the lanes and the offers - and, in words, what silenced a window and what this reading cannot know.GET/logistics/{operator_id}/claims· scope logistics:readList ClaimsGET/logistics/{operator_id}/community· scope logistics:readCarrier CommunityPOST/logistics/{operator_id}/community/drivers-circle· scope logistics:writeOpen Drivers CirclePOST/logistics/{operator_id}/community/operators-exchange· scope logistics:writeOpen Operators ExchangePOST/logistics/{operator_id}/community/shippers-room· scope logistics:writeOpen Shippers RoomOne live shippers' room per carrier - the one writer refuses a second, in words.POST/logistics/{operator_id}/crews· scope logistics:writeCreate CrewGET/logistics/{operator_id}/crews· scope logistics:readCrewsGET/logistics/{operator_id}/deliveries· scope logistics:readList DeliveriesPATCH/logistics/{operator_id}/directory· scope logistics:writeSet DirectoryThe dial. No second gate: being FOUND needs something OFFERED, and offering is the go-live the carrier already paid for - a paywall on the dial would charge twice for one door.GET/logistics/{operator_id}/fleet-truth· scope logistics:readFleet TruthGET/logistics/{operator_id}/fleets· scope logistics:readList FleetsGET/logistics/{operator_id}/fuel-rebate-signals· scope logistics:readList Fuel Rebate SignalsGET/logistics/{operator_id}/gateways· scope logistics:readList GatewaysGET/logistics/{operator_id}/handoffs· scope logistics:readList HandoffsPOST/logistics/{operator_id}/hours-of-service· scope logistics:writeRecord Hours Of ServicePATCH/logistics/{operator_id}/hours-of-service/limits· scope logistics:writeSet LimitsGET/logistics/{operator_id}/hubs· scope logistics:readList HubsPOST/logistics/{operator_id}/lane-contracts· scope logistics:writePropose Lane ContractTHE CARRIER'S HALF: a standing price on one of ITS OWN lanes for the shipper a job of theirs names, priced in LINES through LG5's one line arithmetic, in the one definition's words. Proposed, not agreed.POST/logistics/{operator_id}/lanes· scope logistics:writeAdd LaneGET/logistics/{operator_id}/leads· scope logistics:readOperator LeadsPOST/logistics/{operator_id}/leads· scope logistics:writeWrite Down A LeadGET/logistics/{operator_id}/loads· scope logistics:readList LoadsGET/logistics/{operator_id}/ltl-groups· scope logistics:readList Ltl GroupsGET/logistics/{operator_id}/members· scope logistics:readList Operator MembersPOST/logistics/{operator_id}/members· scope logistics:writeAdd Operator MemberGET/logistics/{operator_id}/moat· scope logistics:readMoatGET/logistics/{operator_id}/money· scope logistics:readMoneyThe Jobs desk's reading: every payment by kind, the routes standing, the contracts by lane - and the lanes and shippers the propose form offers (a desk that shows something must read it).POST/logistics/{operator_id}/offers· scope logistics:writeMint Offer DoorThe hand's offer: an ask, and the next three REAL open times - no later than the day the shipper needs it.GET/logistics/{operator_id}/pulse· scope logistics:readRead PulseThe nights this carrier has behind it, newest first - and what moved, which is only a trend once there are two.POST/logistics/{operator_id}/qualifications· scope logistics:writeAdd QualificationPOST/logistics/{operator_id}/quotes· scope logistics:writeCreate QuoteThe carrier's priced answer, born a DRAFT. The amount is the sum of the lines, never typed.GET/logistics/{operator_id}/quotes· scope logistics:readList QuotesGET/logistics/{operator_id}/reliability-scores· scope logistics:readList Reliability ScoresGET/logistics/{operator_id}/repair-events· scope logistics:readList Repair EventsGET/logistics/{operator_id}/roster· scope logistics:readRosterGET/logistics/{operator_id}/route-previews· scope logistics:readList Route PreviewsPATCH/logistics/{operator_id}/segment· scope logistics:writeSet SegmentGET/logistics/{operator_id}/services· scope logistics:readList ServicesPOST/logistics/{operator_id}/services/from-kit· scope logistics:writePropose KitThe segment's starter kit, minted as DRAFTS. It offers nothing and never runs twice into doubles.GET/logistics/{operator_id}/standing· scope logistics:readStandingThe carrier reads ITS OWN standing as a shipper would: the same function the public page calls, so the desk can never show a mark the page does not. The wall is asked FIRST on the caller's own cursor (a stranger reads no operator); the signals are then read on the system cursor, because the fee ledger is not a member's to read.GET/logistics/{operator_id}/telemetry· scope logistics:readList TelemetryPOST/logistics/{operator_id}/tenders· scope logistics:writeDraft TenderPOST/logistics/{operator_id}/vehicle-documents· scope logistics:writeAdd DocumentGET/logistics/{operator_id}/vehicles· scope logistics:readList VehiclesPOST/logistics/{operator_id}/windows· scope logistics:writeAdd WindowGET/logistics/{operator_id}/work· scope logistics:readWorkPOST/logistics/{operator_id}/work-records· scope logistics:writeRecord WorkA measure x a rate. EARNED IS COMPUTED ONCE HERE. The rate is the member's own for that kind unless this record states another (a different run pays a different rate).
Governor Suite - ८० doors - governor-suite:read, governor-suite:write
The governor's own instruments: documents, draws, reimbursement and status.
POST/governor-suite· scope governor-suite:writeCreate ProfilePOST/governor-suite/benefits· scope governor-suite:writeCreate Benefit PlanPATCH/governor-suite/benefits/{plan_id}/toggle-enrollment· scope governor-suite:writeToggle Benefit EnrollmentPOST/governor-suite/compliance-clerk/tasks· scope governor-suite:writeCreate Clerk TaskPATCH/governor-suite/compliance-clerk/tasks/{task_id}/status· scope governor-suite:writeSet Clerk Task StatusPOST/governor-suite/cpa-export· scope governor-suite:writeCreate Cpa Export StatePATCH/governor-suite/cpa-export/{state_id}/status· scope governor-suite:writeSet Cpa Export StatusPOST/governor-suite/credentials/{credential_id}/seen· scope governor-suite:writeRecord Seeing ItTHE CALLER'S OWN NAME GOES ON IT. Somebody recording that they saw a document is the only thing that moves a credential out of 'nobody has recorded seeing the document'.POST/governor-suite/credentials/{credential_id}/superseded· scope governor-suite:writeSupersedeA RENEWAL REPLACES, IT DOES NOT OVERWRITE. The register keeps what was true, which is the whole point of holding it: 'we held that licence until March' is a different fact from 'we hold it'.POST/governor-suite/document-templates· scope governor-suite:writeCreate Document TemplatePATCH/governor-suite/document-templates/{template_id}/white-label· scope governor-suite:writeToggle White LabelNo pricing or branding rule changes without approval (spec section 18) - this endpoint itself is the explicit approval action; premium entitlement verification stays a real follow-on (not modeled in this Core pass, matching the spec's own unresolved pricing-tier boundary in section 24).POST/governor-suite/draws/propose· scope governor-suite:writePropose DrawPATCH/governor-suite/draws/{draw_id}/status· scope governor-suite:writeSet Draw StatusPOST/governor-suite/filing-calendar· scope governor-suite:writeCreate Filing Calendar ItemPATCH/governor-suite/filing-calendar/{item_id}/status· scope governor-suite:writeSet Filing Calendar StatusPOST/governor-suite/filing-scan· scope governor-suite:writeRun ScanRead what the scout has already stored and propose. It fetches nothing.POST/governor-suite/filing-sources· scope governor-suite:writeLink SourceSay that a page the platform already watches belongs to one government. It adds no source - the page is registered on the scout registry first, where its terms, its robots answer and its calls-a-day dial live.POST/governor-suite/filings/{filing_id}/approve· scope governor-suite:writeApprove FilingA PERSON'S WORD, on a PARTICULAR package. Approving and then filing something else is the defect the hash exists to make impossible, so an approval carries the hash it approved.POST/governor-suite/filings/{filing_id}/transmit· scope governor-suite:writeTransmit DoorPOST/governor-suite/filings/{filing_id}/withdraw· scope governor-suite:writeWithdraw FilingPOST/governor-suite/gate-strikes· scope governor-suite:writeRecord Gate StrikeGET/governor-suite/governments· scope governor-suite:readList GovernmentsThe picker. A DESK THAT SHOWS SOMETHING MUST READ IT - a formation jurisdiction chosen from an empty list is a tenant who cannot record where their company lives, which is the same defect four waves of this platform have already shipped.GET/governor-suite/integrations· scope governor-suite:readList IntegrationsPOST/governor-suite/llc-renewals· scope governor-suite:writeCreate Llc RenewalPATCH/governor-suite/llc-renewals/{renewal_id}/status· scope governor-suite:writeSet Llc Renewal StatusPOST/governor-suite/member-resolutions/generate· scope governor-suite:writeGenerate Member ResolutionGET/governor-suite/mine· scope governor-suite:readGet My Profile EndpointPOST/governor-suite/officers/{officer_id}/left-office· scope governor-suite:writeLeft OfficeLEAVING IS A DATE, NOT A DELETE. The row stays, because what was true stays true.POST/governor-suite/overrides/{override_id}/withdraw· scope governor-suite:writeWithdraw OverrideWITHDRAWN, NEVER DELETED, and it takes its own reason - because "we said expired, you said current, then you withdrew that" is a history somebody will need to read.POST/governor-suite/registered-agents/{engagement_id}/end· scope governor-suite:writeEnd AgentENDING IS A DATE AND A REASON, NOT A DELETE (GV5's law): who served as your agent last year is a question somebody will ask, and a state that has been served at that address will ask it.POST/governor-suite/registered-agents/{engagement_id}/engage· scope governor-suite:writeEngage DoorPOST/governor-suite/reimbursements· scope governor-suite:writeCreate ReimbursementGET/governor-suite/reimbursements/{req_id}/receipt· scope governor-suite:readDownload ReceiptPATCH/governor-suite/reimbursements/{req_id}/status· scope governor-suite:writeSet Reimbursement StatusGET/governor-suite/renewal-rules· scope governor-suite:readList RulesWorld-readable: a tenant may see the rule its own dates are worked out from, which is the whole difference between a date and a date somebody can check.POST/governor-suite/renewal-rules· scope governor-suite:writeRecord RuleTHE EMPEROR'S HAND. A rule is platform data read by every tenant, so it is not a tenant's to write - the same posture as the government catalogue it hangs from. GV7's scout may one day propose these from a government's own published page; adopting one will still be a person's act.GET/governor-suite/renewal-rules/needed· scope governor-suite:readRules Worth RecordingHIS RULING (2026-09-21): "seed only the states tenants use". This is the list that makes that ruling actionable instead of aspirational - the governments REAL businesses are really formed in or really hold credentials from, counted, newest need first, so research goes where it is used rather than across fifty states nobody on this platform touches. OWNER-ONLY AND AGGREGATE. It counts across every…POST/governor-suite/reports/annual· scope governor-suite:writeCreate Annual PnlPATCH/governor-suite/reports/annual/{package_id}/status· scope governor-suite:writeSet Annual Pnl StatusGET/governor-suite/rule-proposals· scope governor-suite:readList ProposalsWhat the outside pages said, waiting for a person. Owner-only: the scout registry is his, and what a tenant reads is the RULE that comes out of this, with its source written on it.POST/governor-suite/rule-proposals/{proposal_id}/adopt· scope governor-suite:writeAdoptADOPTING IS A PERSON'S ACT, AND IT GOES THROUGH GV3'S OWN DOOR. This writes no rule itself - it hands `record_rule` the proposal's figures and, as its source, the words that were read and the link they were read at, so every tenant reading the rule can check the government's own sentence. A proposal with only a cadence is refused: GV3 has no way to say "every two years, starting nobody knows when"…POST/governor-suite/rule-proposals/{proposal_id}/dismiss· scope governor-suite:writeDismissA DISMISSAL SAYS WHY, and the row refuses one without it. Without a reason the same title is proposed and ignored every month, and the pile stops meaning anything - CO1's own hand rule.POST/governor-suite/tasks· scope governor-suite:writeCreate TaskPATCH/governor-suite/tasks/{task_id}/status· scope governor-suite:writeSet Task StatusPOST/governor-suite/w9-w8· scope governor-suite:writeCreate W9 W8PATCH/governor-suite/w9-w8/{req_id}/mark-on-file· scope governor-suite:writeMark W9 W8 On FileGET/governor-suite/{profile_id}· scope governor-suite:readGet ProfileGET/governor-suite/{profile_id}/benefits· scope governor-suite:readList Benefit PlansGET/governor-suite/{profile_id}/compliance-clerk· scope governor-suite:readList Clerk TasksGET/governor-suite/{profile_id}/cpa-export· scope governor-suite:readList Cpa Export StatesGET/governor-suite/{profile_id}/credentials· scope governor-suite:readList CredentialsPOST/governor-suite/{profile_id}/credentials· scope governor-suite:writeAdd CredentialGET/governor-suite/{profile_id}/document-templates· scope governor-suite:readList Document TemplatesGET/governor-suite/{profile_id}/draws· scope governor-suite:readList Draw ProposalsGET/governor-suite/{profile_id}/entity· scope governor-suite:readGet EntityPUT/governor-suite/{profile_id}/entity· scope governor-suite:writeSet EntityOne row per business, so this is an upsert and never a second entity. Changing where a company was FORMED is not an edit anybody makes twice by accident, and the audit trail keeps both.GET/governor-suite/{profile_id}/filing-calendar· scope governor-suite:readList Filing CalendarPOST/governor-suite/{profile_id}/filings· scope governor-suite:writePrepare FilingPREPARING IS FREE AND ALWAYS HAS BEEN. The lock is on what leaves, not on what a business may look at - a package a person can read is the whole value of the wave while the rail is dark.GET/governor-suite/{profile_id}/gate-strikes· scope governor-suite:readList Gate StrikesGET/governor-suite/{profile_id}/llc-renewals· scope governor-suite:readList Llc RenewalsGET/governor-suite/{profile_id}/member-resolutions· scope governor-suite:readList Member ResolutionsGET/governor-suite/{profile_id}/members· scope governor-suite:readList MembersPOST/governor-suite/{profile_id}/members· scope governor-suite:writeAdd MemberGET/governor-suite/{profile_id}/officers· scope governor-suite:readList OfficersPOST/governor-suite/{profile_id}/officers· scope governor-suite:writeRecord OfficerGET/governor-suite/{profile_id}/overrides· scope governor-suite:readList OverridesThe history, because that is the point of never deleting one.POST/governor-suite/{profile_id}/overrides· scope governor-suite:writeRecord OverrideA PERSON DISAGREES, IN THEIR OWN WORDS, WITH ONE READING.GET/governor-suite/{profile_id}/payout-status· scope governor-suite:readGet Payout StatusPATCH/governor-suite/{profile_id}/payout-status· scope governor-suite:writeUpdate Payout StatusGET/governor-suite/{profile_id}/registered-agents· scope governor-suite:readList AgentsPOST/governor-suite/{profile_id}/registered-agents· scope governor-suite:writeRecord AgentRECORDING AN AGENT YOU ALREADY HAVE IS FREE, and it is what most businesses need: the register should hold that fact rather than only the ones this platform arranged.GET/governor-suite/{profile_id}/reimbursements· scope governor-suite:readList ReimbursementsGET/governor-suite/{profile_id}/renewal-outlook· scope governor-suite:readGet Renewal OutlookGET/governor-suite/{profile_id}/reports/annual· scope governor-suite:readList Annual PnlGET/governor-suite/{profile_id}/tasks· scope governor-suite:readList TasksGET/governor-suite/{profile_id}/tax-sanctuary· scope governor-suite:readGet Tax SanctuaryPOST/governor-suite/{profile_id}/tax-sanctuary/sync· scope governor-suite:writeSync Tax SanctuaryReal cross-pillar sync: reads the tenant's own actual linked Tax profile (same organization_id) - never fabricated. If no Tax profile exists yet for this organization, this honestly reports tax_profile_status='not_linked' rather than inventing data.GET/governor-suite/{profile_id}/treasury· scope governor-suite:readGet Treasury SummaryPOST/governor-suite/{profile_id}/treasury/sync· scope governor-suite:writeSync Treasury SummaryReal cross-pillar sync: reads the tenant's own actual linked Treasury profile (same organization_id) - every total here is genuinely computed from real Treasury rows, never fabricated. Honestly reports treasury_linked=false when no Treasury profile exists yet.GET/governor-suite/{profile_id}/w9-w8· scope governor-suite:readList W9 W8
Public doors (no key needed)
२०० doors
GET/public/affiliate/go/{offer_id}GoPOST/public/billing/dodo/webhookDodo WebhookDodo's subscription lifecycle callback. RETURNS 200 FOR EVENTS WE DO NOT CARE ABOUT. Dodo retries anything that is not a 200 with exponential backoff, so answering 4xx to an irrelevant event buys retries for nothing.POST/public/booking/cancel/{token}Public CancelA guest cancels with the token from their confirmation. No account, exactly as they booked.GET/public/booking/{slug}Public SlotsPOST/public/booking/{slug}/bookPublic BookGET/public/cinema/access/{access_token}Check AccessGET/public/cinema/license/{token}License OfferGET/public/cinema/license/{token}/downloadDownload Licensed FilmThe delivery: the file moves only for CAPTURED money.POST/public/cinema/license/{token}/payPay LicensePOST/public/cinema/releases/{release_id}/buyBuy ScreeningA viewer buys access to watch. No account needed (the guest-checkout ruling); the purchase is born pending BEFORE Stripe, on the STUDIO'S own account.GET/public/cinema/releases/{release_id}/watchWatch ReleaseTHE WALL. A published free release streams to anyone; a priced one streams only for a token carrying captured money for THIS release - checked in code, because RLS scoping a row does not authorize its contents.GET/public/cinema/site/{slug}/filmsPublic FilmsWhat a stranger with the studio's link sees: published releases only, price stated or free stated - the drafts stay invisible.POST/public/concierge/avatar-clipAvatar ClipA lip-synced mp4 of her speaking a server-owned line (greeting or stored reply). Returns the clip when cached; otherwise kicks a background render and answers 202 {"status": "rendering"} - the widget polls and swaps the clip in when ready (the tier design: text instant, cloned voice ~10s, true lip-sync swapped in when rendered).POST/public/concierge/education-intentEducation IntentPOST/public/concierge/sessionStart SessionPOST/public/concierge/speakSpeakSpeaks a server-owned line (greeting or stored reply) - never caller-supplied text (the counsel's guard). Voice tiering: her ONE cloned voice (VoxCPM2, 27 locales - one consistent female voice, and the only voice for 14 previously-silent languages), else Piper where it exists (uk), else 404 = honest text-only. First clone per line blocks ~10-50s; cached mp3 forever after. NOTE the locale is used i…GET/public/concierge/{visitor_token}/messagesList MessagesA visitor reading their own conversation back. THIS KEPT THE WRONG END. `ORDER BY created_at LIMIT 200` ascending takes the OLDEST two hundred, so past message 200 the widget stopped showing what had just been said - the visitor typed, a reply was written, and neither appeared. A truncated list is a lie about being whole; this was a chat that quietly stopped working, and the silent cap is what hi…POST/public/concierge/{visitor_token}/messagesSend MessagePOST/public/concierge/{visitor_token}/voiceVoice MessageTalk to the Concierge: audio in -> Whisper transcribes (language-hinted) -> the SAME grounded brain replies. Returns the concierge message (with id) so the widget can play it back in voice. The visitor's spoken words are stored as their turn.POST/public/confirm/{token}Confirm SubscriptionDouble opt-in's second half. Confirming is benign (it grants nothing but the list membership the person just asked for), so the mailed link may act on GET - the industry's own shape for confirmation links.GET/public/confirm/{token}Confirm SubscriptionDouble opt-in's second half. Confirming is benign (it grants nothing but the list membership the person just asked for), so the mailed link may act on GET - the industry's own shape for confirmation links.GET/public/congregation/campaign/{fund_slug}Public CampaignThe outward-facing campaign: the fund, its goal, honest progress, and the door to the church's own giving page with this fund preselected.POST/public/congregation/{site_slug}/care-requestsSubmit Care RequestAnyone may ask for prayer or care - no account, the requester chooses what the congregation may see. Throttled like every anonymous door.GET/public/congregation/{site_slug}/prayerPublic Prayer PageWhat the site's prayer page needs to stand: the congregation's name and its own words (502's labels - a mosque's page says imam, a creator's says supporters). A business with no congregation has NO prayer page: a door must lead to someone who will read what is written. Nothing a person ever asked is readable here - care requests have no public read arm, ever. Registered AFTER /campaign/{fund_slug…POST/public/csp-reportReceive Csp ReportThe report endpoint the Caddy block names. Answers 204 with nothing - a browser sends these in the background and reads no answer. Throttled per address (a page with a broken policy sends one per blocked resource, a script sends millions).GET/public/developers/referenceReferenceThe reference: families of doors a key may open (with each door's own words), the public doors, how a key is presented, how a delivery is signed, which families a webhook may name.GET/public/education/catalogPublic CatalogGET/public/education/credentials/{kind}/{certificate_id}Verify Credential PublicGET/public/education/transcripts/{share_id}Verify Transcript PublicGET/public/employment/{verification_id}/verifyVerify EmploymentPOST/public/events/webhookEvents Stripe WebhookThe pillar-webhook pattern: own secret, signature or refusal, captured only.GET/public/events/{slug}Public EventPOST/public/events/{slug}/buy-ticketBuy TicketThe paid door: pending purchase FIRST, Checkout on the tenant's own account, the seat granted only when the webhook sees captured money.POST/public/events/{slug}/registerPublic RegisterFREE events register directly - the ticket code IS the confirmation. A priced event refuses this door and points at the paid one, so nobody holds a seat they never paid for.GET/public/farms/directoryPublic DirectoryPOST/public/farms/directory/{farm_id}/askPublic AskGET/public/farms/offers/{token}Read OfferPOST/public/farms/offers/{token}/acceptAccept OfferAccepting names ONE of the offered times; the lead is engaged (the farm answered and the buyer said yes); the sale still happens on the farm's store, where the link points.GET/public/farms/quotes/{token}Read QuotePOST/public/farms/region-asksPublic Region AskThe directory's 'ask the farms near me' - no account needed; an account holder is joined by email.GET/public/farms/showcase/{asset_id}Public Showcase FileA PUBLISHED showcase asset's file, for the farm's page - a draft stays the staff's (404).GET/public/farms/site/{slug}Public Farm PagePOST/public/farms/site/{slug}/joinPublic Farm JoinThe page's "keep me posted": the email joins the org's ONE audience list (G2's table, the practice door's shape) with the consent words recorded and source 'farm' - the CHECK widened WITH this caller (606). Consent is a real yes or a 422.GET/public/forms/{slug}Public FormPOST/public/forms/{slug}/submitPublic SubmitGET/public/games/playtest/{token}Playtest OfferWhat the invited tester reads: the world, who asked, how long the door is open. Facts only, on the system cursor - the invite IS the credential.POST/public/games/worlds/{world_id}/wishlistJoin WishlistThe demo gate's second door: not ready to buy, but wants to hear. The email joins the org's ONE audience list (consent words recorded there) and the wishlist row remembers which world - both on the system cursor, fenced to worlds whose demo the tenant deliberately opened.GET/public/give/moncash/returnMoncash ReturnWhere MonCash sends a payer back to. PUBLIC, and treated as hostile. THE QUERY PARAMETERS PROVE NOTHING. Anyone can visit this URL with any order id - it is a plain GET in a browser. What decides whether money moved is `RetrieveOrderPayment`, asked of MonCash with the church's own credential, and the AMOUNT RECORDED IS THE ONE MONCASH REPORTS. Trusting an amount from the URL would let a donor ret…POST/public/give/paystack/webhookPaystack WebhookPaystack's completion callback. A SEPARATE ENDPOINT FROM STRIPE'S, because the two sign differently: Stripe uses HMAC SHA256 with a per-endpoint signing secret, Paystack uses HMAC SHA512 with the API secret key itself. One endpoint accepting both would have to guess which scheme applies, and guessing wrong on a signature check is how an unverified event gets processed. RETURNS 200 EVEN WHEN THE …POST/public/give/webhookGive WebhookStripe's completion callback for gifts made on a connected account. SEPARATE FROM THE ACCOUNT WEBHOOK because these arrive on the CONNECT endpoint with their own signing secret, and mixing them means one bad secret silently disables both.GET/public/give/{slug}Public PageWhat a stranger with the link sees. No session, on purpose. REFUSES TO SHOW A PAGE THAT CANNOT TAKE MONEY. If the church has not finished onboarding, a donor who fills in an amount and hits a Stripe error blames the church. Better to say plainly that giving is not open yet.POST/public/give/{slug}/checkoutStart GiftCreate a Checkout Session ON THE CHURCH'S ACCOUNT and hand back the URL. NO CARD DETAILS TOUCH THIS PLATFORM. Stripe hosts the payment page; we send an amount and a destination and receive a link. That is not only a PCI position, it is the same principle as the Express onboarding: the regulated part belongs to the party equipped to carry it.GET/public/interpret/{code}Public SessionGET/public/interpret/{code}/turnsPublic TurnsPOST/public/interpret/{code}/turnsPublic SpeakOne push-to-talk turn: hear it, translate it, speak it, append it. Synchronous by design - the speaker is standing there waiting; a few seconds is the product.GET/public/interpret/{code}/turns/{turn_id}/audioPublic Turn AudioGET/public/l/{code}Resolve Short LinkThe public resolver: returns the target for the web layer's 302 and counts the click as a daily aggregate - nothing about the visitor is stored, ever.GET/public/logistics/ask-wordsPublic Ask WordsThe words an ask is made under and the pickers it offers - from the ONE definition, so a page can never show one promise while the row keeps another.POST/public/logistics/carrier/{operator_id}/askPublic AskGET/public/logistics/carrier/{slug}Public Carrier PagePOST/public/logistics/carrier/{slug}/joinPublic Carrier JoinTHE CARRIER'S "KEEP ME POSTED" - shipped WITH the page this time (MF4a shipped without one and a factory's digest had a reach of zero until MF19). One audience list, the consent words RECORDED, a real yes or a 422, and the source word widened with this caller (647).GET/public/logistics/directoryPublic DirectoryGET/public/logistics/offers/{token}Read OfferPOST/public/logistics/offers/{token}/acceptAccept OfferAccepting names ONE of the offered times. The time is checked STILL OPEN at this moment - two offers may have held the same hour, and the second to accept it is told so rather than double-booked.GET/public/logistics/quotes/{token}Read QuoteGET/public/logistics/track/{token}TrackBY ITS TOKEN, ON THE SYSTEM CURSOR - and built from two WHITELISTS, so nothing the carrier wrote about a person or a place can reach a stranger by being added to a row later.POST/public/manufacturing/capacity/{listing_id}/askPublic Capacity AskGET/public/manufacturing/directoryPublic DirectoryPOST/public/manufacturing/factory/{factory_id}/askPublic AskGET/public/manufacturing/factory/{slug}Public Factory PagePOST/public/manufacturing/factory/{slug}/joinPublic Factory JoinMF19 - THE FACTORY'S "KEEP ME POSTED", which this pillar never had. FM6 gave the farm page this door and OF8 gave the practice page one; MF4a built the public factory page without it, so there was NO WAY for a visitor to join a factory's audience. That is not a missing convenience: the community digest may only reach somebody who has already said yes to hearing from this business, so a factory's …GET/public/manufacturing/license/{token}Read LicenseGET/public/manufacturing/license/{token}/downloadDownload Licensed FileThe delivery: the file moves only for CAPTURED money - 402 before, an attachment after.POST/public/manufacturing/license/{token}/payPay LicenseThe pay door in the shape's place. It REFUSES: no price stands on any licence, and none can until a ruling sets one - said in words, never a provider error.GET/public/manufacturing/quotes/{token}Read QuotePOST/public/manufacturing/region-asksOpen Region Ask PublicA buyer with no account asks the region. Their email is the whole identity here.GET/public/marketPublic MarketTHE PLATFORM-WIDE MARKET (TJ3, the Emperor's demand engine): every published tenant's products, services and courses, browsable by anyone - Fitinty as the mall, not just the landlord. Every buyer any tenant attracts becomes a potential customer of every other. The same public-read boundary as every /site page, applied across orgs: only content of organizations with a LIVE theme appears (a storefr…POST/public/marketplace/checkoutStart Marketplace CheckoutPUBLIC - guests buy too (LST5's ruling). The cart's state is read from Medusa, never trusted from the caller; the seller and the amount come from rows.GET/public/marketplace/checkout/{checkout_id}Checkout StatusThe thanks page's reading: paid or not, order formed or forming - in words. The id is unguessable and the answer reveals what the buyer already knows.POST/public/marketplace/checkout/{checkout_id}/place-orderRetry Place OrderThe retry door for a paid checkout whose order has not formed.GET/public/marketplace/factsMarketplace Card FactsM-walk fix: the marketplace browse grid rendered cards with NO price and NO seller - a buyer deciding blind. This is the mall's own mirror read (the same rows /public/market serves), shaped as a map for the browse cards: who sells it, where its storefront page is, and whether a published full-story page exists.GET/public/marketplace/promotionsPromoted RailTHE SPOTLIGHT RAIL for the browse screens: live promotions with their product facts, every one carrying promoted=true - the label IS the honesty.POST/public/marketplace/stripe-webhookMarketplace Stripe WebhookGET/public/media/access/{access_token}Check AccessWhat this key opens - for the public player to decide whether to show the unlocked state. An episode key names its episode; a membership key names its SHOW (it opens every premium episode there). Paid or not, in words; never the sales book.POST/public/media/episodes/{episode_id}/buyBuy EpisodeA listener buys one premium episode. No account needed - a podcast listener rarely has one (the marketplace guest-checkout ruling, applied here). The email is where the unlock link goes conceptually; today the success redirect carries the token and the email is the purchase's honest record of WHO bought.POST/public/media/episodes/{episode_id}/tipTip EpisodeA gratuity on any published episode - free or premium. The whole amount is the tenant's (fee 0, the SL3 ruling: nobody's thank-you carries a cut) and it never enters the sales ledger.GET/public/media/license/{token}License OfferWhat the licensee reads before paying: the terms in the tenant's own words.GET/public/media/license/{token}/downloadDownload Licensed FileThe delivery: the file moves only for CAPTURED money - the same wall the premium episode stands behind.POST/public/media/license/{token}/payPay LicensePOST/public/media/shows/{show_id}/membershipJoin MembershipA listener becomes a monthly member of the show, on the show owner's own Stripe account (mode=subscription). The membership row is the STANDING record: paid = active, void = cancelled; money lands one row per invoice cycle.GET/public/media/sponsor/{token}Sponsor OfferWhat the sponsor sees before paying: the facts in plain words, nothing else.POST/public/media/sponsor/{token}/payPay SponsorshipPOST/public/platform/audienceJoin Platform AudienceThe join door on fitinty.com itself: the platform's own list, kept on the Fitinty organization with the SAME machinery tenants get - the Emperor markets Fitinty with the tools he sells. (The tenant join door requires a published storefront; the platform's own pages ARE its storefront, so this door checks only that the one org exists.)GET/public/platform/pressPress KitThe public press page's data: the Emperor's words plus figures MEASURED at this moment from the platform's own tables - never a stored total, never a projection.GET/public/platform/social-linksPlatform Social LinksGET/public/platform/testimonialsPublic TestimonialsGET/public/podcast-episodes/{episode_id}/audioPublic AudioGET/public/podcast-shows/{show_id}/artworkPublic ArtworkGET/public/presentation-factory/shared/{token}Shared PagePOST/public/presentation-factory/shared/{token}/commentsShared CommentGET/public/presentation-factory/shared/{token}/deckShared AssetPOST/public/presentation-factory/shared/{token}/reportShared ReportGET/public/presentation-followup/{token}Followup PagePUBLIC, no account: the people who were in the room read what they were promised. Only answered AND published items appear, each labelled if it has no source behind it.POST/public/presentation-sessions/joinJoinPOST/public/presentation-sessions/{sid}/questionsAskPOST/public/presentation-sessions/{sid}/questions/{qid}:upvoteUpvotePOST/public/presentation-sessions/{sid}/runs/{run_id}/answerAnswer PollGET/public/presentation-sessions/{sid}/stateAudience StatePolled every couple of seconds by every phone in the room, so it stays small: compare state_version and only re-render when it moved.GET/public/pricing/plansPublic PlansThe price list, for anybody. Only `is_public` plans: `founding-all` is what every existing organization silently resolves to and is NOT for sale, so publishing it would advertise a free everything-tier.GET/public/reports/source/{token}Public SourceR4 - the page the document server fetches to convert: a SHORT-LIVED signed token names ONE report; the token is minted only inside the export door, never shown to a person. Read on the system cursor because the document server is nobody.GET/public/review-media/{review_id}Review MediaGET/public/reviews/course/{course_id}Course ReviewsGET/public/reviews/platformPlatform ReviewsGET/public/reviews/practice/{office_id}Practice ReviewsGET/public/reviews/service/{listing_id}Service ReviewsGET/public/services/offers/{token}Read OfferPOST/public/services/offers/{token}/acceptAccept OfferAccepting turns the offer into a booking INTENT and hands back the claim path - the existing claim flow signs the person in and places the booking with all its standing guards. Only a time the offer actually named is accepted.GET/public/services/quotes/{token}Read QuoteGET/public/site/domain-allowedDomain AllowedCaddy on-demand-TLS ask endpoint. 200 = may request a certificate, 403 = must not. Caddy treats ANY 2xx as permission, so this must never return 200 on the error path - hence an explicit 403 rather than a body the caller has to interpret.GET/public/site/resolve-domainResolve DomainCustom-domain routing: maps a Host header (port already stripped by the caller) to an org slug -- but ONLY for a domain whose ownership was proved (migration 261). Two independent gates, both required. The domain must be the LIVE theme version's developer.domainOverrides.publicDomain, so an unpublished change never reroutes traffic; and it must have a VERIFIED claim owned by that same organizatio…GET/public/site/tls-askTls AskCaddy on-demand TLS 'ask' endpoint: Caddy calls this with ?domain=<host> before issuing a certificate for an unknown domain. THIS is the endpoint prod's Caddyfile actually points at (/public/site/tls-ask), so the ownership gate had to land here rather than only on the newer /public/site/domain-allowed - otherwise migration 261 would have shipped while the live certificate path kept using the weak…GET/public/site/{slug}Get Public SiteBOTH HALVES, KEPT. The storefront reads the two doors above so each gets the caching it deserves; this one stays because it is a public contract older than the split and nothing is served by breaking a caller to make a point. It is assembled from the same one definition, so the three can never disagree.POST/public/site/{slug}/audienceJoin AudienceThe join door. A rejoin after unsubscribing is a RE-CONSENT: the row updates, the unsubscribe clears, nothing doubles.POST/public/site/{slug}/audience/leaveLeave AudienceThe same sentence whether or not the email was ever on the list - this door is not an oracle for probing who subscribes to what.GET/public/site/{slug}/communityPublic CommunityThe open room: published announcements + clubs with real member counts. Anonymous read - the room's life IS the invitation.GET/public/site/{slug}/community/clubs/{club_id}Public ClubGET/public/site/{slug}/contentGet Public Site ContentTHE HALF THAT MAY NEVER BE CACHED. Courses, services, events and products - everything a tenant can switch off, and the exact payload the sixty-second window used to hold on to.GET/public/site/{slug}/courses/{course_id}Get Public CourseGET/public/site/{slug}/events/{event_id}Get Public EventGET/public/site/{slug}/feed.xmlStore Product FeedThe Google Merchant Center product feed (RSS 2.0 + g: namespace). Only items Google would accept ride; the excluded count is stated in the feed itself so an empty-looking feed is never a mystery.GET/public/site/{slug}/game-demosList Game DemosGET/public/site/{slug}/game-demos/{world_id}Get Game DemoPOST/public/site/{slug}/intentsCreate IntentGET/public/site/{slug}/jobsPublic JobsPublished openings on a LIVE storefront - the same gate as products: a shop that 404s cannot hire through its window.POST/public/site/{slug}/jobs/{post_id}/applyPublic ApplyA stranger applies - no account (the guest-checkout ruling's spirit). The one demand is reachability: an application with no email AND no phone is a name we can never answer.GET/public/site/{slug}/landing/{product_id}Public LandingThe public showcase: the PUBLISHED page plus the product's real buy facts. The CTA carries the same product path the catalog uses - one click to the buy flow.GET/public/site/{slug}/logoGet Public Site LogoGET/public/site/{slug}/partnersList Site PartnersThe partners this business chose to show on ITS OWN storefront. The other side's toggle governs the other side's page - each tenant designs only their own.POST/public/site/{slug}/partners/{partnership_id}/visitRecord Partner VisitA visitor follows a partner card. The click lands in the append-only evidence ledger - the referral counts both owners read are counts of these rows - and the visitor gets the partner's public address back. No visitor identity is recorded: a click is a click.GET/public/site/{slug}/pickup-locationsPickup LocationsST2: the checkout's pickup choices - the org's active physical places, on a LIVE storefront (the same gate as every public window).GET/public/site/{slug}/podcastsPublic ShowsGET/public/site/{slug}/podcasts/{show_slug}/feed.xmlPublic FeedGET/public/site/{slug}/postsPublic PostsGET/public/site/{slug}/posts/{post_slug}Public PostGET/public/site/{slug}/practicePublic PracticePOST/public/site/{slug}/practice/askPublic Practice AskThe question door: a stranger's ask becomes ONE office_lead (source 'question') through the moat's one writer, fenced to the practice the slug resolves to. Consent is recorded as the fact it is; without it the door refuses in words.POST/public/site/{slug}/practice/bookPublic Practice BookA consultation booked through the door. Signed-in only: booking_insert demands the client's own id (no anonymous write ever creates an appointment - the intent-claim posture). The time must be one the door actually offers (409 otherwise), the row is written on the CLIENT's cursor, and its lead is minted once (source 'booking').POST/public/site/{slug}/practice/joinPublic Practice JoinOF8 - the practice page's "keep me posted" door: not ready to ask or book, but wants to hear. The email joins the org's ONE audience list (G2's table, the games door's shape) with the consent words recorded and source 'practice' - the CHECK widened WITH this caller (526). Consent is a real yes or a 422; the closed loop then measures this door like any other.GET/public/site/{slug}/practice/slotsPublic Practice SlotsGET/public/site/{slug}/productsList Public ProductsThe CATALOG the storefront never had (LST1) - browse, search, filter by category, paginate. Reads only the public-safe mirror; published rows of this org alone. The category list rides along so the browse bar renders from real data, never a taxonomy.GET/public/site/{slug}/products/{product_id}Get Public ProductPublic product detail (STOREFRONT_GAPS: "Product detail pages / purchase links"). Reads ONLY the public-safe mirror (marketplace_public_product) - never Medusa or the credentials-bearing vendor row - and only ACTIVE rows of THIS org, behind the same live-theme gate as every other public page. Purchase itself stays where it already works: the signed-in Marketplace (test mode until Stripe live sign-…GET/public/site/{slug}/products/{product_id}/also-boughtAlso BoughtG4 - the bought-together shelf: pairs counted from REAL order lines by the pairing agent, joined to the published mirror so a withdrawn product never rides along. The reading's age is stated - a stale shelf says so instead of pretending.GET/public/site/{slug}/products/{product_id}/questionsList Product QuestionsPublic Q&A (441): questions and their answers publish together; a hidden question shows nothing. Unanswered shows AS unanswered - honest pressure.POST/public/site/{slug}/products/{product_id}/questionsAsk Product QuestionAnyone may ask (a question is a sale mid-decision); rate-limited per IP; only the seller answers; the seller's hide takes a REASON - the Commons' hand, not an eraser.GET/public/site/{slug}/products/{product_id}/reviewsList Product ReviewsPublic reviews for one product: the list, the average, the count - every one of them a verified purchase by construction (no other kind can exist in the table).POST/public/site/{slug}/products/{product_id}/reviewsSubmit Product ReviewVERIFIED OR REFUSED (440, generalized by 442). The proof is the order id + the order's own email, checked against the store's real records; the email is checked and DISCARDED. One review per order per product. A seller cannot write their own reviews - there is no tenant door. The reviewer may attach THEMSELF saying it: photo, voice note or video (his ruling on recorded formats), size-capped, serve…POST/public/site/{slug}/referralMint ReferralA customer's personal code - ONE per person per store; asking again returns the same code (the row is the truth, not the request).GET/public/site/{slug}/referral-programPublic ProgramGET/public/site/{slug}/referral/{code}Referral StatusThe referrer's window, keyed by the code itself - the secret only they hold. No email lookup exists on purpose (an email is guessable; a code is not).GET/public/site/{slug}/services/{listing_id}Get Public ServiceGET/public/site/{slug}/services/{listing_id}/imageGet Public Service ImageA service listing's image, behind the same gates as the listing itself: the site must be live and the listing active. Served from the API's own disk (no object storage exists).GET/public/site/{slug}/services/{listing_id}/slotsPublic SlotsReal open slots for one listing on one day - windows sliced by the listing's own duration, minus pending/confirmed bookings. Past slots (for today) are excluded.GET/public/site/{slug}/sitemap.xmlStore Sitemap XmlThe storefront's own sitemap - `sitemap_urls`, the one list of its pages. Live theme required - a site that 404s must not invite a crawler.GET/public/site/{slug}/social-linksSite Social LinksPOST/public/site/{slug}/subscribeSubscribeGET/public/site/{slug}/themeGet Public Site ThemeTHE HALF THAT MAY BE CACHED (his ruling, 2026-09-22). A storefront was serving what its tenant had switched off for up to a minute, because theme AND content came back in ONE payload on one sixty-second cache entry - so the only ways to make the content fresh were to give up the theme's cache with it, or to split them. He ruled the split: the chrome is cached, the content is never cached, and a t…POST/public/site/{slug}/unsubscribeUnsubscribeToken-matched unsubscribe via a SECURITY DEFINER helper (migration 243): an UPDATE's WHERE reads rows under SELECT policies, and subscriber rows deliberately have no public SELECT - emails are PII. The helper does the one narrow update without ever making the table publicly readable.POST/public/site/{slug}/visitRecord VisitPrivacy-light page-view counter, called fire-and-forget by the web middleware. Stores nothing about the visitor -- one daily counter per (org, page, kind). The visitor's User-Agent arrives as X-Visitor-Agent (the middleware forwards the original request's header, since this call is server-to-server and its own UA would be Node's), is used ONLY to decide human vs bot, and is never written anywhere…GET/public/sitemapPublic SitemapEverything a search engine may index: every org with a PUBLISHED theme, plus the ids of its public detail pages. Same public-read RLS boundary as the pages themselves -- nothing appears here that the storefront wouldn't serve.GET/public/social-oauth/callbackOauth CallbackGET/public/song-factory/shared/{token}Shared PageGET/public/song-factory/shared/{token}/audioShared AssetPOST/public/song-factory/shared/{token}/commentsShared CommentPOST/public/song-factory/shared/{token}/reportShared ReportGET/public/system/healthSystem HealthGET/public/talk/{token}Talk PagePUBLIC, no account: for the person who missed the room. Returns the pinned slides, the timeline the player needs, and the voice label - and counts the open, which is all a link can honestly know.GET/public/talk/{token}/audioTalk AudioGET/public/talk/{token}/captionsTalk CaptionsGET/public/talk/{token}/transcriptTalk TranscriptReading instead of listening is a real accessibility need, not a lesser option.GET/public/tax/copy/{token}Public CopyGET/public/tax/identity/{token}Public DescribePOST/public/tax/identity/{token}Public SubmitPOST/public/tenant-draftCreate Tenant DraftThe Convince moment: words in, a business standing at a preview URL out.GET/public/tenant-draft/{token}Get Tenant DraftPATCH/public/tenant-draft/{token}Change Draft PillarThe preview's override: the visitor corrects WHERE their business belongs before claiming it. The token is the authority (it is the same secret that claims), an already- claimed draft refuses, and the pillar must exist - free text would be a silent zero.POST/public/tradingview/{token}Receive AlertReceive a TradingView alert and turn it into a trade intent. Returns 200 with an outcome for anything it understood well enough to record, including refusals - TradingView retries on non-2xx and a retried alert is indistinguishable from a replay, so refusing loudly with a 4xx would produce exactly the traffic this endpoint is built to reject. An unknown token is the one exception: it gets a flat …GET/public/unsubscribe/{token}Unsubscribe InfoPOST/public/unsubscribe/{token}UnsubscribePOST/public/verifyVerify PastedRe-computes the canonical hash and HMAC over the pasted payload. A single changed character in the payload flips both answers to false.GET/public/verify/file/{sha256}Verify By FileThe strongest check: hash the file you were given (the page does it in your browser - the file never leaves your machine) and ask whether this installation certified that exact file.GET/public/verify/{cert_id}Verify By IdGET/public/video-factory/shared/{token}Shared PageGET/public/video-factory/shared/{token}/captionsShared CaptionsPOST/public/video-factory/shared/{token}/commentsShared CommentPOST/public/video-factory/shared/{token}/reportShared ReportGET/public/video-factory/shared/{token}/videoShared AssetGET/public/workspace-documents/shared/{token}Shared PagePOST/public/workspace-documents/shared/{token}/commentsShared CommentGET/public/workspace-documents/shared/{token}/fileShared AssetPOST/public/workspace-documents/shared/{token}/reportShared Report
the served openapi document - this reference is never written by hand