{"components":{"responses":{"BookingsDisabled":{"description":"Bookings are off on this API (`BOOKINGS_ENABLED` isn’t `true`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"not found."}}}},"BookingNotFound":{"description":"No such booking for this parent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"Booking not found.","data":null}}}}},"schemas":{"ChildGuardian":{"properties":{"email":{"type":"string"},"joinedAt":{"format":"date-time","type":"string"},"userId":{"format":"uuid","type":"string"}},"required":["userId","email","joinedAt"],"type":"object"},"ChildInvitation":{"properties":{"createdAt":{"format":"date-time","type":"string"},"email":{"format":"email","type":"string"},"expiresAt":{"format":"date-time","type":"string"},"id":{"format":"uuid","type":"string"}},"required":["id","email","createdAt","expiresAt"],"type":"object"},"ChildInvitationPreview":{"allOf":[{"$ref":"#/components/schemas/ChildInvitation"},{"properties":{"childId":{"format":"uuid","type":"string"},"childName":{"type":"string"},"invitedByEmail":{"type":"string"}},"required":["childId","childName","invitedByEmail"],"type":"object"}]},"FamilyApprovalSource":{"additionalProperties":false,"description":"The authenticated viewer is always the approving actor. Optional source {type:\"manual\"} or {type:\"assistant\", conversationId, turnId} names one of the viewer's own completed Assistant turns that proposed the change; the write itself is the explicit approval.","properties":{"conversationId":{"format":"uuid","type":"string"},"turnId":{"format":"uuid","type":"string"},"type":{"enum":["manual","assistant"]}},"required":["type"],"type":"object"},"FamilyChildFields":{"additionalProperties":false,"description":"Optional registration fields. A planning label can identify a profile without full names; enabling registration reuse requires both child.firstName and child.lastName.","properties":{"child.allergies":{"maxLength":4000,"type":"string"},"child.birthDate":{"maxLength":10,"type":"string"},"child.firstName":{"maxLength":255,"type":"string"},"child.grade":{"maxLength":100,"type":"string"},"child.lastName":{"maxLength":255,"type":"string"},"child.notes":{"maxLength":4000,"type":"string"},"emergencyContact.name":{"maxLength":255,"type":"string"},"emergencyContact.phone":{"maxLength":64,"type":"string"},"guardian.email":{"maxLength":254,"type":"string"},"guardian.name":{"maxLength":255,"type":"string"},"guardian.phone":{"maxLength":64,"type":"string"},"guardian.relationship":{"maxLength":255,"type":"string"},"secondaryGuardian.email":{"maxLength":254,"type":"string"},"secondaryGuardian.name":{"maxLength":255,"type":"string"},"secondaryGuardian.phone":{"maxLength":64,"type":"string"}},"type":"object"},"FamilyChildInput":{"additionalProperties":false,"description":"A new profile needs a nonempty planning label or both first and last names. Registration reuse requires both names. PUT replaces fields and preferences; when planning is omitted, existing planning values are retained and the merged profile must still have a label or full name.","properties":{"fields":{"$ref":"#/components/schemas/FamilyChildFields"},"planning":{"$ref":"#/components/schemas/FamilyChildPlanning"},"preferences":{"$ref":"#/components/schemas/FamilyChildPreferences"},"reuseAllowed":{"default":false,"type":"boolean"},"source":{"$ref":"#/components/schemas/FamilyApprovalSource"},"version":{"minimum":1,"type":"integer"}},"required":["fields"],"type":"object"},"FamilyChildPlanning":{"additionalProperties":false,"description":"Enduring planning details registration forms do not use. A label alone can start a profile; registration reuse still needs first and last names. Save a date of birth or an age with its as-of date, not both. Nothing is inferred.","properties":{"age":{"additionalProperties":false,"properties":{"asOf":{"format":"date","type":"string"},"years":{"maximum":21,"minimum":0,"type":"integer"}},"required":["years","asOf"],"type":"object"},"gradeSchoolYear":{"description":"The school year fields[\"child.grade\"] applies to, such as 2026-2027.","pattern":"^[0-9]{4}-[0-9]{4}$","type":"string"},"label":{"maxLength":60,"type":"string"},"requirements":{"items":{"additionalProperties":false,"properties":{"category":{"enum":["health_safety","dietary","accessibility","supervision","communication","social","other"]},"detail":{"description":"A practical parent-stated condition; no diagnosis is needed.","maxLength":300,"minLength":1,"type":"string"},"kind":{"enum":["required","preference"]}},"required":["kind","category","detail"],"type":"object"},"maxItems":20,"type":"array"},"requirementsStatus":{"enum":["unknown","none","listed"]}},"type":"object"},"FamilyChildPreferences":{"additionalProperties":false,"properties":{"interests":{"items":{"maxLength":100,"type":"string"},"maxItems":20,"type":"array"},"interestsStatus":{"description":"unknown means the parent has not answered; none means no interests; listed requires interests. Omitted, it is listed when interests are present and otherwise unknown.","enum":["unknown","none","listed"]},"notes":{"maxLength":2000,"type":"string"}},"type":"object"},"FamilyChildProfile":{"properties":{"createdAt":{"format":"date-time","type":"string"},"fields":{"$ref":"#/components/schemas/FamilyChildFields"},"id":{"format":"uuid","type":"string"},"planning":{"$ref":"#/components/schemas/FamilyChildPlanning"},"preferences":{"$ref":"#/components/schemas/FamilyChildPreferences"},"provenance":{"additionalProperties":{"$ref":"#/components/schemas/FamilyProvenance"},"description":"Current-value approval for saved registration fields, preferences and planning values. Removed values leave no entry; unchanged values keep their provenance. Another adult's conversation reference is omitted.","type":"object"},"reuseAllowed":{"type":"boolean"},"role":{"description":"Owner controls invitations, deletion and reuse permission. Guardians may correct fields/preferences and use already-enabled reuse.","enum":["owner","guardian"],"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"version":{"minimum":1,"type":"integer"}},"required":["id","version","fields","preferences","reuseAllowed","provenance","createdAt","updatedAt","role","planning"],"type":"object"},"FamilyCorrection":{"additionalProperties":false,"description":"Narrow correction: each named key replaces its value and null removes it. Unnamed values and their approvals are unchanged. The merged record is validated like a complete submission. The authenticated viewer is always the approving actor. Optional source {type:\"manual\"} or {type:\"assistant\", conversationId, turnId} names one of the viewer's own completed Assistant turns that proposed the change; the write itself is the explicit approval.","properties":{"changes":{"minProperties":1,"type":"object"},"source":{"$ref":"#/components/schemas/FamilyApprovalSource"},"version":{"minimum":0,"type":"integer"}},"required":["version","changes"],"type":"object"},"FamilyProvenance":{"description":"Approval of the current value only. Another adult's sourceReference is never returned; removed values leave no entry.","properties":{"actorId":{"format":"uuid","type":"string"},"approvedAt":{"format":"date-time","type":"string"},"source":{"enum":["manual","assistant"]},"sourceReference":{"properties":{"conversationId":{"format":"uuid","type":"string"},"turnId":{"format":"uuid","type":"string"}},"type":"object"},"version":{"description":"Record version at which this value was approved or reconfirmed.","type":"integer"}},"required":["source","actorId","approvedAt"],"type":"object"},"FamilyReconfirmation":{"additionalProperties":false,"description":"Records a fresh approval of unchanged current values and advances the record version. A key without a saved value returns 409.","properties":{"keys":{"items":{"type":"string"},"maxItems":40,"minItems":1,"type":"array"},"source":{"$ref":"#/components/schemas/FamilyApprovalSource"},"version":{"minimum":1,"type":"integer"}},"required":["version","keys"],"type":"object"},"BookingError":{"type":"object","required":["error","message"],"properties":{"error":{"const":true},"message":{"type":"string","description":"A sentence you can show the parent as is."},"data":{"type":"null"}}},"BookingMoney":{"type":"object","required":["amountCents","currency"],"properties":{"amountCents":{"type":"integer","description":"Amount in the smallest currency unit, such as cents."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 currency code."}},"example":{"amountCents":45000,"currency":"USD"}},"BookingStatus":{"type":"string","enum":["authorizing","prepared","submitting","submit_uncertain","accepted","running","awaiting_decision","confirmed","waitlisted","failed","cancelled","uncertain"],"description":"The step the booking is at, for support and diagnostics. Apps decide what to show from `phase`; each status belongs to exactly one phase, and a new status may be added to an existing phase without notice.","x-open-enum":true},"BookingPaymentStatus":{"type":"string","enum":["none","hold_pending","held","captured","released","failed"],"description":"The state of the hold on the parent’s card. `hold_pending` means a hold attempt’s outcome is unknown; it’s retried with the same idempotency key."},"BookingShareableField":{"type":"string","enum":["child.birthDate","child.grade","child.allergies","guardian.phone"],"description":"Optional profile fields the parent can choose to share. The child’s first and last name and the guardian’s name and email are always shared."},"BookingApprovalInput":{"type":"object","additionalProperties":false,"required":["idempotencyKey","childProfileId","childProfileVersion","sessionId","maxSpend","allowWaitlist","autoAcceptDecisions","shareFields","answers","approveBooking"],"properties":{"idempotencyKey":{"type":"string","format":"uuid","description":"Generate one per approval form. Resending the identical body returns the same booking with `200`; reusing the key with a different body returns `409`."},"childProfileId":{"type":"string","format":"uuid"},"childProfileVersion":{"type":"integer","minimum":1,"description":"The profile version the parent reviewed. If the profile has changed since, the request returns `409`."},"sessionId":{"type":"string","format":"uuid","description":"The catalog session to book."},"maxSpend":{"$ref":"#/components/schemas/BookingMoney","description":"The spending limit, including fees and tax, in the session’s currency and at most 2,500,000 cents. The hold on the parent’s card is for exactly this amount."},"allowWaitlist":{"type":"boolean","description":"Accept a waitlist place if the session is full."},"autoAcceptDecisions":{"type":"boolean","description":"Let School’s Out answer routine camp questions and accept session changes within the spending limit. It never raises spending above the limit, signs a waiver, or creates a camp account."},"shareFields":{"type":"array","items":{"$ref":"#/components/schemas/BookingShareableField"},"uniqueItems":true},"answers":{"type":"array","maxItems":50,"items":{"type":"object","additionalProperties":false,"required":["key","label","value"],"properties":{"key":{"type":"string","maxLength":100},"label":{"type":"string","maxLength":500},"value":{"type":["string","boolean","number"]}}}},"approveBooking":{"const":true,"description":"The parent’s explicit approval. Any other value returns `400`."}},"example":{"idempotencyKey":"0b7c3f52-9a41-4e2d-8f6a-3c1d5e7b9a20","childProfileId":"1f8a3c5e-7b2d-4e9f-a6c1-4d0b8e2f7a93","childProfileVersion":4,"sessionId":"c3a9e1f2-6d4b-4a8c-b2e7-0f5d9c1a3b48","maxSpend":{"amountCents":45000,"currency":"USD"},"allowWaitlist":true,"autoAcceptDecisions":false,"shareFields":["child.birthDate"],"answers":[{"key":"photo_consent","label":"Photo consent","value":false}],"approveBooking":true}},"BookingDecisionOption":{"type":"object","required":["optionId","label","total"],"properties":{"optionId":{"type":"string"},"label":{"type":"string"},"total":{"oneOf":[{"$ref":"#/components/schemas/BookingMoney"},{"type":"null"}],"description":"The camp’s total if the parent chooses this option."}}},"BookingPendingDecision":{"type":"object","required":["decisionId","code","message","options","proposedOptionId","expiresAt","screenshot"],"properties":{"decisionId":{"type":"string","format":"uuid"},"code":{"type":"string","enum":["unknown_question","choose_option","session_changed","waiver_signature","provider_account_required","unverified_step","answer_required"],"x-open-enum":true},"message":{"type":"string"},"options":{"type":"array","items":{"$ref":"#/components/schemas/BookingDecisionOption"}},"proposedOptionId":{"type":["string","null"]},"expiresAt":{"type":"string","format":"date-time","description":"When the camp’s checkout hold ends. After this, the booking fails with `decision_expired`."},"screenshot":{"description":"The camp’s page as Hallpass last saw it, for an `unverified_step` decision; null otherwise.","oneOf":[{"type":"null"},{"type":"object","additionalProperties":false,"required":["mediaType","data","width","height","capturedAt"],"properties":{"mediaType":{"type":"string","enum":["image/jpeg","image/png"]},"data":{"type":"string","description":"Base64 of the image."},"width":{"type":"integer","minimum":1},"height":{"type":"integer","minimum":1},"capturedAt":{"type":"string","format":"date-time"}}}]}}},"BookingDecisionRecord":{"type":"object","required":["decisionId","code","outcome","optionId","decidedBy","decidedAt"],"properties":{"decisionId":{"type":"string","format":"uuid"},"code":{"type":"string"},"outcome":{"type":"string","enum":["accepted","declined","expired"],"x-open-enum":true},"optionId":{"type":["string","null"]},"decidedBy":{"type":["string","null"],"enum":["parent","auto",null]},"decidedAt":{"type":"string","format":"date-time","description":"When the question was answered."}}},"BookingReceipt":{"type":"object","required":["providerReference","providerStatus","amount","confirmedAt"],"properties":{"providerReference":{"type":"string","description":"The camp’s confirmation or order number."},"providerStatus":{"type":"string","enum":["enrolled","waitlisted"]},"amount":{"oneOf":[{"$ref":"#/components/schemas/BookingMoney"},{"type":"null"}],"description":"What the parent was charged. Present only when there was a charge."},"confirmedAt":{"type":"string","format":"date-time","description":"When the camp confirmed the booking."}}},"BookingFailure":{"type":"object","required":["code","message","retryable"],"properties":{"code":{"type":"string","examples":["payment_declined","payment_method_required","booking_rejected","sold_out","price_above_approval","decision_expired"]},"message":{"type":"string"},"retryable":{"type":"boolean","description":"Whether a new, re-approved booking might succeed."}}},"BookingPhase":{"type":"string","enum":["in_progress","needs_parent","booked","waitlisted","not_booked","checking"],"description":"What the booking means to the parent, and which payload it carries. `in_progress`: School’s Out is booking it. `needs_parent`: the camp asked a question only the parent can answer; `decision` carries it. `booked`: the camp enrolled the child; `receipt` carries the confirmation. `waitlisted`: the child holds a waitlist place; `receipt` carries it. `not_booked`: the booking ended without a place; `failure` explains why, or is `null` when the parent cancelled. `checking`: the outcome isn’t known yet; `checkingWith` says whether School’s Out is resolving the card hold or confirming with the camp. Never show `checking` as booked or failed. Adding a phase is a breaking change."},"FamilyBookingCommon":{"type":"object","description":"The fields every booking carries, whatever its phase.","required":["id","phase","status","paymentStatus","childProfileId","organization","session","approval","progressStage","hold","decisions","pollAfterMs","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"phase":{"$ref":"#/components/schemas/BookingPhase"},"status":{"$ref":"#/components/schemas/BookingStatus"},"paymentStatus":{"$ref":"#/components/schemas/BookingPaymentStatus"},"childProfileId":{"type":["string","null"],"format":"uuid","description":"`null` after the profile is deleted. The booking itself remains."},"organization":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"session":{"type":"object","required":["id","name","startDate","endDate"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"startDate":{"type":["string","null"],"format":"date","description":"First day of the session."},"endDate":{"type":["string","null"],"format":"date","description":"Last day of the session."}}},"approval":{"type":"object","required":["approvedAt","maxSpend","allowWaitlist","autoAcceptDecisions","sharedFields"],"properties":{"approvedAt":{"type":"string","format":"date-time","description":"When the parent approved the booking."},"maxSpend":{"$ref":"#/components/schemas/BookingMoney"},"allowWaitlist":{"type":"boolean"},"autoAcceptDecisions":{"type":"boolean"},"sharedFields":{"type":"array","items":{"$ref":"#/components/schemas/BookingShareableField"},"description":"The optional fields the parent chose to share. Always-shared fields aren’t listed."}}},"progressStage":{"type":["string","null"],"enum":["resolving_session","filling_form","reviewing_total","paying","confirming",null],"x-open-enum":true,"description":"The furthest checkout stage Hallpass reported, or `null` before it reports one. New stages may be added."},"hold":{"description":"Why a running run is waiting on something nobody can answer: Pilgrim holding an earlier task for a person's review, Pilgrim's assistance switched off at its Mac, or Hallpass retrying its worker. `null` while the run moves on its own.","oneOf":[{"type":"null"},{"type":"object","additionalProperties":false,"required":["code","message","since"],"properties":{"code":{"type":"string","enum":["pilgrim_queue_held","pilgrim_assistance_off","executor_retrying","pilgrim_busy"],"x-open-enum":true},"message":{"type":"string","minLength":1,"maxLength":500,"description":"What the run waits for, written for the requester."},"since":{"type":"string","format":"date-time"}}}]},"decisions":{"type":"array","items":{"$ref":"#/components/schemas/BookingDecisionRecord"}},"pollAfterMs":{"type":["integer","null"],"description":"How long to wait before reading the booking again. `null` once nothing will change without the parent."},"createdAt":{"type":"string","format":"date-time","description":"When the booking was created."},"updatedAt":{"type":"string","format":"date-time","description":"When the booking last changed."}}},"FamilyBookingInProgress":{"description":"School’s Out is placing the card hold or booking with the camp.","allOf":[{"$ref":"#/components/schemas/FamilyBookingCommon"},{"type":"object","required":["phase","status","decision","receipt","failure","checkingWith"],"properties":{"phase":{"type":"string","const":"in_progress"},"status":{"type":"string","enum":["authorizing","prepared","submitting","submit_uncertain","accepted","running"],"x-open-enum":true},"decision":{"type":"null"},"receipt":{"type":"null"},"failure":{"type":"null"},"checkingWith":{"type":"null"}}}]},"FamilyBookingNeedsParent":{"description":"The camp asked a question only the parent can answer. Answer it before `decision.expiresAt`.","allOf":[{"$ref":"#/components/schemas/FamilyBookingCommon"},{"type":"object","required":["phase","status","decision","receipt","failure","checkingWith"],"properties":{"phase":{"type":"string","const":"needs_parent"},"status":{"type":"string","enum":["awaiting_decision"],"x-open-enum":true},"decision":{"$ref":"#/components/schemas/BookingPendingDecision"},"receipt":{"type":"null"},"failure":{"type":"null"},"checkingWith":{"type":"null"}}}]},"FamilyBookingBooked":{"description":"The camp enrolled the child. `receipt` has been checked against the approval. `paymentStatus` may still be `held` while the charge settles.","allOf":[{"$ref":"#/components/schemas/FamilyBookingCommon"},{"type":"object","required":["phase","status","decision","receipt","failure","checkingWith"],"properties":{"phase":{"type":"string","const":"booked"},"status":{"type":"string","enum":["confirmed"],"x-open-enum":true},"decision":{"type":"null"},"receipt":{"$ref":"#/components/schemas/BookingReceipt"},"failure":{"type":"null"},"checkingWith":{"type":"null"}}}]},"FamilyBookingWaitlisted":{"description":"The child holds a waitlist place. `receipt.amount` is present when the camp took a deposit.","allOf":[{"$ref":"#/components/schemas/FamilyBookingCommon"},{"type":"object","required":["phase","status","decision","receipt","failure","checkingWith"],"properties":{"phase":{"type":"string","const":"waitlisted"},"status":{"type":"string","enum":["waitlisted"],"x-open-enum":true},"decision":{"type":"null"},"receipt":{"$ref":"#/components/schemas/BookingReceipt"},"failure":{"type":"null"},"checkingWith":{"type":"null"}}}]},"FamilyBookingNotBooked":{"description":"The booking ended without a place. `failure` is `null` only when the parent cancelled cleanly.","allOf":[{"$ref":"#/components/schemas/FamilyBookingCommon"},{"type":"object","required":["phase","status","decision","receipt","failure","checkingWith"],"properties":{"phase":{"type":"string","const":"not_booked"},"status":{"type":"string","enum":["failed","cancelled"],"x-open-enum":true},"decision":{"type":"null"},"receipt":{"type":"null"},"failure":{"oneOf":[{"$ref":"#/components/schemas/BookingFailure"},{"type":"null"}]},"checkingWith":{"type":"null"}}}]},"FamilyBookingChecking":{"description":"The outcome isn’t known yet. Never show it as booked or failed.","allOf":[{"$ref":"#/components/schemas/FamilyBookingCommon"},{"type":"object","required":["phase","status","decision","receipt","failure","checkingWith"],"properties":{"phase":{"type":"string","const":"checking"},"status":{"type":"string","enum":["uncertain"],"x-open-enum":true},"decision":{"type":"null"},"receipt":{"type":"null"},"failure":{"type":"null"},"checkingWith":{"type":"string","enum":["card","camp"],"description":"`card`: a card hold School’s Out could not resolve. Nothing reached the camp, the parent may cancel, and the child may be booked again. `camp`: School’s Out is confirming the outcome with the camp."}}}]},"FamilyBooking":{"description":"A family booking. `phase` selects the variant and decides which of `decision`, `receipt`, `failure` and `checkingWith` are present.","oneOf":[{"$ref":"#/components/schemas/FamilyBookingInProgress"},{"$ref":"#/components/schemas/FamilyBookingNeedsParent"},{"$ref":"#/components/schemas/FamilyBookingBooked"},{"$ref":"#/components/schemas/FamilyBookingWaitlisted"},{"$ref":"#/components/schemas/FamilyBookingNotBooked"},{"$ref":"#/components/schemas/FamilyBookingChecking"}],"discriminator":{"propertyName":"phase","mapping":{"in_progress":"#/components/schemas/FamilyBookingInProgress","needs_parent":"#/components/schemas/FamilyBookingNeedsParent","booked":"#/components/schemas/FamilyBookingBooked","waitlisted":"#/components/schemas/FamilyBookingWaitlisted","not_booked":"#/components/schemas/FamilyBookingNotBooked","checking":"#/components/schemas/FamilyBookingChecking"}}},"BookingEventKind":{"type":"string","enum":["approval_granted","payment_hold_placed","payment_hold_failed","task_submitted","task_submit_uncertain","task_status_observed","decision_requested","decision_accepted","decision_declined","confirmation_received","payment_captured","payment_capture_failed","payment_released","cancel_requested","reconciled"],"x-open-enum":true,"description":"What happened. New kinds may be added; show `summary` for a kind you don’t recognise."},"BookingActor":{"type":"string","enum":["parent","auto","hallpass","system","staff"],"x-open-enum":true,"description":"Who caused the event. New actors may be added."},"BookingEvent":{"type":"object","required":["kind","status","actor","summary","createdAt"],"properties":{"kind":{"$ref":"#/components/schemas/BookingEventKind"},"status":{"$ref":"#/components/schemas/BookingStatus","description":"The booking’s status after this event."},"actor":{"$ref":"#/components/schemas/BookingActor"},"summary":{"type":"string","maxLength":500},"createdAt":{"type":"string","format":"date-time","description":"When the event was recorded."}}},"FamilyBookingDetail":{"allOf":[{"$ref":"#/components/schemas/FamilyBooking"},{"type":"object","required":["events"],"properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/BookingEvent"},"description":"The full history, oldest first."}}}]},"BookingPaymentMethod":{"type":"object","required":["brand","last4"],"properties":{"brand":{"type":"string","description":"Can be empty; show “Card” in that case."},"last4":{"type":"string","pattern":"^[0-9]{4}$"}}},"FamilyFeedPost":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string"},"topic":{"type":"string"},"headline":{"type":"string"},"body":{"type":"array","items":{"type":"string"}},"reason":{"type":"string"},"signal":{"type":["string","null"],"enum":["like","dislike","dont_show_again",null]}},"required":["id","kind","topic","headline","body","reason","signal"],"additionalProperties":false},"FamilyFeedEditionKey":{"type":"object","properties":{"date":{"type":"string","format":"date"},"slot":{"type":"string","enum":["morning","evening"]}},"required":["date","slot"],"additionalProperties":false},"FamilyFeedEdition":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"date":{"type":"string","format":"date"},"slot":{"type":"string","enum":["morning","evening"]},"publishedAt":{"type":"string","format":"date-time"},"posts":{"type":"array","items":{"$ref":"#/components/schemas/FamilyFeedPost"}}},"required":["id","date","slot","publishedAt","posts"],"additionalProperties":false}},"securitySchemes":{"AccountCredential":{"description":"Current account credential. Named machine keys cannot execute SQL. Intelligence alternatively uses its signed session cookie; cookie POST requests require the session-bound CSRF token and trusted origin.","in":"header","name":"x-api-key","type":"apiKey"},"ApiKeyAuth":{"description":"Account credential or named machine key for integrations. Keep it server-side and send it in x-api-key. Intelligence browser requests separately support the existing signed session cookie; cookie mutations require CSRF verification.","in":"header","name":"x-api-key","type":"apiKey"}}},"info":{"description":"Read-only external integration access to the canonical School’s Out catalog and scores. Legacy Insights datasets require ADMIN access. SQL, generic database access, physical table names, and writes are not part of the public API. Product and operator operations are marked x-internal and retain their own credential, plan, role, origin and CSRF requirements.","title":"School’s Out family API","version":"0.2.0"},"openapi":"3.1.0","paths":{"/api/users/viewer/child-invitations/accept":{"post":{"description":"Current nondelegated account with a live verified email matching the invitation recipient. Wrong or unverified accounts receive 403 without child details; unavailable tokens return 404. POST {token,approve:true} consumes an available invitation once and grants guardian access in one transaction. The actor’s current email and verification state are locked, then the profile and invitation are locked and expiry/revocation rechecked. Returns data.child with role guardian. Guardians may read and correct current details/preferences and use owner-enabled registration reuse; they cannot delete the profile, change reuse permission, or manage other guardians. Profile or owner removal cascades invitations and memberships.","requestBody":{"content":{"application/json":{"schema":{"properties":{"approve":{"const":true,"type":"boolean"},"token":{"maxLength":43,"minLength":43,"pattern":"^[A-Za-z0-9_-]{43}$","type":"string"}},"required":["token","approve"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"child":{"$ref":"#/components/schemas/FamilyChildProfile"}},"required":["child"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid invitation input or missing explicit approval"},"401":{"description":"Current account required"},"403":{"description":"Owner-only action or current verified recipient email required"},"404":{"description":"Profile, guardian or invitation not available to this account"},"409":{"description":"Duplicate pending invitation or guardian limit"},"429":{"description":"Invitation creation rate limit"}},"security":[{"AccountCredential":[]}],"summary":"Explicitly accept guardian access once","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Child profiles are visible to their owner and accepted active guardians; unrelated accounts receive 404.","x-curl-examples":[{"code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/child-invitations/accept\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'","title":"Explicitly accept guardian access once"}],"x-internal":true}},"/api/users/viewer/child-invitations/preview":{"post":{"description":"Current nondelegated account with a live verified email matching the invitation recipient. Wrong or unverified accounts receive 403 without child details; unavailable tokens return 404. POST {token} returns the child name, inviting owner email, recipient address and invitation dates for review. It does not return saved profile fields or grant access. Tokens travel in the JSON body, not the API URL. Invitation landing links use /family/invitations#token.","requestBody":{"content":{"application/json":{"schema":{"properties":{"token":{"maxLength":43,"minLength":43,"pattern":"^[A-Za-z0-9_-]{43}$","type":"string"}},"required":["token"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"invitation":{"$ref":"#/components/schemas/ChildInvitationPreview"}},"required":["invitation"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid invitation input or missing explicit approval"},"401":{"description":"Current account required"},"403":{"description":"Owner-only action or current verified recipient email required"},"404":{"description":"Profile, guardian or invitation not available to this account"},"409":{"description":"Duplicate pending invitation or guardian limit"},"429":{"description":"Invitation creation rate limit"}},"security":[{"AccountCredential":[]}],"summary":"Review an invitation addressed to your verified email","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Child profiles are visible to their owner and accepted active guardians; unrelated accounts receive 404.","x-curl-examples":[{"code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/child-invitations/preview\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'","title":"Review an invitation addressed to your verified email"}],"x-internal":true}},"/api/users/viewer/children":{"get":{"description":"Current account cookie or account credential only; machine keys and delegated service requests are rejected. Lists owned child profiles and profiles with an accepted, active guardian membership, with role owner or guardian. Each profile includes its registration fields, planning details, interests and preference notes, current version and current-value provenance. Provenance records manual approval or an approved Assistant suggestion, the actor and approval time; another adult's conversation reference is omitted. No previous values are retained. Connected-service imports are not supported.","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"children":{"items":{"$ref":"#/components/schemas/FamilyChildProfile"},"type":"array"}},"required":["children"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid profile input"},"401":{"description":"Current account required"},"404":{"description":"Profile not found for this owner"},"409":{"description":"Stale version or profile count limit"}},"security":[{"AccountCredential":[]}],"summary":"List profiles you own or have accepted guardian access to","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Child profiles are visible to their owner and accepted active guardians; unrelated accounts receive 404.","x-curl-examples":[{"code":"curl -X GET \"https://apis.schoolsoutapp.com/api/users/viewer/children\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\"","title":"List profiles you own or have accepted guardian access to"}],"x-internal":true},"post":{"description":"Creates one of up to 20 owned profiles from approved values. A planning label or first and last names is required; registration reuse needs both names and defaults to false. Fields are canonical registration strings; preferences hold interests with an unknown/none/listed status and notes; planning holds structured planning details. Each current value records source, actor, approval time and version. The authenticated viewer is always the approving actor. Optional source {type:\"manual\"} or {type:\"assistant\", conversationId, turnId} names one of the viewer's own completed Assistant turns that proposed the change; the write itself is the explicit approval.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FamilyChildInput"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"child":{"$ref":"#/components/schemas/FamilyChildProfile"}},"required":["child"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid profile input"},"401":{"description":"Current account required"},"404":{"description":"Profile not found for this owner"},"409":{"description":"Stale version or profile count limit"}},"security":[{"AccountCredential":[]}],"summary":"Create a manually approved child profile","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Child profiles are visible to their owner and accepted active guardians; unrelated accounts receive 404.","x-curl-examples":[{"code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/children\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'","title":"Create a manually approved child profile"}],"x-internal":true}},"/api/users/viewer/children/{childId}":{"delete":{"description":"DELETE {version} permanently removes the profile, guardian memberships, invitations, care requirement links and child-linked remembered facts; submitted registration snapshots and receipts remain. Owner only. Stale versions return 409.","parameters":[{"in":"path","name":"childId","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"version":{"minimum":1,"type":"integer"}},"required":["version"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"deleted":{"const":true,"type":"boolean"}},"required":["deleted"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid profile input"},"401":{"description":"Current account required"},"404":{"description":"Profile not found for this owner"},"409":{"description":"Stale version or profile count limit"}},"security":[{"AccountCredential":[]}],"summary":"Permanently remove the reusable profile; existing registration snapshots remain","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Child profiles are visible to their owner and accepted active guardians; unrelated accounts receive 404.","x-curl-examples":[{"code":"curl -X DELETE \"https://apis.schoolsoutapp.com/api/users/viewer/children/$CHILD_ID\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"version\":1}'","title":"Permanently remove the reusable profile; existing registration snapshots remain"}],"x-internal":true},"patch":{"description":"Applies a narrow correction to fields, preferences.notes, preferences.interests ({status, values}), planning.label, planning.age, planning.gradeSchoolYear or planning.requirements ({status, items}); null removes a value. reuseAllowed cannot be changed here. Owner or current guardian; locks the actor and profile and rechecks membership before saving. The authenticated viewer is always the approving actor. Optional source {type:\"manual\"} or {type:\"assistant\", conversationId, turnId} names one of the viewer's own completed Assistant turns that proposed the change; the write itself is the explicit approval.","parameters":[{"in":"path","name":"childId","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FamilyCorrection"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"child":{"$ref":"#/components/schemas/FamilyChildProfile"}},"required":["child"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid input"},"401":{"description":"Current account required"},"404":{"description":"Record not found for this account"},"409":{"description":"Stale version, limit, or nothing to reconfirm"}},"security":[{"AccountCredential":[]}],"summary":"Correct named child profile values without replacing the profile","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Child profiles are visible to their owner and accepted active guardians; unrelated accounts receive 404.","x-curl-examples":[{"code":"curl -X PATCH \"https://apis.schoolsoutapp.com/api/users/viewer/children/$CHILD_ID\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"version\":3,\"changes\":{\"child.allergies\":null,\"planning.requirements\":{\"status\":\"none\",\"items\":[]}}}'","title":"Remove an allergy note and record that there are no participation requirements"}],"x-internal":true},"put":{"description":"PUT fully replaces fields and preferences with the current version; omitted or blank optional fields are removed. Omitting planning keeps the saved planning so older clients cannot clear it; sending planning replaces it. Only the owner may change reuseAllowed. Unchanged values retain provenance; changed values record the acting account, source and new version. Stale versions return 409. The authenticated viewer is always the approving actor. Optional source {type:\"manual\"} or {type:\"assistant\", conversationId, turnId} names one of the viewer's own completed Assistant turns that proposed the change; the write itself is the explicit approval.","parameters":[{"in":"path","name":"childId","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/FamilyChildInput"},{"required":["version"],"type":"object"}]}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"child":{"$ref":"#/components/schemas/FamilyChildProfile"}},"required":["child"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid profile input"},"401":{"description":"Current account required"},"404":{"description":"Profile not found for this owner"},"409":{"description":"Stale version or profile count limit"}},"security":[{"AccountCredential":[]}],"summary":"Correct an owned or shared child profile using its current version","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Child profiles are visible to their owner and accepted active guardians; unrelated accounts receive 404.","x-curl-examples":[{"code":"curl -X PUT \"https://apis.schoolsoutapp.com/api/users/viewer/children/$CHILD_ID\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'","title":"Correct an owned or shared child profile using its current version"}],"x-internal":true}},"/api/users/viewer/children/{childId}/confirmations":{"post":{"description":"Records a fresh approval of the named current values (provenance keys such as guardian.email, preferences.interests or planning.age) without changing them. Owner or current guardian. The authenticated viewer is always the approving actor. Optional source {type:\"manual\"} or {type:\"assistant\", conversationId, turnId} names one of the viewer's own completed Assistant turns that proposed the change; the write itself is the explicit approval.","parameters":[{"in":"path","name":"childId","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FamilyReconfirmation"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"child":{"$ref":"#/components/schemas/FamilyChildProfile"}},"required":["child"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid input"},"401":{"description":"Current account required"},"404":{"description":"Record not found for this account"},"409":{"description":"Stale version, limit, or nothing to reconfirm"}},"security":[{"AccountCredential":[]}],"summary":"Reconfirm unchanged child profile values","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Child profiles are visible to their owner and accepted active guardians; unrelated accounts receive 404.","x-curl-examples":[{"code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/children/$CHILD_ID/confirmations\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"version\":4,\"keys\":[\"child.grade\",\"planning.gradeSchoolYear\"]}'","title":"Reconfirm the saved grade and its school year"}],"x-internal":true}},"/api/users/viewer/children/{childId}/guardians":{"get":{"description":"Current profile owner only; machine, service and delegated-service credentials are rejected. Cookie mutations require CSRF. GET returns active guardian accounts and unexpired pending invitations. It never returns invitation tokens, token hashes, or acceptance links. A profile has at most five active or pending guardians in addition to its owner.","parameters":[{"in":"path","name":"childId","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"guardians":{"items":{"$ref":"#/components/schemas/ChildGuardian"},"type":"array"},"invitations":{"items":{"$ref":"#/components/schemas/ChildInvitation"},"type":"array"}},"required":["guardians","invitations"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid invitation input or missing explicit approval"},"401":{"description":"Current account required"},"403":{"description":"Owner-only action or current verified recipient email required"},"404":{"description":"Profile, guardian or invitation not available to this account"},"409":{"description":"Duplicate pending invitation or guardian limit"},"429":{"description":"Invitation creation rate limit"}},"security":[{"AccountCredential":[]}],"summary":"Review guardian access and pending invitations","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Owner-only guardian management; API_ASSISTANT_ORIGINS are not trusted on these routes.","x-curl-examples":[{"code":"curl -X GET \"https://apis.schoolsoutapp.com/api/users/viewer/children/$CHILD_ID/guardians\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\"","title":"Review guardian access and pending invitations"}],"x-internal":true}},"/api/users/viewer/children/{childId}/guardians/{userId}":{"delete":{"description":"Current profile owner only; machine, service and delegated-service credentials are rejected. Cookie mutations require CSRF. DELETE removes this child’s guardian membership. Future profile reads, corrections and registration reuse recheck membership and refuse after revocation. Other children and existing registration receipts are unchanged.","parameters":[{"in":"path","name":"childId","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"userId","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"revoked":{"const":true,"type":"boolean"}},"required":["revoked"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid invitation input or missing explicit approval"},"401":{"description":"Current account required"},"403":{"description":"Owner-only action or current verified recipient email required"},"404":{"description":"Profile, guardian or invitation not available to this account"},"409":{"description":"Duplicate pending invitation or guardian limit"},"429":{"description":"Invitation creation rate limit"}},"security":[{"AccountCredential":[]}],"summary":"Remove guardian access","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Owner-only guardian management; API_ASSISTANT_ORIGINS are not trusted on these routes.","x-curl-examples":[{"code":"curl -X DELETE \"https://apis.schoolsoutapp.com/api/users/viewer/children/$CHILD_ID/guardians/$USER_ID\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'","title":"Remove guardian access"}],"x-internal":true}},"/api/users/viewer/children/{childId}/invitations":{"post":{"description":"Current profile owner only; machine, service and delegated-service credentials are rejected. Cookie mutations require CSRF. POST {email} creates an invitation valid for seven days. The response reveals inviteUrl once and reports emailSent. Configured email delivery is attempted after commit; failed or unavailable email delivery leaves a usable copy link. The fragment contains 32 random bytes encoded as 43 URL-safe base64 characters; only its SHA-256 hash is stored. The invitee must verify the matching current account email, review the invitation, and explicitly accept. Creating an invitation grants no access. Duplicate pending addresses or the five-guardian limit return 409; creation is limited to 20 invitations per account per hour.","parameters":[{"in":"path","name":"childId","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"email":{"format":"email","maxLength":254,"type":"string"}},"required":["email"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"emailSent":{"type":"boolean"},"invitation":{"$ref":"#/components/schemas/ChildInvitation"},"inviteUrl":{"description":"Revealed once; token lives in the URL fragment.","format":"uri","type":"string"}},"required":["invitation","inviteUrl","emailSent"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid invitation input or missing explicit approval"},"401":{"description":"Current account required"},"403":{"description":"Owner-only action or current verified recipient email required"},"404":{"description":"Profile, guardian or invitation not available to this account"},"409":{"description":"Duplicate pending invitation or guardian limit"},"429":{"description":"Invitation creation rate limit"}},"security":[{"AccountCredential":[]}],"summary":"Invite another guardian to this child profile","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Owner-only guardian management; API_ASSISTANT_ORIGINS are not trusted on these routes.","x-curl-examples":[{"code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/children/$CHILD_ID/invitations\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'","title":"Invite another guardian to this child profile"}],"x-internal":true}},"/api/users/viewer/children/{childId}/invitations/{invitationId}":{"delete":{"description":"Current profile owner only; machine, service and delegated-service credentials are rejected. Cookie mutations require CSRF. DELETE revokes an unaccepted invitation for this child. Acceptance and revocation serialize on the profile, so a revoked invitation cannot later grant access. Use the guardian removal route after an invitation has been accepted.","parameters":[{"in":"path","name":"childId","required":true,"schema":{"format":"uuid","type":"string"}},{"in":"path","name":"invitationId","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"data":{"properties":{"revoked":{"const":true,"type":"boolean"}},"required":["revoked"],"type":"object"},"success":{"const":true,"type":"boolean"}},"required":["success","data"],"type":"object"}}},"description":"Success"},"400":{"description":"Invalid invitation input or missing explicit approval"},"401":{"description":"Current account required"},"403":{"description":"Owner-only action or current verified recipient email required"},"404":{"description":"Profile, guardian or invitation not available to this account"},"409":{"description":"Duplicate pending invitation or guardian limit"},"429":{"description":"Invitation creation rate limit"}},"security":[{"AccountCredential":[]}],"summary":"Revoke an invitation","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Owner-only guardian management; API_ASSISTANT_ORIGINS are not trusted on these routes.","x-curl-examples":[{"code":"curl -X DELETE \"https://apis.schoolsoutapp.com/api/users/viewer/children/$CHILD_ID/invitations/$INVITATION_ID\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'","title":"Revoke an invitation"}],"x-internal":true}},"/api/users/viewer/registrations":{"get":{"description":"Requires an account session or account credential belonging to the parent. Machine keys, service and delegated-service credentials are rejected. Cookie mutations require the current CSRF token. Responses are private and not cached.","responses":{"200":{"description":"Latest 200 owner-scoped registrations, newest first; data.registrations carries id, organizationId, organizationName, childProfileId (nullable after removal), profileVersion, confirmationCode, status, createdAt and authorization. Authorization carries version, purpose, approvedAt, sharedFields, reusedFields, fieldLabels and consentStatements. Previously submitted registrations remain after profile removal."},"401":{"description":"Sign-in required"},"403":{"description":"Account credential required"}},"security":[{"AccountCredential":[]}],"summary":"List your registration disclosure receipts","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Registration receipts belong to the submitting adult.","x-curl-examples":[{"code":"curl -X GET \"https://apis.schoolsoutapp.com/api/users/viewer/registrations\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\"","title":"List your registration disclosure receipts"}],"x-internal":true}},"/api/users/viewer/registrations/{id}":{"post":{"description":"Current account owns the child profile or has accepted active guardian access; cookie mutations require CSRF. Machine keys and service impersonation are rejected. Registers with camp id using an explicitly approved saved profile. Send the normal registration body plus profile {id,version}, approveSharing:true and consentStatements [{key,label,text}] matching each accepted form acknowledgement. The transaction locks the submitting account and profile, then rechecks live owner or guardian access, version and owner-controlled reuse permission before allocating capacity. The receipt belongs to the submitting account; joining a profile does not grant access to another account’s registration history. Corrections never rewrite submitted registrations. Stale profile/consent returns 409; inaccessible profiles return 404. Guest registration remains at POST /api/organizations/{id}/registrations.","parameters":[{"description":"Destination organization ID","in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"description":"Canonical and custom answers follow the destination’s public registration form. Only visible requested fields are accepted. Consent statements must exactly match accepted acknowledgements in form order. The API checks the current profile version/reuse permission in the same transaction as registration and capacity.","properties":{"acknowledgements":{"type":"object"},"answers":{"type":"object"},"approveSharing":{"const":true,"type":"boolean"},"child":{"type":"object"},"consent":{"const":true,"type":"boolean"},"consentStatements":{"items":{"properties":{"key":{"type":"string"},"label":{"type":"string"},"text":{"type":"string"}},"required":["key","label","text"],"type":"object"},"type":"array"},"emergencyContact":{"type":"object"},"guardian":{"type":"object"},"profile":{"properties":{"id":{"format":"uuid","type":"string"},"version":{"maximum":9007199254740991,"minimum":1,"type":"integer"}},"required":["id","version"],"type":"object"},"secondaryGuardian":{"type":"object"},"sessionIds":{"items":{"format":"uuid","type":"string"},"maxItems":6,"type":"array"},"source":{"type":"object"}},"required":["profile","approveSharing","consentStatements","child","guardian","consent"],"type":"object"}}},"required":true},"responses":{"201":{"description":"Registration receipt in data.registration; approved disclosure persisted atomically"},"400":{"description":"Invalid reference or missing approval"},"401":{"description":"Sign-in required"},"403":{"description":"Account credential required"},"404":{"description":"Camp or owned child profile not found"},"409":{"description":"Profile changed, reuse revoked, form closed, or consent wording changed; reload and review"},"422":{"description":"Registration answers or selected sessions are invalid"}},"security":[{"AccountCredential":[]}],"summary":"Submit an explicitly approved registration using your child profile","tags":["Internal family profiles"],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Registration receipts belong to the submitting adult.","x-curl-examples":[{"code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/registrations/$ID\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'","title":"Submit an explicitly approved registration using your child profile"}],"x-internal":true}},"/api/users/viewer/payment-method":{"get":{"tags":["Internal family bookings"],"x-internal":true,"summary":"Get the saved card","description":"Returns the saved card, or `null` if the parent hasn’t saved one. After setup, it returns the newest card.","responses":{"200":{"description":"The saved card, or `null`.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"const":true},"data":{"type":"object","required":["paymentMethod"],"properties":{"paymentMethod":{"oneOf":[{"$ref":"#/components/schemas/BookingPaymentMethod"},{"type":"null"}]}}}}},"example":{"success":true,"data":{"paymentMethod":{"brand":"visa","last4":"4242"}}}}}},"401":{"description":"Current account required. A request without a session or credential answers {\"authorized\": false}; a machine key or delegated service request answers the error envelope with message \"unauthorized.\", because these routes act only as the signed-in parent."},"404":{"$ref":"#/components/responses/BookingsDisabled"}},"security":[{"AccountCredential":[]}],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. The route is registered only when the API runs with BOOKINGS_ENABLED=true; otherwise it returns 404.","x-curl-examples":[{"title":"Get the saved card","code":"curl -X GET \"https://apis.schoolsoutapp.com/api/users/viewer/payment-method\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\""}]}},"/api/users/viewer/payment-method/setup":{"post":{"tags":["Internal family bookings"],"x-internal":true,"summary":"Start card setup","description":"Returns a Stripe Checkout URL in setup mode. Send the browser there; Stripe returns the parent to `returnUrl` afterward.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["returnUrl"],"properties":{"returnUrl":{"type":"string","format":"uri","description":"Absolute URL whose origin is in the API’s `REDIRECT_ALLOWLIST`."}}},"example":{"returnUrl":"https://data.schoolsoutapp.com/family/bookings/new?session=c3a9e1f2-6d4b-4a8c-b2e7-0f5d9c1a3b48"}}}},"responses":{"200":{"description":"The Stripe-hosted setup URL.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"const":true},"data":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}}}},"example":{"success":true,"data":{"url":"https://checkout.stripe.com/c/pay/cs_live_..."}}}}},"400":{"description":"The body isn’t exactly `{ returnUrl }`, or its origin isn’t allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"returnUrl must be an allowed absolute URL.","data":null}}}},"401":{"description":"Current account required. A request without a session or credential answers {\"authorized\": false}; a machine key or delegated service request answers the error envelope with message \"unauthorized.\", because these routes act only as the signed-in parent."},"404":{"$ref":"#/components/responses/BookingsDisabled"},"503":{"description":"Stripe couldn’t start setup. Nothing was saved; try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"Card setup is unavailable. Try again shortly.","data":null}}}}},"security":[{"AccountCredential":[]}],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. The route is registered only when the API runs with BOOKINGS_ENABLED=true; otherwise it returns 404.","x-curl-examples":[{"title":"Start card setup","code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/payment-method/setup\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"returnUrl\": \"https://data.schoolsoutapp.com/family/bookings/new?session=c3a9e1f2-6d4b-4a8c-b2e7-0f5d9c1a3b48\"}'"}]}},"/api/users/viewer/bookings":{"get":{"tags":["Internal family bookings"],"x-internal":true,"summary":"List bookings","description":"Returns the parent’s 100 most recent bookings, newest first. Event history is omitted; get a single booking to read it.","responses":{"200":{"description":"The parent’s bookings.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"const":true},"data":{"type":"object","required":["bookings"],"properties":{"bookings":{"type":"array","items":{"$ref":"#/components/schemas/FamilyBooking"}}}}}},"example":{"success":true,"data":{"bookings":[{"id":"6f1d2c3b-4a5e-4f60-8b7a-1c2d3e4f5a6b","phase":"booked","status":"confirmed","paymentStatus":"captured","childProfileId":"1f8a3c5e-7b2d-4e9f-a6c1-4d0b8e2f7a93","organization":{"id":"9e4f7a21-3b6c-4d8e-a1f5-2c7b0d9e6f34","name":"Lakeside Science Camp"},"session":{"id":"c3a9e1f2-6d4b-4a8c-b2e7-0f5d9c1a3b48","name":"Robotics Week","startDate":"2027-07-12","endDate":"2027-07-16"},"approval":{"approvedAt":"2026-10-09T17:00:00Z","maxSpend":{"amountCents":45000,"currency":"USD"},"allowWaitlist":true,"autoAcceptDecisions":false,"sharedFields":["child.birthDate"]},"progressStage":null,"hold":null,"decision":null,"decisions":[],"receipt":{"providerReference":"LSC-20931","providerStatus":"enrolled","amount":{"amountCents":42500,"currency":"USD"},"confirmedAt":"2026-10-09T17:06:35Z"},"failure":null,"checkingWith":null,"pollAfterMs":null,"createdAt":"2026-10-09T17:00:00Z","updatedAt":"2026-10-09T17:06:41Z"}]}}}}},"401":{"description":"Current account required. A request without a session or credential answers {\"authorized\": false}; a machine key or delegated service request answers the error envelope with message \"unauthorized.\", because these routes act only as the signed-in parent."},"404":{"$ref":"#/components/responses/BookingsDisabled"}},"security":[{"AccountCredential":[]}],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. The route is registered only when the API runs with BOOKINGS_ENABLED=true; otherwise it returns 404.","x-curl-examples":[{"title":"List bookings","code":"curl -X GET \"https://apis.schoolsoutapp.com/api/users/viewer/bookings\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\""}]},"post":{"tags":["Internal family bookings"],"x-internal":true,"summary":"Approve a booking","description":"Records the parent’s approval, places a hold on their card for the spending limit, and sends the booking to Hallpass. A fast checkout can finish within this request; otherwise, follow the booking until it settles. The child profile must allow reuse, as it does for registration.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingApprovalInput"}}}},"responses":{"201":{"description":"The new booking. Its status may already be past `authorizing`.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"const":true},"data":{"type":"object","required":["booking"],"properties":{"booking":{"$ref":"#/components/schemas/FamilyBookingDetail"}}}}},"example":{"success":true,"data":{"booking":{"id":"6f1d2c3b-4a5e-4f60-8b7a-1c2d3e4f5a6b","phase":"in_progress","status":"accepted","paymentStatus":"held","childProfileId":"1f8a3c5e-7b2d-4e9f-a6c1-4d0b8e2f7a93","organization":{"id":"9e4f7a21-3b6c-4d8e-a1f5-2c7b0d9e6f34","name":"Lakeside Science Camp"},"session":{"id":"c3a9e1f2-6d4b-4a8c-b2e7-0f5d9c1a3b48","name":"Robotics Week","startDate":"2027-07-12","endDate":"2027-07-16"},"approval":{"approvedAt":"2026-10-09T17:00:00Z","maxSpend":{"amountCents":45000,"currency":"USD"},"allowWaitlist":true,"autoAcceptDecisions":false,"sharedFields":["child.birthDate"]},"progressStage":null,"hold":null,"decision":null,"decisions":[],"receipt":null,"failure":null,"checkingWith":null,"pollAfterMs":2000,"createdAt":"2026-10-09T17:00:00Z","updatedAt":"2026-10-09T17:06:41Z","events":[{"kind":"approval_granted","status":"authorizing","actor":"parent","summary":"Approved the booking with a spending limit of 450.00 USD.","createdAt":"2026-10-09T17:00:00Z"},{"kind":"payment_hold_placed","status":"prepared","actor":"system","summary":"Held 450.00 USD on the family card.","createdAt":"2026-10-09T17:00:01Z"},{"kind":"task_submitted","status":"submitting","actor":"system","summary":"Sent the approved booking to Hallpass.","createdAt":"2026-10-09T17:00:01Z"}]}}}}}},"200":{"description":"The existing booking for this `idempotencyKey`, returned because the request was a replay.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"const":true},"data":{"type":"object","required":["booking"],"properties":{"booking":{"$ref":"#/components/schemas/FamilyBookingDetail"}}}}}}}},"400":{"description":"The approval is invalid: an unknown property, `approveBooking` not `true`, a spending limit or currency that isn’t allowed, a field that can’t be shared or is missing from the profile, or too many answers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"Approve the booking to continue.","data":null}}}},"401":{"description":"Current account required. A request without a session or credential answers {\"authorized\": false}; a machine key or delegated service request answers the error envelope with message \"unauthorized.\", because these routes act only as the signed-in parent."},"404":{"description":"Bookings are off, the child profile isn’t available to this parent, or the session is no longer listed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"That session is no longer available.","data":null}}}},"409":{"description":"The profile changed or no longer allows reuse, the `idempotencyKey` was reused with different details, or the child already has an active or confirmed booking for this session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"This child already has a booking in progress or confirmed for this session.","data":null}}}}},"security":[{"AccountCredential":[]}],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. The route is registered only when the API runs with BOOKINGS_ENABLED=true; otherwise it returns 404.","x-curl-examples":[{"title":"Approve a booking","code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/bookings\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"idempotencyKey\": \"0b7c3f52-9a41-4e2d-8f6a-3c1d5e7b9a20\", \"childProfileId\": \"1f8a3c5e-7b2d-4e9f-a6c1-4d0b8e2f7a93\", \"childProfileVersion\": 4, \"sessionId\": \"c3a9e1f2-6d4b-4a8c-b2e7-0f5d9c1a3b48\", \"maxSpend\": {\"amountCents\": 45000, \"currency\": \"USD\"}, \"allowWaitlist\": true, \"autoAcceptDecisions\": false, \"shareFields\": [\"child.birthDate\"], \"answers\": [{\"key\": \"photo_consent\", \"label\": \"Photo consent\", \"value\": false}], \"approveBooking\": true}'"}]}},"/api/users/viewer/bookings/{bookingId}":{"get":{"tags":["Internal family bookings"],"x-internal":true,"summary":"Get a booking","description":"Returns one booking with its full event history. Read it again after `pollAfterMs`. A booking that belongs to another account looks exactly like one that doesn’t exist.","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The booking and its events.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"const":true},"data":{"type":"object","required":["booking"],"properties":{"booking":{"$ref":"#/components/schemas/FamilyBookingDetail"}}}}},"example":{"success":true,"data":{"booking":{"id":"6f1d2c3b-4a5e-4f60-8b7a-1c2d3e4f5a6b","phase":"booked","status":"confirmed","paymentStatus":"captured","childProfileId":"1f8a3c5e-7b2d-4e9f-a6c1-4d0b8e2f7a93","organization":{"id":"9e4f7a21-3b6c-4d8e-a1f5-2c7b0d9e6f34","name":"Lakeside Science Camp"},"session":{"id":"c3a9e1f2-6d4b-4a8c-b2e7-0f5d9c1a3b48","name":"Robotics Week","startDate":"2027-07-12","endDate":"2027-07-16"},"approval":{"approvedAt":"2026-10-09T17:00:00Z","maxSpend":{"amountCents":45000,"currency":"USD"},"allowWaitlist":true,"autoAcceptDecisions":false,"sharedFields":["child.birthDate"]},"progressStage":null,"hold":null,"decision":null,"decisions":[],"receipt":{"providerReference":"LSC-20931","providerStatus":"enrolled","amount":{"amountCents":42500,"currency":"USD"},"confirmedAt":"2026-10-09T17:06:35Z"},"failure":null,"checkingWith":null,"pollAfterMs":null,"createdAt":"2026-10-09T17:00:00Z","updatedAt":"2026-10-09T17:06:41Z","events":[{"kind":"approval_granted","status":"authorizing","actor":"parent","summary":"Approved the booking with a spending limit of 450.00 USD.","createdAt":"2026-10-09T17:00:00Z"},{"kind":"payment_hold_placed","status":"prepared","actor":"system","summary":"Held 450.00 USD on the family card.","createdAt":"2026-10-09T17:00:01Z"},{"kind":"task_submitted","status":"submitting","actor":"system","summary":"Sent the approved booking to Hallpass.","createdAt":"2026-10-09T17:00:01Z"},{"kind":"confirmation_received","status":"confirmed","actor":"hallpass","summary":"The camp confirmed reference LSC-20931.","createdAt":"2026-10-09T17:06:40Z"},{"kind":"payment_captured","status":"confirmed","actor":"system","summary":"Charged 425.00 USD to the family card.","createdAt":"2026-10-09T17:06:41Z"}]}}}}}},"401":{"description":"Current account required. A request without a session or credential answers {\"authorized\": false}; a machine key or delegated service request answers the error envelope with message \"unauthorized.\", because these routes act only as the signed-in parent."},"404":{"description":"No such booking for this parent, or bookings are off.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"Booking not found.","data":null}}}}},"security":[{"AccountCredential":[]}],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. The route is registered only when the API runs with BOOKINGS_ENABLED=true; otherwise it returns 404.","x-curl-examples":[{"title":"Get a booking","code":"curl -X GET \"https://apis.schoolsoutapp.com/api/users/viewer/bookings/$BOOKING_ID\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\""}]}},"/api/users/viewer/bookings/{bookingId}/decisions/{decisionId}":{"post":{"tags":["Internal family bookings"],"x-internal":true,"summary":"Answer a camp question","description":"Accepts one of the offered options, or declines. Declining ends the booking and releases the hold.","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"decisionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["outcome"],"properties":{"outcome":{"type":"string","enum":["accept","decline"]},"optionId":{"type":"string","description":"Required to accept; must be absent to decline."},"answer":{"type":["string","null"],"minLength":1,"maxLength":2000,"description":"Your typed answer. Required to accept an `answer_required` decision; omit it otherwise."}}},"examples":{"accept":{"value":{"outcome":"accept","optionId":"medium"}},"decline":{"value":{"outcome":"decline"}}}}}},"responses":{"200":{"description":"The answer was recorded and checkout resumes.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"const":true},"data":{"type":"object","required":["booking"],"properties":{"booking":{"$ref":"#/components/schemas/FamilyBookingDetail"}}}}}}}},"400":{"description":"`optionId` is missing when accepting, present when declining, or not one of the offered options.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"Choose one of the offered options.","data":null}}}},"401":{"description":"Current account required. A request without a session or credential answers {\"authorized\": false}; a machine key or delegated service request answers the error envelope with message \"unauthorized.\", because these routes act only as the signed-in parent."},"404":{"$ref":"#/components/responses/BookingNotFound"},"409":{"description":"The question was already answered or has expired, or the booking is briefly busy. Read the booking again instead of retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"That decision is no longer waiting for an answer.","data":null}}}},"503":{"description":"Hallpass couldn’t be reached. Nothing was recorded; try again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"The answer could not be sent. Try again.","data":null}}}}},"security":[{"AccountCredential":[]}],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. The route is registered only when the API runs with BOOKINGS_ENABLED=true; otherwise it returns 404.","x-curl-examples":[{"title":"Answer a camp question","code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/bookings/$BOOKING_ID/decisions/$DECISION_ID\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"outcome\": \"accept\", \"optionId\": \"medium\"}'"}]}},"/api/users/viewer/bookings/{bookingId}/cancel":{"post":{"tags":["Internal family bookings"],"x-internal":true,"summary":"Cancel a booking","description":"Cancels the booking and releases the hold, as long as the camp’s checkout hasn’t started payment.","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The cancellation was recorded. The status becomes `cancelled` once Hallpass confirms.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"const":true},"data":{"type":"object","required":["booking"],"properties":{"booking":{"$ref":"#/components/schemas/FamilyBookingDetail"}}}}}}}},"401":{"description":"Current account required. A request without a session or credential answers {\"authorized\": false}; a machine key or delegated service request answers the error envelope with message \"unauthorized.\", because these routes act only as the signed-in parent."},"404":{"$ref":"#/components/responses/BookingNotFound"},"409":{"description":"The booking is already final, checkout has started payment, or the booking is briefly busy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingError"},"example":{"error":true,"message":"Checkout has started with the camp and can no longer be cancelled here.","data":null}}}}},"security":[{"AccountCredential":[]}],"x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. The route is registered only when the API runs with BOOKINGS_ENABLED=true; otherwise it returns 404.","x-curl-examples":[{"title":"Cancel a booking","code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/bookings/$BOOKING_ID/cancel\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\""}]}},"/api/users/viewer/feed":{"get":{"tags":["Internal family profiles"],"x-internal":true,"summary":"Read the family feed","description":"Read the current immutable published edition, generating it on first request, or read a previously published edition by date and slot. Scores and ranking evidence remain private.","x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Records are private to the signed-in adult; sharing a child never shares them.","security":[{"AccountCredential":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"type":"object","properties":{"edition":{"$ref":"#/components/schemas/FamilyFeedEdition"},"current":{"$ref":"#/components/schemas/FamilyFeedEditionKey"},"editions":{"type":"array","items":{"$ref":"#/components/schemas/FamilyFeedEditionKey"}}},"required":["edition","current","editions"],"additionalProperties":false}},"required":["success","data"],"additionalProperties":false}}}},"400":{"description":"Invalid input"},"401":{"description":"Current account required"},"404":{"description":"Record not found for this account"},"409":{"description":"Household search area is required to publish the first edition."}},"x-curl-examples":[{"title":"Read the family feed","code":"curl -X GET \"https://apis.schoolsoutapp.com/api/users/viewer/feed\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\""}],"parameters":[{"name":"date","in":"query","schema":{"type":"string","format":"date"},"description":"Edition date, used with slot."},{"name":"slot","in":"query","schema":{"type":"string","enum":["morning","evening"]},"description":"Edition slot, used with date."}]}},"/api/users/viewer/feed/posts/{postId}/signal":{"put":{"tags":["Internal family profiles"],"x-internal":true,"summary":"Set a feed post signal","description":"Set, change or clear the signed-in adult's own signal. A post owned by another account returns 404.","x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Records are private to the signed-in adult; sharing a child never shares them.","security":[{"AccountCredential":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"type":"object","properties":{"postId":{"type":"string","format":"uuid"},"signal":{"type":["string","null"],"enum":["like","dislike","dont_show_again",null]}},"required":["postId","signal"],"additionalProperties":false}},"required":["success","data"],"additionalProperties":false}}}},"400":{"description":"Invalid input"},"401":{"description":"Current account required"},"404":{"description":"Record not found for this account"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"signal":{"type":["string","null"],"enum":["like","dislike","dont_show_again",null]}},"required":["signal"],"additionalProperties":false},"example":{"signal":"like"}}}},"x-curl-examples":[{"title":"Set a feed post signal","code":"curl -X PUT \"https://apis.schoolsoutapp.com/api/users/viewer/feed/posts/00000000-0000-4000-8000-000000000001/signal\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"signal\":\"like\"}'"}],"parameters":[{"name":"postId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}]}},"/api/users/viewer/feed/camp-views":{"post":{"tags":["Internal family profiles"],"x-internal":true,"summary":"Record a camp page view","description":"Record the signed-in adult's dwell time on an organization, activity or session. This is private family interest evidence.","x-auth":"Current account cookie or account credential only. Machine keys, investor previews and delegated service requests are rejected regardless of plan, billing team, staff or ADMIN level. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Records are private to the signed-in adult; sharing a child never shares them.","security":[{"AccountCredential":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"type":"object","properties":{"recorded":{"type":"boolean","const":true}},"required":["recorded"],"additionalProperties":false}},"required":["success","data"],"additionalProperties":false}}}},"400":{"description":"Invalid input"},"401":{"description":"Current account required"},"404":{"description":"Record not found for this account"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"targetKind":{"type":"string","enum":["organization","activity","session"]},"targetId":{"type":"string","format":"uuid"},"dwellSeconds":{"type":"integer","minimum":0,"maximum":86400}},"required":["targetKind","targetId","dwellSeconds"],"additionalProperties":false},"example":{"targetKind":"organization","targetId":"00000000-0000-4000-8000-000000000001","dwellSeconds":30}}}},"x-curl-examples":[{"title":"Record a camp page view","code":"curl -X POST \"https://apis.schoolsoutapp.com/api/users/viewer/feed/camp-views\" \\\n  -H \"x-api-key: $SCHOOLS_OUT_ACCOUNT_CREDENTIAL\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"targetKind\":\"organization\",\"targetId\":\"00000000-0000-4000-8000-000000000001\",\"dwellSeconds\":30}'"}]}}},"security":[{"ApiKeyAuth":[]}],"servers":[{"description":"Production","url":"https://apis.schoolsoutapp.com"}],"tags":[{"name":"Internal family profiles","description":"Private child profiles shared only with explicitly invited and accepted guardians. Machine keys and service impersonation are rejected."},{"name":"Internal family bookings","description":"Parent-approved camp bookings: the saved card, approval with a spending limit, status polling, camp questions and cancellation. Registered only with BOOKINGS_ENABLED=true; machine keys and service impersonation are rejected."}]}