Account, research, and catalog services. Use an account credential in x-api-key or an Intelligence browser session. Machine keys follow the permissions of their owning account. Hallpass, the task broker this API calls over Render’s private network, has its own reference for staff.
GET /api/activities
Lists canonical activities with Organization, city, region, category, age, skill, format, seasonality, sorting, and pagination filters. Activities have no enrollment price, currency, discount, or care-pricing fields; sessions own enrollment pricing and nullable freeform `discounts` text. The activity image_url compatibility read returns its selected active cover only. City and region match the activity's own recorded location, without parent-organization fallback. includeRows=false returns metadata and counts with an empty activities array. Unfiltered total and unfilteredTotal use durable incrementally maintained totals after relevant background work settles and fall back to live counts while committed changes are pending; filtered totals and returned rows remain live. canonical_url is the verified official primary source; relevant_urls is an array of supporting or unverified pages and does not establish an official site. Both fields require Team data access. Normal JSON records omit bookkeeping and ingestion history, null and blank record fields, and unknown availability, registration-status, and coordinate-precision values. Zero, false, empty arrays, masked values, positional range bounds, and structured schedule states retain their meanings. Related entity summaries follow the same projection; CSV and SQL retain their existing contracts. Bookkeeping and provenance are available separately at /api/activities/{id}/provenance. Age ranges use age_range_years; normal JSON records and column metadata omit the redundant age_range_months alias. CSV, SQL, and the legacy ageMonths filter retain month compatibility. Records and column metadata omit score. Fetch /api/activities/{id}/score for live detail or /api/activities/scores?ids=UUID,UUID&cached=true for persisted summaries; ordinary list/detail reads do not load scores. Each ordinary JSON row includes image_urls, a deduplicated array of its own active image URLs, ordered by selected cover, selected logo where applicable, then gallery position. Empty galleries return an empty array. Remote URLs are preserved verbatim; uploaded images retain protected organization brand-asset URLs. Documents, deleted or misowned uploads, inactive memberships, other entities’ media, and source provenance are excluded. Investor previews omit image_urls, CSV columns remain unchanged, and includeRows=false skips media reads.
This endpoint requires an API key; fields and result windows are masked to the account plan
Returns canonical Activity category, skill, format, and seasonality facets. With ?field= it returns one facet scoped to the list's filters; field=age_group counts activities by the age bands (3-5, 6-8, 9-12, 13-17 years, and unknown) their age range overlaps, and field=updated_day counts them by the day of their latest update over the last 30 days. Optional snapshot=true with a single field serves incrementally maintained chart counts when no list filters or search are active; filtered requests and the default remain live. Pending source changes or due calendar updates fall back to live counts, so committed edits, deletion, and blacklisting remain visible before background refresh completes. Authentication and field access remain live.
Returns one tier-masked canonical Activity with its Organization and Session summaries. canonical_url is the verified official primary source; relevant_urls is an array of supporting or unverified pages and does not establish an official site. Both fields require Team data access. Activities have no enrollment price, currency, discount, or care-pricing fields. Session care-pricing quotes follow price access rules; schedule hours never grant access to nested care prices. Its image_url compatibility read returns the selected active cover only. Normal JSON records omit bookkeeping and ingestion history, null and blank record fields, and unknown availability, registration-status, and coordinate-precision values. Zero, false, empty arrays, masked values, positional range bounds, and structured schedule states retain their meanings. Related entity summaries follow the same projection; CSV and SQL retain their existing contracts. Moved fields are configuration_id, external_ids, created_at, updated_at, source_created_at, field_provenance, source_records, merge_jobs, and relationship_jobs. Read permitted bookkeeping and provenance at /api/activities/{id}/provenance. Age ranges use age_range_years; normal JSON records and column metadata omit the redundant age_range_months alias. CSV, SQL, and the legacy ageMonths filter retain month compatibility. Records and column metadata omit score. Fetch /api/activities/{id}/score for live detail or /api/activities/scores?ids=UUID,UUID&cached=true for persisted summaries; ordinary list/detail reads do not load scores. Normal detail responses include data.media, the first gallery page of up to 100 images, with entity, items, coverId, logoId, total, limit, and offset. Images include displayUrl, label, kind, width, and height; source attribution is omitted even for ADMIN. Previews omit media. Optional includeMedia=false skips the gallery; omitted or true includes it. The parameter accepts one exact true or false value, with malformed or repeated values returning 400. Use the existing /media endpoint for additional pages or authorized source attribution.
Account cookie or header, or permitted machine key, from Starter up.
Returns one activity's paginated media gallery and selected cover ID with available image dimensions and permitted source attribution. limit defaults to 30 and caps at 100; offset defaults to 0 and caps at 1000000. Media belongs only to the requested activity; parent organization media is not inherited. Optional includeProvenance=false skips source attribution and its database read. Omitted or true preserves existing ADMIN source access; this flag never grants authority. Accepts one exact true or false value; malformed or repeated values return 400. Gallery-only pagination can use false; one image’s source disclosure can use true with limit=1 and that image’s offset.
Account cookie or header, or permitted machine key, from Starter up. Previews cannot read media. Source provenance is limited to non-preview ADMIN account viewers.
Returns data.activity with id and visible bookkeeping moved from normal reads. Only authorized ADMIN readers additionally receive field_provenance, source_records, merge_jobs, and relationship_jobs. Moved fields are configuration_id, external_ids, created_at, updated_at, source_created_at, field_provenance, source_records, merge_jobs, and relationship_jobs. Field provenance separates current value ownership (state, sources, value_state) from source checks and field decisions. Entries cover visible fields before sparse projection, including cleared and unknown values. A found or checked_not_found observation can exist without a value change; absence of observation means no recorded check. Sources, latest_observation, and history share a flat record shape; history is newest first. The latest observation follows observed time, while decision history follows contribution time. Omitted field_provenance means unavailable; an absent entry in an available map is unknown. Field citations are distinct from site-scoped scrape URLs.
Account cookie or header, or permitted machine key, from Starter up. Bookkeeping follows the existing field masks. Field provenance and raw histories require an ADMIN user-backed viewer, including an ADMIN-owned machine key; staff, bare service credentials, and membership upgrades do not grant this authority. All previews are denied.
Ask Assistant a question with optional current-conversation history or a saved conversation, and optionally the viewer's approved family context, using the account's read-only canonical data access, and stream a short answer with suggested replies and grounded activity or session results. With webSearch true, a catalog miss or a confirmed gap in a returned listing can fall back to web search, whose pages are returned as web sources and reported as advertised. May propose budget, search-area or child-interest preferences or a remembered fact for the parent's separate exact-value review; Assistant never saves them. Limited to staff and ADMIN accounts during internal testing.
Staff or ADMIN account only during internal testing, decided from the stored account. Current account credential or signed Intelligence session with CSRF from a trusted or API_ASSISTANT_ORIGINS origin. Service delegation to a staff or ADMIN account is allowed only without includeFamilyContext or conversation. Machine keys and investor-preview sessions are refused.
curl -N -X POST https://apis.schoolsoutapp.com/api/assistant -H "x-api-key: YOUR_ACCOUNT_CREDENTIAL" -H "Content-Type: application/json" -d '{"question":"Only afternoons","history":[{"question":"Art camps in Seattle for my 8-year-old?","answer":"Two options fit. Which dates work?"}]}'
GET /api/assistant/conversations
Returns up to 200 of the account's conversations, most recently updated first, without turns. Conversations are kept until deleted or the account is deleted.
Current account cookie or account credential only; delegated service requests, machine keys and investor previews are refused. Conversations are private to their owner. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin.
GET returns the conversation's turns in order. DELETE permanently removes the transcript and clears its references from saved provenance and remembered facts, which remain approved.
Current account cookie or account credential only; delegated service requests, machine keys and investor previews are refused. Conversations are private to their owner. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin.
GET /api/assistant/conversations/{conversationId}
GET returns the conversation's turns in order. DELETE permanently removes the transcript and clears its references from saved provenance and remembered facts, which remain approved.
Current account cookie or account credential only; delegated service requests, machine keys and investor previews are refused. Conversations are private to their owner. Cookie mutations require CSRF from a trusted or API_ASSISTANT_ORIGINS origin.
GET /api/auth/gmail-callback
Consumes the Google state and requires the starting browser nonce. Web flow exchanges the code, validates both Gmail scopes and stores the sealed grant, then returns gmail=connected, denied or failed. Native flow only seals a short-lived authorization code and returns gmail=authorized&code=<one-use completion>; denial/failure includes no code. Only authenticated app completion may exchange and save a native grant. Every valid-state native outcome echoes challenge=<original public S256>; the app must match it to its pending attempt before completing or clearing its proof.
This endpoint is open to the public (Google redirect target); the one-time state and the starting browser's nonce cookie identify the parent
GET /api/auth/gmail-start
Consumes ticket, sets the HttpOnly callback nonce in the system browser, then redirects to Google consent. Expired or replayed tickets return 400. No account credential is accepted in a URL; the browser step alone cannot store a connection.
Public, independent-auth browser navigation. A one-use 10-minute native bootstrap ticket identifies the starting account and app proof.
GET /api/canonical/map
Returns a bounded GeoJSON map of all matching organization, activity, and session records. Requires bbox=west,south,east,north and zoom. World-anchored grid cells retain complete per-kind counts and coarsen to fit the feature budget without sampling. Accessible coordinates retain full stored precision on every plan; preview field masks apply before spatial selection. Coverage describes the full matching permitted population; viewport counts describe complete cells intersecting the viewport. Optional regionKind, regionCode, and regionDataset select an immutable source boundary. Selected-region membership uses distance in meters from the viewer-masked stored point to the unsimplified boundary, with inclusive layer tolerances initially 100 m for neighborhoods, 500 m for cities/ZIPs, 2 km for counties, 5 km for metros, and 10 km for states. A point-centered tolerance circle may partially overlap the region; the center need not be inside, and records may match multiple regions. The same rule applies to address-grade and ZIP/state approximate points, without source-area polygon intersections or zoom dependence. Unknown, missing, masked, and invalid locations are excluded. Approximate quality remains separate. This does not change structured canonical/Ask location classifications. Approximate and unlocated coverage remain separate. Optional includeCampPoints=true adds precise organization-only campPoints within the requested bounds, using the same masked cohort and snapshot. Features contain UUID, point geometry, and empty properties by default. Optional includeCampNames=true additionally adds properties.name only when the camp name is disclosed, using the existing viewer-level text scrubbing and preview name masks. It requires includeCampPoints=true or returns HTTP 400. Omitted/false adds no name property and performs no camp-point name reads. Hidden or missing names have no fallback label. Names share each camp point's masked coordinate, filtered cohort, and snapshot; groups never supply camp labels. The frontend requests this projection only at label zoom and uses collision-managed native symbols. Set includeCampScores=true to add properties.scoreBand from the persisted organization score. It requires includeCampPoints=true or returns HTTP 400. Bands are weak, fair, good, strong, or unavailable and use the same thresholds as organization score details. Missing prepared scores, restricted accounts, and every preview receive unavailable. Omitted/false skips score-band reads. Workers own score calculation and refresh; map reads do not calculate scores. Approximate ZIP/state locations and child records are excluded; overflow returns HTTP 413 without truncation. Map and member reads exclude identities with committed source work still queued and overlay those identities from current source rows. Projection state and the live overlay share one repeatable-read snapshot; a proven clean snapshot takes the saved-row fast path. Initial preparation returns HTTP 503 with code=map_cache_warming; callers may retry this state. Successful finished responses may be reused from a bounded per-process memory cache keyed by normalized options, current disclosure policy, stream version, representation, and compression. PostgreSQL retains validity fences and invalidation history, not response bodies; completed responses do not survive process restarts. Every request still authenticates and checks access. Source, live eligibility, score, geography, and projection changes invalidate dependent responses; reuse expires by its maximum age and the next UTC midnight. Cache failures fall back to normal reads. Private/no-store and JSON error behavior remain unchanged. X-Map-Revision, when available, is an opaque equality token scoped to relevant source changes, disclosure, policy, and UTC day; clients must not parse it. Successful responses carry a weak ETag bound to normalized options, disclosure, representation, and compression. Send it with If-None-Match to revalidate; after current authorization and a fresh source-fence check, a match returns HTTP 304 with no body and without running the map data query. Retain a streamed ETag only after its complete frame passes sequence and count validation. Revision tokens fence server response reuse and remain available to clients; metadata revision changes do not automatically replace an already loaded Explorer point snapshot. Score writes invalidate requests with minimumScore or maximumScore filters and requests with includeCampScores=true. The map route additionally accepts application/vnd.schoolsout.map-stream+cbor;version=1. After optional whole-stream gzip decompression, each frame is a four-byte unsigned big-endian payload length followed by one CBOR object, capped at 1 MiB. Frames have type: start (version 1, bounds [west,south,east,north], opaque revision or empty string when unavailable), zero or more points (zero-based sequence, features), result (data contains ordinary map metadata without campPoints), and complete (pointCount, batchCount). Gzip flushes per frame. Points are provisional until complete and matching totals; terminal error frames contain code and message. Abort, overflow, malformed/truncated streams, invalid sequences, and missing completion cannot establish reusable coverage or cached success. Pre-stream errors retain JSON status/error responses. Completed streams may be cached as their own representation. Explorer requests all permitted, plottable camp dots in one world-bounds stream for the current filters/access, with names up front. It retains the completed cohort snapshot until filters/access change, explicit refresh, or reload. Pan and zoom request separate camera-specific counts/groups without downloading points again; metadata revision changes do not automatically refresh the dots. Camp dots open camp information directly at every zoom. Use bounded ordinary JSON/CBOR fallback when streaming is unsupported. Progressive delivery targets earlier points without a fixed latency guarantee. The route also supports application/vnd.schoolsout.map-stream+cbor;version=2 when includeCampPoints=true; omitted or false returns HTTP 400 before streaming. V2 uses the same uint32 big-endian length-prefixed CBOR framing, whole-stream gzip, 1 MiB frame limit, provisional points, terminal errors, and completion checks as v1. Its start frame contains version 2, bounds, opaque revision, and ordered columns: id, longitude, latitude, followed by name when includeCampNames=true and scoreBand when includeCampScores=true. Points frames contain zero-based sequence and positional rows matching those columns; coordinates remain float64 and an included missing or undisclosed name is null. Complete contains matching pointCount and batchCount. V2 has no features or result frame and does not calculate grouping, coverage, viewport, tier, region, or preview metadata. Selected-region validation still returns pre-stream HTTP 400 for a missing boundary or dataset, or a dataset that is not active; it never substitutes an empty successful point population. Accept quality chooses v1 or v2. Each API instance warms completed-response bytes independently, so a restart begins cold. Every reuse validates the current database snapshot, compaction epoch, scoped dependency revision, and UTC evaluation day. Durable canonical map and boundary materialized projections remain in PostgreSQL across restarts. Migration 0047 adds GiST indexes on the guarded WGS84 geography expressions used by selected-region reads from canonical_map_records and entity_configurations. They narrow candidate identities; the query materializes distinct float8 longitude/latitude pairs, evaluates authoritative ST_DWithin once per pair, then joins matches back to every identity. Float8 is the existing ST_MakePoint input conversion, not new rounding. Stored source coordinates, output precision, masking, preview disclosure, boundary geometry, tolerances, and exact membership identities remain unchanged. Prepare migration 0047 before deploying the matching API code. collectionId requires a direct staff or ADMIN account (no service delegation, machine key, or preview) and selects staff-curated organizations and their coherently owned descendants in Explorer cohort reads. ageYears filters by whole completed years; ageMonths remains a legacy compatibility filter. Categories are independent on organizations, activities, and sessions and are never implicitly inherited.
Starter or above; preview requires explorer.browse_masked and retains its fixed disclosed sample.
Reads simplified display outlines from an imported geography snapshot. Supports state, metro, county, city, neighborhood, and ZIP kinds; provide bbox and zoom, q for name/code search, code for one boundary, or point=longitude,latitude for source polygons covering that point. Point is exclusive with bbox/code/q, accepts one finite longitude within ±180 and latitude within ±85, and includes source edges. Point lookup is independent of viewport, simplification, and overlay truncation; overlapping candidates retain stable code ordering and existing feature/byte budgets. Features carry immutable datasetId, kind, code, source, and vintage. City features distinguish incorporated places from Census-designated places. Availability and truncation are explicit; display geometry must never determine precise selected-region membership. Display geometry and source bounds are prepared in the database outside request processing. Point and region membership retain original source polygons. Pending preparation returns HTTP 503 with code=map_cache_warming; callers may retry this state. Successful finished responses may be reused from a bounded per-process memory cache keyed by normalized options, current disclosure policy, representation, and compression; response bodies do not survive a restart. Every request still authenticates and checks access. Source, live eligibility, score, geography, and projection changes invalidate dependent responses; reuse expires by its maximum age and the next UTC midnight. Cache failures fall back to normal reads. Private/no-store and JSON error behavior remain unchanged. X-Map-Revision, when available, is an opaque equality token scoped to relevant source changes, disclosure, policy, and UTC day; clients must not parse it. Successful responses carry a weak ETag bound to normalized options, disclosure, representation, and compression. Send it with If-None-Match to revalidate; after current authorization and a fresh source-fence check, a match returns HTTP 304 with no body and without running the map data query. Retain a streamed ETag only after its complete frame passes sequence and count validation. Revision tokens fence server response reuse and remain available to clients; metadata revision changes do not automatically replace an already loaded Explorer point snapshot. Score writes invalidate requests with minimumScore or maximumScore filters and requests with includeCampScores=true. Migration 0048 indexes boundary provenance lookup without limiting provenance values. Rows whose combined UTF-8 octet length across dataset_id, kind, source, source_url, and vintage is at most 2,000 bytes use a covering btree index on all five keys; larger rows use a complementary partial btree index on dataset_id and kind. MapBoundaries combines those disjoint bounded and overflow paths with UNION ALL, then retains the existing exact distinct source tuples and ordering. The threshold only routes index use; provenance values are never rejected or truncated. Apply prepare migrations 0047 then 0048 before deploying the matching API code.
Starter or above; preview requires explorer.browse_masked.
Pages the complete membership of a map group using the same catalog and selected-region filters. Pass the returned groupId as group, optional limit, and the returned nextCursor as after. Members carry targetKind and id for detail reads. Co-located records remain individually accessible without invented location offsets. Reads the same durable map projection and masking rules as the map. Initial preparation returns HTTP 503 with code=map_cache_warming. Successful finished responses may be reused from a bounded per-process memory cache keyed by normalized options, current disclosure policy, representation, and compression; response bodies do not survive a restart. Every request still authenticates and checks access. Source, live eligibility, score, geography, and projection changes invalidate dependent responses; reuse expires by its maximum age and the next UTC midnight. Cache failures fall back to normal reads. Private/no-store and JSON error behavior remain unchanged. X-Map-Revision, when available, is an opaque equality token scoped to relevant source changes, disclosure, policy, and UTC day; clients must not parse it. Successful responses carry a weak ETag bound to normalized options, disclosure, representation, and compression. Send it with If-None-Match to revalidate; after current authorization and a fresh source-fence check, a match returns HTTP 304 with no body and without running the map data query. Retain a streamed ETag only after its complete frame passes sequence and count validation. Revision tokens fence server response reuse and remain available to clients; metadata revision changes do not automatically replace an already loaded Explorer point snapshot. Score writes invalidate requests with minimumScore or maximumScore filters and requests with includeCampScores=true. Map and member reads exclude identities with committed source work still queued and overlay those identities from current source rows. Projection state and the live overlay share one repeatable-read snapshot; a proven clean snapshot takes the saved-row fast path. collectionId selects staff-curated organizations and their coherently owned descendants in Explorer cohort reads. ageYears filters by whole completed years; ageMonths remains a legacy compatibility filter. Categories are independent on organizations, activities, and sessions and are never implicitly inherited.
Starter or above; preview requires explorer.browse_masked and retains its fixed disclosed sample.
GET /api/canonical/map/stream
Streams the authorized camp-point snapshot once, then compact point upserts and removals over SSE. Requires includeCampPoints=true and the same bbox, zoom, filters, names, scores, tier and preview disclosure as the finite map route. Each generation begins with event:start containing version=1, mode=snapshot or delta, revision, sourceRevision, bounds, columns, and fromRevision for deltas. Columns are id, longitude, latitude, optionally name, then optionally scoreBand. event:points contains zero-based sequence, rows of positional upsert tuples, and removeIds (empty for snapshots). event:complete contains revision, pointCount, batchCount, upsertCount and removeCount, and is the only event carrying id:revision. Clients commit data and the cursor only after validating the complete generation. First-load batches may be displayed provisionally; replacements and deltas retain the completed map until complete. Send the last committed cursor in Last-Event-ID to resume. A matching current cursor returns event:ready with revision and sourceRevision and no point download; retained history produces a delta; missing or evicted history produces a replacement snapshot. The cursor is query/disclosure scoped and opaque. sourceRevision is the separate equality token used by finite map metadata. Database revision and points are read from the same snapshot. The server checks dependency fences and shares point builds only when dependencies change; idle connections do not query or transmit the full point population repeatedly. Snapshot history is bounded API-process memory, not durable replay; restarts or another replica can require a replacement. Connections are bounded so reconnects renew authorization, and heartbeat comments carry no data changes. Pre-stream failures use ordinary HTTP JSON errors; terminal event:error contains code and message, optionally status. Incomplete generations never advance the cursor. HTTP 401/402/403 or access refusals must discard retained protected data. No worker or canonical write is started by a subscription.
Starter or above; preview requires explorer.browse_masked and retains its fixed disclosed sample.
Read quantitative counts, distributions, and missing-data coverage for a selected Explorer region.
Starter and higher data access; authenticated account or permitted machine key. Investor preview is not supported.
POST /api/canonical/query
Runs one read-only SELECT or WITH statement against the masked canonical relations. activities.score uses stored results prepared by workers and refreshed after database changes, with decay and calendar rules refreshed roughly hourly. Pausing workers retains existing results and evaluation times without preventing API reads. Returns retryable 503 while scoring objects are being installed. organization_scores reads the same indexed, persisted score results for complete score columns, including camps without activities. calculated_at identifies each result’s evaluation time; ranking queries never run the score evaluator. Optional location adds location_match and distance_meters to entity views without removing rows: point {type,latitude,longitude,radiusMeters} or US area {type:area,kind:state|zip,code,radiusMeters?}. Address-point calculations use full stored coordinate precision on every plan; source quality still limits geographic accuracy. ZIP intersections are possible_area_match with no venue distance; state_coverage is broad; missing boundaries stay unresolved. Group or filter classifications explicitly. Raw spatial functions and geography tables remain hidden. Response data.location carries the request, available reference versions/sources, and pointCoordinates=source. Historical saved metadata may retain rounded_to_two_decimal_places. Preview queries use fixed materialized samples bounded by the disclosure page size before expressions, joins, and aggregates; policy-masked columns, catalog-only datasets, legacy tables, score relations, and location queries are unavailable. Preview results carry preview.sample=true and resultLimit, and aggregates describe only the sample. Statements stop after 15 seconds; only the service credential without x-viewer-id may raise that to 60 with timeoutSeconds, for unattended background reads.
Account credential or Intelligence session cookie with plan restrictions; machine keys are refused. Free reaches canonical data through internal Ask only. Preview sessions additionally require explorer.query_masked and share the preview search rate limit.
curl -X POST https://apis.schoolsoutapp.com/api/canonical/query -H "Content-Type: application/json" -H "x-api-key: ACCOUNT_CREDENTIAL" -d '{"sql":"select location_match, count(*)::int from activities group by location_match","location":{"type":"point","latitude":47.6062,"longitude":-122.3321,"radiusMeters":5000}}'
GET /api/canonical/reference
Read the live organization, activity, and session structure and application rules. Staff or ADMIN access is required; no record values are returned.
Current staff or ADMIN operator credential; preview sessions are rejected
Describes the viewer-visible canonical columns, including the activities view’s nullable parent-camp score, plus the cached organization_scores view available to account canonical queries. Preview sessions receive only policy-disclosed sample columns and no score relation, with preview.sample and resultLimit metadata. canonical_url is the verified official primary source; relevant_urls is an array of supporting or unverified pages and does not establish an official site. Both fields require Team data access.
This endpoint requires an API key; hidden columns are omitted for the account plan
Searches canonical Organizations, Activities, and Sessions. Ordinary account requests retain kind ordering and exact total; ordinary preview search returns 403 and requires mode=suggestions. Optional mode=suggestions returns one globally ranked bounded page (default 10, limit 1–20), without total: exact names, prefixes, then other name/place matches; ties use folded name, kind, and UUID. Suggestions match names, related names, city, and region; descriptions and notes remain in ordinary search only. q characters %, _, and ! match literally in suggestions. Each suggestion carries target_kind, id, name, parent_id, parent_name, city, region, verified, and nullable organization_id. Optional location contains the linked active nonblacklisted camp's saved latitude, longitude, and coordinate_precision, with full stored precision on every plan and finite map bounds ±85/±180; child coordinates and viewport centroids never supply it. Missing/unavailable camps have no camp ID or location. Preview matching and relationships are restricted to fixed disclosed per-kind samples and field masks.
Starter or above with current catalog access. Preview suggestions preserve the fixed disclosed sample.
Translate a plain-language catalog search into the current viewer's permitted structured filters, or request clarification.
Account or permitted machine credential required
GET /api/collections
GET lists shared staff-managed camp collections; archived=false is the default, archived=true selects archived collections, and archived=all includes both. POST {name} creates one. Active names are unique without regard to case. Responses include memberCount for retained memberships and availableMemberCount for camps that remain nondeleted and nonblacklisted. Collections contain organizations only; use collectionId on organization Explorer reads to browse members. Creator attribution becomes null if the creating account is deleted; collection membership is retained.
Direct current staff or ADMIN account session. Service delegation, machine keys, and preview viewers are rejected. Mutations from a signed Intelligence cookie require a trusted origin and session-bound CSRF token.
GET lists shared staff-managed camp collections; archived=false is the default, archived=true selects archived collections, and archived=all includes both. POST {name} creates one. Active names are unique without regard to case. Responses include memberCount for retained memberships and availableMemberCount for camps that remain nondeleted and nonblacklisted. Collections contain organizations only; use collectionId on organization Explorer reads to browse members. Creator attribution becomes null if the creating account is deleted; collection membership is retained.
Direct current staff or ADMIN account session. Service delegation, machine keys, and preview viewers are rejected. Mutations from a signed Intelligence cookie require a trusted origin and session-bound CSRF token.
GET reads a collection, including archived collections. PATCH {name} renames an active collection or PATCH {archived:true} archives it. Archived collections remain readable and preserve membership, but cannot be renamed, changed, or restored. Active names are unique without regard to case; the response includes retained and currently available camp counts. Creator attribution becomes null if the creating account is deleted; collection membership is retained.
Direct current staff or ADMIN account session. Service delegation, machine keys, and preview viewers are rejected. PATCH from a signed Intelligence cookie requires a trusted origin and session-bound CSRF token.
GET reads a collection, including archived collections. PATCH {name} renames an active collection or PATCH {archived:true} archives it. Archived collections remain readable and preserve membership, but cannot be renamed, changed, or restored. Active names are unique without regard to case; the response includes retained and currently available camp counts. Creator attribution becomes null if the creating account is deleted; collection membership is retained.
Direct current staff or ADMIN account session. Service delegation, machine keys, and preview viewers are rejected. PATCH from a signed Intelligence cookie requires a trusted origin and session-bound CSRF token.
Checks which of 1–100 requested organization UUIDs are available members of this shared collection. The organizationIds parameter is required, nonempty, and contains distinct UUIDs; input is UUID-normalized and deduplicated. The response returns {organizationIds, unavailableOrganizationIds} from one SQL snapshot, with no camp names. organizationIds contains available requested members; unavailableOrganizationIds contains requested IDs that are missing, deleted, or blacklisted without distinguishing why. Only absence from organizationIds for an available ID confirms non-membership; an unavailable ID omitted there cannot confirm removal. Active and archived collections are readable.
Direct current staff or ADMIN account session. Service delegation, machine keys, and preview viewers are rejected.
PUT adds an available, nondeleted, nonblacklisted camp to an active collection. DELETE removes its membership. Membership is idempotent. Archived collections cannot be changed; archived memberships remain readable. A collection can contain organizations only.
Direct current staff or ADMIN account session. Service delegation, machine keys, and preview viewers are rejected. Mutations from a signed Intelligence cookie require a trusted origin and session-bound CSRF token.
curl -X PUT https://apis.schoolsoutapp.com/api/collections/COLLECTION_UUID/organizations/CAMP_UUID -H "x-api-key: YOUR_ACCOUNT_KEY"
PUT /api/collections/{id}/organizations/{organizationId}
PUT adds an available, nondeleted, nonblacklisted camp to an active collection. DELETE removes its membership. Membership is idempotent. Archived collections cannot be changed; archived memberships remain readable. A collection can contain organizations only.
Direct current staff or ADMIN account session. Service delegation, machine keys, and preview viewers are rejected. Mutations from a signed Intelligence cookie require a trusted origin and session-bound CSRF token.
curl -X PUT https://apis.schoolsoutapp.com/api/collections/COLLECTION_UUID/organizations/CAMP_UUID -H "x-api-key: YOUR_ACCOUNT_KEY"
POST /api/hallpass/plans
School’s Out hallpass/plans service.
Account or permitted machine credential required
GET /api/hallpass/runs
School’s Out hallpass/runs service.
Account or permitted machine credential required
POST /api/hallpass/runs
School’s Out hallpass/runs service.
Account or permitted machine credential required
GET /api/hallpass/runs/{runId}
School’s Out hallpass/runs/{runId} service.
Account or permitted machine credential required
GET /api/hallpass/runs/{runId}/activity
School’s Out hallpass/runs/{runId}/activity service.
Account or permitted machine credential required
POST /api/hallpass/runs/{runId}/cancel
School’s Out hallpass/runs/{runId}/cancel service.
Account or permitted machine credential required
POST /api/hallpass/runs/{runId}/decisions/{decisionId}
School’s Out hallpass/runs/{runId}/decisions/{decisionId} service.
Account or permitted machine credential required
GET /api/hallpass/runs/{runId}/screen
School’s Out hallpass/runs/{runId}/screen service.
Account or permitted machine credential required
GET /api/hallpass/runs/{runId}/trace
School’s Out hallpass/runs/{runId}/trace service.
Account or permitted machine credential required
GET /api/hallpass/settings
School’s Out hallpass/settings service.
Account or permitted machine credential required
GET /api/health
Minimal API availability check; operational database, credential, and write status is returned only to the service credential or ADMIN
This endpoint is open to the public
curl https://apis.schoolsoutapp.com/api/health
GET /api/ingestion/codex-accounts
The team's shared pool of Codex (ChatGPT) subscriptions that Trident workers lease, one per worker session. GET returns data.accounts (every subscription that is not removed): id, label, email, plan, status (pending_login, awaiting_approval, active, exhausted, login_failed, login_required, removing), device_url, user_code and code_expires_at while a worker waits for the device login, used_5h_pct, used_week_pct, resets_at (the limiting window's reset), resets_5h_at, resets_week_at, usage_ceiling_pct (1-100 or null), leased_by (instance_id, name) and leased_at, last_used_at, last_error (redacted, at most 300 characters), created_by, created_at and updated_at. POST with a label of 1-80 characters returns 201 with data.account in pending_login; a worker then runs the device login and shows its link and code. Responses never contain tokens, auth.json contents, or filesystem paths.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/codex-accounts
The team's shared pool of Codex (ChatGPT) subscriptions that Trident workers lease, one per worker session. GET returns data.accounts (every subscription that is not removed): id, label, email, plan, status (pending_login, awaiting_approval, active, exhausted, login_failed, login_required, removing), device_url, user_code and code_expires_at while a worker waits for the device login, used_5h_pct, used_week_pct, resets_at (the limiting window's reset), resets_5h_at, resets_week_at, usage_ceiling_pct (1-100 or null), leased_by (instance_id, name) and leased_at, last_used_at, last_error (redacted, at most 300 characters), created_by, created_at and updated_at. POST with a label of 1-80 characters returns 201 with data.account in pending_login; a worker then runs the device login and shows its link and code. Responses never contain tokens, auth.json contents, or filesystem paths.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/codex-accounts/{id}/remove
POST marks a shared Codex subscription removing and returns 202 with data.account; the worker holding it (or any worker when none holds it) stops using it, deletes the server's login directory, and marks it removed (removed subscriptions leave the list). Removal never signs the teammate out: no logout or token revocation runs. Removing one already being removed returns it unchanged; a removed or unknown subscription returns 404.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/codex-accounts/{id}/usage-ceiling
POST {"usage_ceiling_pct": 1-100 or null} sets or clears the subscription's usage ceiling and returns 200 with data.account. The ceiling applies to either window's total use, including the owner's own: at or above it workers stop leasing the subscription (running calls finish; the worker switches subscriptions or pauses on Codex), and it keeps its login. It returns after a fresh reading below the ceiling once its window resets, or at once when the ceiling is raised or cleared. Allowed in any status before removal. 400 for an invalid value; 404 for a removed or unknown subscription.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/crawler-campaigns
List or create durable mass-crawl jobs. Creation accepts location, nearby_locations, providers, confidence_threshold, follow_aggregators and a stable idempotency_key. Retries retain the original creation identity after settings edits. The complete search plan is scoped to explicit localities, including neighborhood searches for Portland, Oregon.
Current staff or ADMIN operator; standard CSRF for cookie mutations.
POST /api/ingestion/crawler-campaigns
List or create durable mass-crawl jobs. Creation accepts location, nearby_locations, providers, confidence_threshold, follow_aggregators and a stable idempotency_key. Retries retain the original creation identity after settings edits. The complete search plan is scoped to explicit localities, including neighborhood searches for Portland, Oregon.
Current staff or ADMIN operator; standard CSRF for cookie mutations.
POST /api/ingestion/crawler-campaigns/search-plan
Generate the full deduplicated term list and query/provider count without creating a job or contacting search providers. The response contains data.search_plan with terms, providers and search_count. Cookie requests require CSRF.
Current staff or ADMIN operator; standard CSRF for cookie POST requests.
GET /api/ingestion/crawler-campaigns/{id}
Read current-generation search, crawl, resolved Trident classification and unresolved counts. Successful historical crawls are reused. No request owns worker lifetime.
Current staff or ADMIN operator; standard CSRF for cookie mutations.
POST /api/ingestion/crawler-campaigns/{id}/control
Pause, resume, or rerun. Rerun requires a paused/completed campaign and expected_generation, making uncertain retries idempotent. It retains every saved URL and discovery path.
Current staff or ADMIN operator; standard CSRF for cookie mutations.
GET /api/ingestion/crawler-campaigns/{id}/discoveries
Read up to 100 discovery edges after an optional decimal after ID. Without url_id, results cover the selected campaign across its runs. With url_id, incoming edges cover every mass-crawl job that recorded the shared URL. campaign_id, campaign_location, generation and parent_url_id retain the path provenance.
Current staff or ADMIN operator; standard CSRF for cookie mutations.
GET /api/ingestion/crawler-campaigns/{id}/homepages
Read deduplicated first-party camp homepages at the saved confidence threshold. Each includes classification details, assessment and source_url_id/source_url/historical_source for evidence and discovery navigation. Returns data.homepages.
Current staff or ADMIN operator; standard CSRF for cookie POST requests.
GET /api/ingestion/crawler-campaigns/{id}/performance
School’s Out ingestion/crawler-campaigns/{id}/performance service.
Account or permitted machine credential required
GET /api/ingestion/crawler-campaigns/{id}/search-plan
Read all enabled current-generation terms, including completed searches, without truncation or regeneration. Returns data.search_plan with terms, providers, search_count and settings_revision from one database snapshot.
Current staff or ADMIN operator; standard CSRF for cookie POST requests.
GET /api/ingestion/crawler-campaigns/{id}/searches
Read up to 100 term/provider states after an optional decimal after ID, with attempts, pages fetched and completion reasons. Blocks remain retryable instead of exhausted.
Current staff or ADMIN operator; standard CSRF for cookie mutations.
POST /api/ingestion/crawler-campaigns/{id}/settings
Apply complete live settings with revision conflict protection. Completed matching searches and saved URL evidence are retained. Paused jobs stay paused; changing the search plan can reopen completed jobs. Returns data.campaign; stale revisions return 409.
Current staff or ADMIN operator; standard CSRF for cookie POST requests.
GET /api/ingestion/crawler-campaigns/{id}/settings-history
School’s Out ingestion/crawler-campaigns/{id}/settings-history service.
Account or permitted machine credential required
GET /api/ingestion/crawler-campaigns/{id}/urls
Read up to 100 saved URLs after an optional decimal after ID, with crawl status, Trident assessment, and historical source metadata.
Current staff or ADMIN operator; standard CSRF for cookie mutations.
GET /api/ingestion/crawler-campaigns/{id}/urls/{url_id}
School’s Out ingestion/crawler-campaigns/{id}/urls/{url_id} service.
Account or permitted machine credential required
GET /api/ingestion/crawler-jobs
GET lists crawler jobs with search/status filters and pagination. POST creates an independent job for every submission, including duplicates. Separate provider/settings masks select search, CAPTCHA, proxy and JEV behavior. Ordinary search jobs use contract version 2. mode=organization_website requires organization.name, derives an omitted search_term, and enables activity_filter. Organization mode and search with activity_filter=true use contract 4. acquisition_budget members are positive integer caps or null for no caller cap; omitted query and candidate caps default to null, while pages, pages per site, and model requests default to 30, 4, and 10. Historical contract 3 jobs retain positive integer budgets. Aggregators cannot qualify; identity and activity outcomes retain evidence and unresolved states. Gated jobs reject settings bit 4 and jev_config. max_urls_per_site is a positive integer up to 2147483647 (default 100), counting unique accepted destinations per source registrable domain across pages and sitemaps. Settings bit 8 permits external domains; otherwise the same domain and its subdomains remain eligible. Historical version 1 jobs retain null max_urls_per_site and legacy discovery behavior. Omitted depth is 3; unlimited requires explicit null and allow_unlimited=true. JEV configuration versions are immutable. Job counts always describe the latest crawl. Failed, blocked or cancelled jobs with an empty latest crawl may include saved_results {attempt_id, attempt_number, counts} for a prior crawl, ranked by retained rows, crawled rows and latest attempt number. Counts come from saved rows, independently of JEV sessions.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/crawler-jobs
GET lists crawler jobs with search/status filters and pagination. POST creates an independent job for every submission, including duplicates. Separate provider/settings masks select search, CAPTCHA, proxy and JEV behavior. Ordinary search jobs use contract version 2. mode=organization_website requires organization.name, derives an omitted search_term, and enables activity_filter. Organization mode and search with activity_filter=true use contract 4. acquisition_budget members are positive integer caps or null for no caller cap; omitted query and candidate caps default to null, while pages, pages per site, and model requests default to 30, 4, and 10. Historical contract 3 jobs retain positive integer budgets. Aggregators cannot qualify; identity and activity outcomes retain evidence and unresolved states. Gated jobs reject settings bit 4 and jev_config. max_urls_per_site is a positive integer up to 2147483647 (default 100), counting unique accepted destinations per source registrable domain across pages and sitemaps. Settings bit 8 permits external domains; otherwise the same domain and its subdomains remain eligible. Historical version 1 jobs retain null max_urls_per_site and legacy discovery behavior. Omitted depth is 3; unlimited requires explicit null and allow_unlimited=true. JEV configuration versions are immutable. Job counts always describe the latest crawl. Failed, blocked or cancelled jobs with an empty latest crawl may include saved_results {attempt_id, attempt_number, counts} for a prior crawl, ranked by retained rows, crawled rows and latest attempt number. Counts come from saved rows, independently of JEV sessions.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/crawler-jobs/stream
GET opens request-based server-sent events for the filtered job list. Each connection starts with event:snapshot containing the raw list data projection; changed snapshots and heartbeat comments follow. Reconnect receives a fresh complete snapshot. The connection never owns crawler execution.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
GET /api/ingestion/crawler-jobs/{id}
GET returns job settings and counts, bounded worker attempt/result timelines, meaningful events with explicit totals, optional latest_progress for the current attempt, and the most recent 25 JEV evaluation sessions with history pagination metadata. Exact progress and crawl_progress events are omitted from event history without removing unknown kinds; raw job.latest_event compatibility is unchanged. Preview results omit discovery_path; retrieve saved ancestry from the paged results endpoint. No page bodies, credentials, or local worker artifact paths are exposed. Job counts always describe the latest crawl. Failed, blocked or cancelled jobs with an empty latest crawl may include saved_results {attempt_id, attempt_number, counts} for a prior crawl, ranked by retained rows, crawled rows and latest attempt number. Counts come from saved rows, independently of JEV sessions. Gated details include result.site_assessment and job.site_resolution with evidence, provenance, and bounded acquisition usage.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
POST /api/ingestion/crawler-jobs/{id}/cancel
POST stops a queued or running crawl and returns data.job. Atomically marks the job, active crawl attempt and original JEV session cancelled with reason Stopped by user. Keeps saved results, judgments and history; revokes publication and retry rights. An already terminal job is returned unchanged, making retries safe. Separate re-JEV sessions are unaffected. Unknown jobs return 404.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/crawler-jobs/{id}/evaluations
GET pages saved evaluation history using page/pageSize; detail snapshots include only the most recent 25 sessions with explicit total/truncation metadata. POST queues a separate JEV session for a terminal saved crawl. Freezes saved crawled URL candidates and the selected baseline; omitted model/settings inherit the original configuration. Encoded candidate URLs are limited to 24 MiB per session, checked before queueing. It never searches, fetches pages, recovers pruned branches, or overwrites historical sessions. Organization/activity-gated jobs reject URL-only JEV operations with 400.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/crawler-jobs/{id}/evaluations
GET pages saved evaluation history using page/pageSize; detail snapshots include only the most recent 25 sessions with explicit total/truncation metadata. POST queues a separate JEV session for a terminal saved crawl. Freezes saved crawled URL candidates and the selected baseline; omitted model/settings inherit the original configuration. Encoded candidate URLs are limited to 24 MiB per session, checked before queueing. It never searches, fetches pages, recovers pruned branches, or overwrites historical sessions. Organization/activity-gated jobs reject URL-only JEV operations with 400.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/crawler-jobs/{id}/evaluations/{evaluationId}
GET returns a JEV session and added, removed, unchanged, uncertain, pending and output URL sets. Optional baseline selects another finalized evaluation from the same job. Session counts include added and removed against the session's saved baseline. Organization/activity-gated jobs reject URL-only JEV operations with 400.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
POST /api/ingestion/crawler-jobs/{id}/evaluations/{evaluationId}/cancel
School’s Out ingestion/crawler-jobs/{id}/evaluations/{evaluationId}/cancel service.
Account or permitted machine credential required
GET /api/ingestion/crawler-jobs/{id}/export
GET exports distinct sorted included final URLs as JSONL. Optional attempt selects an owned crawl attempt; optional evaluation selects a saved JEV session. These selectors are mutually exclusive. Omission selects the latest crawl attempt. Explicit scope appears in the filename. Optional confidence_threshold selects the same membership as the matching results read. Export never reruns crawling or JEV. Gated exports contain eligible site roots only; evaluation and confidence_threshold are rejected with 400.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
GET /api/ingestion/crawler-jobs/{id}/results
GET pages saved URL results and provenance using page/pageSize, optionally filtered by excluded=true or false. Optional attempt selects an owned non-evaluation crawl attempt; omission selects the latest crawl attempt. Returns attempt identity and unfiltered counts alongside filtered total from the same read snapshot. Saved excluded pages remain available for reevaluation. Optional discovery_path contains one bounded provider-to-result ancestry with ordered URL steps, explicit complete/partial status, and a reason for missing ancestry; an absent path means it was not saved. No full-crawl graph or worker filesystem is needed. Optional search matches request or final URLs. Optional evaluation (exclusive with attempt and excluded) instead pages one JEV session's canonical URLs: its candidates plus baseline URLs outside them, with that session's verdicts, saved crawl facts, included and baseline_included. Optional baseline selects a finished comparison session; membership is included, excluded, changed, or all. Returns evaluation, baseline_evaluation_id, and unfiltered counts (total, included, excluded, changed, added, removed, errors, pending, uncertain). Optional confidence_threshold (0-1) re-applies a minimum yes probability to saved decisions: membership, total, counts and row excluded/included move, saved verdicts do not; evaluation mode re-thresholds only that session's judgments, never its baseline, for any session including re-JEV of a crawl without JEV filtering; attempt mode requires a JEV-filtered job. Gated jobs reject evaluation and confidence_threshold with 400. Gated results contain one candidate site root per row with site_assessment evidence; site_resolution describes the selected attempt.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
GET /api/ingestion/crawler-jobs/{id}/stream
GET streams event:snapshot updates with the raw job detail projection. Failed reads produce a sanitized event:error and close the connection; clients retain the prior snapshot during reconnect.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
POST /api/ingestion/crawler-jobs/{id}/trident-evaluations
School’s Out ingestion/crawler-jobs/{id}/trident-evaluations service.
Account or permitted machine credential required
GET /api/ingestion/geocoding/coverage
GET: Counts distinct configurations separately from affected entity references, ZIP coverage, precision, missing coordinates/boundaries, pending/unresolved work, dataset availability, credentials, and worker heartbeat. No provider call. Coverage calculation timeouts return 503 with a retryable message; failed calculations have a retry cooldown.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/geocoding/jobs
POST: Recomputes candidate eligibility and pricing transactionally. The fingerprint must match a fresh preview. An idempotency key returns the same job on a repeated confirmation. Freezes candidates and lookup budget; later records enter another job. rerunOf creates a new historical job. Cancellation and uncertain billing do not authorize replacement submissions. GET: Paginated job history. Jobs retain their original scope, mode, quote, requester, outcomes, and timestamps.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/geocoding/jobs
POST: Recomputes candidate eligibility and pricing transactionally. The fingerprint must match a fresh preview. An idempotency key returns the same job on a repeated confirmation. Freezes candidates and lookup budget; later records enter another job. rerunOf creates a new historical job. Cancellation and uncertain billing do not authorize replacement submissions. GET: Paginated job history. Jobs retain their original scope, mode, quote, requester, outcomes, and timestamps.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/geocoding/jobs/{id}
GET: Returns current progress and retained quote/outcomes. Blocked or uncertain provider work is visible.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/geocoding/jobs/{id}/cancel
POST: Stops unsent work. Requests already sent retain their outcomes and may settle after cancellation. Does not refund or resend a provider request. Rerun through a new preview and confirmation.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/geocoding/preview
POST: Read-only quote with no Geocodio calls. Shows affected records/configurations, unique/reusable addresses, exclusions, required lookups, and estimated micro-USD cost before shared provider free credits. Excludes explicit non-US and conflicting state/ZIP evidence.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/geocoding/regions
GET: Lists supported US states and, when state is supplied, recorded city/ZIP labels and their counts. City labels do not represent polygons.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/geocoding/status
GET: Fresh worker heartbeat, credential configuration, and reference availability without waiting for coverage counts. No catalog scan or provider call.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
GET /api/ingestion/scraper-jobs
GET lists paginated scraper jobs filtered by installation, website substring, exact normalized websiteOrigin or websiteTarget, kind, status (queued, running, paused, completed or failed), exact failure code (error, such as training_mismatch), or creation date. websiteTarget accepts one absolute credential-free HTTP(S) URL up to 8192 decoded bytes and matches exact normalized path/query scope across installations with root slash aliases; data.websiteTarget echoes the applied normalized target for capability detection. Count and page share a read snapshot ordered by descending creation time then job ID. websiteOrigin matches scheme and host across page paths and queries; scheme, subdomains, www, and nondefault ports stay distinct. All filters combine. POST queues an onboarding or refresh job for an installation, stamps the current job contract version, and requires an idempotency key. A repeated identical request returns its original job; a changed request with the same key conflicts.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/scraper-jobs
GET lists paginated scraper jobs filtered by installation, website substring, exact normalized websiteOrigin or websiteTarget, kind, status (queued, running, paused, completed or failed), exact failure code (error, such as training_mismatch), or creation date. websiteTarget accepts one absolute credential-free HTTP(S) URL up to 8192 decoded bytes and matches exact normalized path/query scope across installations with root slash aliases; data.websiteTarget echoes the applied normalized target for capability detection. Count and page share a read snapshot ordered by descending creation time then job ID. websiteOrigin matches scheme and host across page paths and queries; scheme, subdomains, www, and nondefault ports stay distinct. All filters combine. POST queues an onboarding or refresh job for an installation, stamps the current job contract version, and requires an idempotency key. A repeated identical request returns its original job; a changed request with the same key conflicts.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/scraper-jobs/website-status
GET returns data.websites for 1–50 repeated websiteUrl values (at most 8192 decoded bytes each), with nullable latest, completed and active job summaries for every unique normalized target, including no-history targets. Queue normalization preserves page/query scope; root slash/no-slash forms share status. Returned job.website stays exact for refresh artifact ownership. Active prefers running over paused or queued; other selections use newest creation time then job ID. Invalid or over-limit batches return 400 without truncation. This read never submits scraper work.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
GET /api/ingestion/scraper-jobs/{id}
Reads one scraper job with attempt and event timelines, including each event's page, model call, failure detail, and counters when the worker reported them. Linked website-publication batches report their downstream pipeline state separately from scrape completion.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
GET /api/ingestion/scraper-jobs/{id}/artifacts
Lists a scraper job's compact diagnostic artifacts (onboarding plan, page decisions, record selections, extractor turns, held records, schedule and classification decisions, adapter and final result) with sizes and omission reasons, without bodies.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
GET /api/ingestion/scraper-jobs/{id}/artifacts/{artifactId}
Reads one diagnostic artifact of a scraper job with its JSON body. Omitted artifacts have no body. Artifacts never contain captured page bodies, the annotated DOM, or credentials.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
POST /api/ingestion/scraper-jobs/{id}/retry
POST queues a failed job again as a new job (201, data.job) with the same installation, website, kind, settings and attempt limit, at the current job contract version. A failed job has at most one retry: a repeated request returns that earlier retry with 200, and nothing new is queued. A job that has not failed returns 409; an unknown job returns 404.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/ingestion/scrapers
Lists scraper installations, their most recent process session, current work, connection status, and provider_pauses: each model provider the installation is paused on, with reason, paused_at, and the latest automatic health probe (probed_at, probe_error, probes). A paused installation claims no jobs. current_jobs lists every running job of the installation, oldest first (an empty array when idle); current_job is the first of them. codex_account is the shared Codex subscription (id, label, email) the installation's live session leases now, or null. build is the latest session's revision with the main_revision, behind_main and checked_at its server last reported (null until reported; behind_main is null when the server could not count it for the running build). update is the newest dashboard update request (id, action, status, target_revision, from_revision, to_revision, message, log_tail, requested_by, created_at, updated_at, finished_at), or null; a request running for over 15 minutes without a result is shown as failed with message no result. The job contract version is separate from build and database migration versions.
Current staff or ADMIN account; signed Intelligence cookie or account credential.
POST /api/ingestion/scrapers/{id}/rollback
POST asks the installation to put its previous build back and returns 202 with data.request (pending, target_revision null); it runs like an update. 409 when the installation already has an active request; 404 for an unknown installation.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
POST /api/ingestion/scrapers/{id}/update
POST asks the installation to update to the main revision its server last reported (build.main_revision) and returns 202 with data.request (pending). The installation's own worker drains its jobs (at most about 10 minutes, otherwise the request is refused as busy) and hands the request to the server's root-owned update script, which re-validates it, checks the new build's database migrations, installs it, and rolls back on failure. The request reports status (pending, draining, running, succeeded, rolled_back, failed, refused), from_revision, to_revision, and a redacted message and log_tail. 409 when the installation already has an active request or has not reported main's revision; 404 for an unknown installation.
Current staff or ADMIN account; signed Intelligence cookie or account credential. Cookie POST requests require CSRF.
GET /api/insights
The insights catalog: every research dataset your plan can read, with tier-filtered union capabilities at the top and column-aware capabilities on each dataset
This endpoint requires an API key (free tier and up); premium datasets are listed from the pro plan
Relevance-ranked, five-second search for up to five terms across visible name, provider, category, description, city, county, state, and country fields, with literal wildcard characters, exact filters, pagination, and actionable busy responses
This endpoint requires an API key (free tier and up)
Rows from one insights dataset with strict exact filters, up to five name searches in one request, sorting, pagination, and one-page CSV output; unknown or malformed parameters return 400
This endpoint requires an API key (free tier and up) — rows are masked to your plan, and free accounts see the first result
Lists canonical organizations with search, location, independent organization category, whole-year age, grade, inclusive score range, sort, and pagination filters. minimumScore and maximumScore are inclusive, orderBy=score sorts using the cached SQL score view, total reflects score filtering, and pagination happens in SQL. Ordinary lists retain organizations awaiting score preparation. List reads do not load score summaries or calculate scores. Returns retryable 503 while score objects are being installed. Responds with tier, resultLimit, and lockedColumns; format=csv downloads a metered export (x-export-remaining) without score columns. Unfiltered total and unfilteredTotal use durable incrementally maintained totals after relevant background work settles and fall back to live counts while committed changes are pending. Filtered totals and returned rows remain live. canonical_url is the verified official primary source; relevant_urls is an array of supporting or unverified pages and does not establish an official site. Both fields require Team data access. ageYears filters by whole completed years; ageMonths remains a legacy compatibility filter; whole-year bounds adapt to inclusive month bounds as [yearMin*12,yearMax*12+11]. collectionId filters this direct-account staff-only organization cohort by shared camp collection; categories are read and filtered from the organization itself, without activity aggregation. Each ordinary JSON row includes image_urls, a deduplicated array of its own active image URLs, ordered by selected cover, selected logo where applicable, then gallery position. Empty galleries return an empty array. Remote URLs are preserved verbatim; uploaded images retain protected organization brand-asset URLs. Documents, deleted or misowned uploads, inactive memberships, other entities’ media, and source provenance are excluded. Investor previews omit image_urls, CSV columns remain unchanged, and includeRows=false skips media reads. Normal JSON records omit bookkeeping and ingestion history, null and blank record fields, and unknown availability, registration-status, and coordinate-precision values. Zero, false, empty arrays, masked values, positional range bounds, and structured schedule states retain their meanings. Related entity summaries follow the same projection; CSV and SQL retain their existing contracts. Bookkeeping and provenance are available separately at /api/organizations/{id}/provenance. Age ranges use age_range_years; normal JSON records and column metadata omit the redundant age_range_months alias. CSV, SQL, and the legacy ageMonths filter retain month compatibility. Records and column metadata omit score. Fetch /api/organizations/{id}/score for live detail or /api/organizations/scores?ids=UUID,UUID&cached=true for persisted summaries; ordinary list/detail reads do not load scores.
Any API key from the Starter plan up. Columns are masked to the plan (contacts, social links, canonical_url, relevant_urls, exact address, and postal code from Team); accessible coordinates retain full stored precision on every plan; Free gets 402
Returns canonical organization city, region, and independent organization category facets without provenance or raw ingestion data. With ?field= it returns one facet scoped to the list's filters; field=updated_day additionally counts organizations by the day of their latest update over the last 30 days. Optional snapshot=true with a single field serves incrementally maintained chart counts when no list filters or search are active; filtered requests and the default remain live. Pending source changes or due calendar updates fall back to live counts, so committed edits, deletion, and blacklisting remain visible before background refresh completes. Authentication and field access remain live. ageYears filters by whole completed years; ageMonths remains a legacy compatibility filter; whole-year bounds adapt to inclusive month bounds as [yearMin*12,yearMax*12+11]. collectionId filters this direct-account staff-only organization cohort by shared camp collection; categories are read and filtered from the organization itself, without activity aggregation.
Any API key from the Starter plan up; Free gets 402
Read live or cached score summaries for a batch of canonical organizations.
Account cookie or header, or permitted machine key, from Starter up; Free gets 402. Free Ask scoring is internal to Go and cannot be enabled through a request header.
Returns one tier-masked canonical organization and its Activity summaries without loading or calculating scores. Deprecated includeScore=false is accepted as a no-op; includeScore=true returns 400 directing callers to /api/organizations/{id}/score. Repeated or malformed includeScore values return 400. canonical_url is the verified official primary source; relevant_urls is an array of supporting or unverified pages and does not establish an official site. Both fields require Team data access. Normal JSON records omit bookkeeping and ingestion history, null and blank record fields, and unknown availability, registration-status, and coordinate-precision values. Zero, false, empty arrays, masked values, positional range bounds, and structured schedule states retain their meanings. Related entity summaries follow the same projection; CSV and SQL retain their existing contracts. Moved fields are configuration_id, external_ids, created_at, updated_at, source_created_at, field_provenance, source_records, merge_jobs, and relationship_jobs. Read permitted bookkeeping and provenance at /api/organizations/{id}/provenance. Age ranges use age_range_years; normal JSON records and column metadata omit the redundant age_range_months alias. CSV, SQL, and the legacy ageMonths filter retain month compatibility. Records and column metadata omit score. Fetch /api/organizations/{id}/score for live detail or /api/organizations/scores?ids=UUID,UUID&cached=true for persisted summaries; ordinary list/detail reads do not load scores. Normal detail responses include data.media, the first gallery page of up to 100 images, with entity, items, coverId, logoId, total, limit, and offset. Images include displayUrl, label, kind, width, and height; source attribution is omitted even for ADMIN. Previews omit media. Optional includeMedia=false skips the gallery; omitted or true includes it. The parameter accepts one exact true or false value, with malformed or repeated values returning 400. Use the existing /media endpoint for additional pages or authorized source attribution.
Account cookie or header, or permitted machine key, from Starter up. Active organization members receive their existing organization detail access.
Returns one organization's paginated media gallery, selected cover and logo IDs, image dimensions when available, and permitted source attribution. limit defaults to 30 and caps at 100; offset defaults to 0 and caps at 1000000. Uploaded images keep their existing protected brand-asset URLs. Media belongs only to the requested organization. Optional includeProvenance=false skips source attribution and its database read. Omitted or true preserves existing ADMIN source access; this flag never grants authority. Accepts one exact true or false value; malformed or repeated values return 400. Gallery-only pagination can use false; one image’s source disclosure can use true with limit=1 and that image’s offset.
Account cookie or header, or permitted machine key, from Starter up; active organization members receive the existing organization detail access. Previews cannot read media. Source provenance is limited to non-preview ADMIN account viewers.
Returns data.organization with id and visible bookkeeping moved from normal reads. Only authorized ADMIN readers additionally receive field_provenance, source_records, merge_jobs, and relationship_jobs. Moved fields are configuration_id, external_ids, created_at, updated_at, source_created_at, field_provenance, source_records, merge_jobs, and relationship_jobs. Field provenance separates current value ownership (state, sources, value_state) from source checks and field decisions. Entries cover visible fields before sparse projection, including cleared and unknown values. A found or checked_not_found observation can exist without a value change; absence of observation means no recorded check. Sources, latest_observation, and history share a flat record shape; history is newest first. The latest observation follows observed time, while decision history follows contribution time. Omitted field_provenance means unavailable; an absent entry in an available map is unknown. Field citations are distinct from site-scoped scrape URLs.
Account cookie or header, or permitted machine key, from Starter up. Active organization members receive their existing organization detail access. Bookkeeping follows the existing field masks. Field provenance and raw histories require an ADMIN user-backed viewer, including an ADMIN-owned machine key; staff, bare service credentials, and membership upgrades do not grant this authority. All previews are denied.
Read recorded camp, program, and session contributions, with reconstructed organization scores for ADMIN accounts.
Account or permitted machine credential required
GET /api/organizations/{id}/score
Read an organization's score, evidence, and peer standing.
Account cookie or header, or permitted machine key, from Starter up; Free gets 402. Evidence fields and the organization are masked to the plan; raw organization provenance is ADMIN-only.
GET returns the signed-in organization edit form with link:null. POST { organization?, clearFields?: string[], reason?, sourceURL?, removeRelevantUrls?: string[], brand?, assets?, submitter? } saves an attributed, reversible operator contribution using the current account. Omitted and blank/null organization fields are ignored; clearFields explicitly clears editable fields, including all relevant_urls; required fields such as name cannot be cleared. Use removeRelevantUrls to remove selected supporting links. A clear or replacement of a populated, different value requires reason; fills and same-value confirmations do not. sourceURL is an optional HTTP(S) field citation. Human edits outrank automated contributions, including human-approved Overseer proposals; source freshness applies within the same authority. organization.relevant_urls adds supporting links. removeRelevantUrls explicitly removes matching saved supporting links, including the last link, while preserving unspecified and concurrently added URLs; removal wins over a simultaneous addition of the same normalized URL. Both lists use HTTP(S) form normalization. canonical_url remains the separate official website. Duplicate additions and absent removals record no new ownership. A removal reversal restores only its originally untracked links or links with still-valid supporting contributions; later removals remain authoritative. Public update tokens do not accept removeRelevantUrls.
An ADMIN or staff actor, or an active account membership of this organization
GET /api/overseer
Reads or replaces the shared Overseer instructions and tool controls. Structured saves supply directives and optionally disabledTools with expectedRevision. Each edit atomically creates a revision, fences previous execution, and cancels its undelivered one-off messages; committed child jobs remain. Empty or all-disabled instructions idle the worker unless a one-off message, requested correction, or system revalidation is pending. Global pause still prevents execution. Legacy directive text is supported: saving its unchanged projection preserves structured instructions, while changed text replaces them with one enabled item; omitted disabledTools preserves restrictions. Stale expectedRevision returns 409 and the current snapshot. executionActive reports a current unexpired worker lease, not worker health. Native thread IDs, checkpoints and execution tokens remain private. latestSlackDelivery is a safe durable observation, not a claim about current channel configuration. paused and pauseVersion expose a global control independent of instruction revision. Configuration saves never implicitly resume paused execution.
Current user-backed staff or ADMIN identity. Account credentials and machine keys belonging to eligible users are accepted; a bare service credential is not. Intelligence uses its signed session cookie; cookie PUT and POST requests require a trusted origin and session-bound CSRF token.
PUT /api/overseer
Reads or replaces the shared Overseer instructions and tool controls. Structured saves supply directives and optionally disabledTools with expectedRevision. Each edit atomically creates a revision, fences previous execution, and cancels its undelivered one-off messages; committed child jobs remain. Empty or all-disabled instructions idle the worker unless a one-off message, requested correction, or system revalidation is pending. Global pause still prevents execution. Legacy directive text is supported: saving its unchanged projection preserves structured instructions, while changed text replaces them with one enabled item; omitted disabledTools preserves restrictions. Stale expectedRevision returns 409 and the current snapshot. executionActive reports a current unexpired worker lease, not worker health. Native thread IDs, checkpoints and execution tokens remain private. latestSlackDelivery is a safe durable observation, not a claim about current channel configuration. paused and pauseVersion expose a global control independent of instruction revision. Configuration saves never implicitly resume paused execution.
Current user-backed staff or ADMIN identity. Account credentials and machine keys belonging to eligible users are accepted; a bare service credential is not. Intelligence uses its signed session cookie; cookie PUT and POST requests require a trusted origin and session-bound CSRF token.
POST /api/overseer/clear-memory
Clears the current execution memory while retaining the exact saved directive and report cadence. It preserves the directive revision, revokes the current run, and emits a memory_cleared event when a saved revision exists. The next execution starts with a fresh native thread and no prior checkpoint or narrative. Historical jobs, action receipts, audit events, and minimal action references remain for provenance and to avoid duplicate actions. A blank or absent directive is allowed; requested corrections and system revalidation remain actionable. Review history and suppression are preserved. A stale expectedRevision returns 409 with the current public snapshot. Thread IDs, checkpoints, and execution authority remain private. Structured instruction enablement and tool restrictions remain unchanged. Undelivered one-off requests are cancelled with message_cancelled events. The global pause state and its version are preserved.
Current user-backed staff or ADMIN identity. Account credentials and machine keys belonging to eligible users are accepted; a bare service credential is not. Intelligence uses its signed session cookie; cookie PUT and POST requests require a trusted origin and session-bound CSRF token.
GET /api/overseer/events
Reads bounded Overseer history. Default order=asc uses the exclusive afterId cursor and returns nextAfterId. order=desc uses exclusive beforeId and returns nextBeforeId; omit the cursor for the newest page. Both return events in the requested order and hasMore, preserve an empty page cursor, accept optional revision (0 means all), and limit 1 to 200. Wrong-direction, mixed, duplicate, unsupported and invalid query parameters return 400. Only directive_saved events include a top-level directive loaded from that durable revision, including empty text for idle; other events omit it. Organization links in details.links include a display label resolved from the current camp name, including historical events. Unnamed or unavailable camps receive an identifying fallback containing the preserved camp ID. Labels do not change stored audit records or action receipts. Internal subagent telemetry is excluded before pagination; its aggregate appears only in the current snapshot. Completed assistant messages use kind=agent_output with details.text, source (coordinator or subagent), phase (commentary or final_answer), optional ordinal agentLabel, and truncated. Text preserves line breaks after credential redaction and byte bounds. Coordinator final control documents, reasoning, tool payloads and native identities are excluded. These observations do not replace verified progress or directive state; existing history pagination applies. Staff one-off requests appear as user_message with requestId and text; message_delivered and message_cancelled events identify their requestId. Delivery acknowledges a successful receiving turn, not completed requested work. pause_changed records explicit global control changes with paused and pauseVersion; the instruction revision is unchanged.
Current user-backed staff or ADMIN identity. Account credentials and machine keys belonging to eligible users are accepted; a bare service credential is not. Intelligence uses its signed session cookie; cookie PUT requests require a trusted origin and session-bound CSRF token.
GET /api/overseer/evidence/{id}
Returns original captured pixels while retained. The global newest 10000 Overseer images are kept; metadata survives removal. Removed images return HTTP 410 and are never recreated by replay.
Current user-backed staff or ADMIN operator; account credentials or eligible machine keys, or the signed Intelligence cookie. Cookie decisions require a trusted origin and session-bound CSRF token. Bare service credentials and preview viewers are rejected.
GET /api/overseer/jobs
Reads current crawler/scraper queue status for explicit durable Overseer action links across revisions. Jobs are deduplicated by kind and ID and attributed to their first linked action revision. Newest first uses an opaque exclusive before cursor and returns jobs, nextBefore and hasMore; limit is 1 to 200, and optional revision selects the originating revision. Later references do not move a job or change its origin. label is the crawl search term or scraper website. campId is nonnull only for one explicit organization link on the originating action that resolves to a live camp; no website/name match or inferred followup establishes a relationship. Missing jobs and other link kinds are omitted. Raw action inputs, results, evidence and execution authority are never returned.
Current user-backed staff or ADMIN identity. Account credentials and machine keys belonging to eligible users are accepted; a bare service credential is not. Intelligence uses its signed session cookie; cookie PUT requests require a trusted origin and session-bound CSRF token.
POST /api/overseer/messages
Queues a staff-authored request for the next worker turn without editing standing instructions or interrupting the current turn. Supply a canonical UUID requestId; retry identical requestId, expectedRevision and text after an uncertain result to retrieve its durable receipt, even after the active revision changes. Reusing an ID for different input returns 409. New stale requests return 409 with the current snapshot. The first request may initialize an empty revision when expectedRevision is zero. The current configuration editor remains the execution authority; the submitting operator is retained as message attribution. A maximum of 100 requests may be pending. Requests are delivered only after a valid successful agent turn; failed attempts retain them for recovery using committed action history. Requests arriving during a turn remain queued for the next turn. Replacing the configuration or clearing memory cancels outstanding requests visibly. Events user_message, message_delivered and message_cancelled identify their requestId; user_message also contains text. Delivered is transport acknowledgment, not proof of requested work completion. The saved owner is reauthorized before inference and tools; a message never takes over execution ownership. Revoking a sender after acceptance does not retroactively cancel the request. Unfinished delivered work can continue through the same revision checkpoint and native thread. A new stale submission includes top-level outcome=rejected and messageRejection={requestId,expectedRevision}; because historical receipt lookup preceded the monotonic revision check under one lock, the exact request cannot later be accepted. An uncertain client may release its retry only for a matching marker or a durable receipt. A generic 409 does not prove delivery outcome. Requests may be queued while paused; enqueue preserves the pause state and they wait for an explicit resume.
Current user-backed staff or ADMIN identity. Account credentials and machine keys belonging to eligible users are accepted; a bare service credential is not. Intelligence uses its signed session cookie; cookie PUT and POST requests require a trusted origin and session-bound CSRF token.
POST /api/overseer/pause
Sets the global desired pause state using expectedRevision and expectedPauseVersion. Both must match the current snapshot, otherwise 409 returns the current snapshot. An unchanged desired state with matching versions is a no-op; an actual change increments only pauseVersion. Pause immediately fences the active turn and asks the supervisor to stop and join its native process and tools; existing execution occupancy remains until cleanup releases it or its lease expires. Instructions, tool controls, native thread, checkpoint, queued messages, editor authority, underlying work state and wake/backoff schedule are preserved. Resume restores eligibility under that schedule; it does not start an API-owned worker or bypass backoff. Save, message enqueue, review feedback and clear-memory never implicitly resume. Pause is supported before any saved instruction and does not create a revision. Existing human review approvals and already-dispatched child jobs remain independent. Paused execution creates no new scheduled progress reports or notification delivery claims, but preserves due timestamps and pending notices; already-claimed delivery may finish under its existing fence. pause_changed events contain paused and pauseVersion when a saved revision exists. After an uncertain response, read the current snapshot before a new explicit control request; do not automatically retry against refreshed versions.
Current user-backed staff or ADMIN identity. Account credentials and machine keys belonging to eligible users are accepted; a bare service credential is not. Intelligence uses its signed session cookie; cookie PUT and POST requests require a trusted origin and session-bound CSRF token.
GET /api/overseer/review-groups
Reads retained update versions across all instruction revisions. Application and optional review status are independent. Filters and stable cursor pagination select history without granting mutation authority. Evidence images are served separately through the authenticated evidence endpoint.
Current user-backed staff or ADMIN operator; account credentials or eligible machine keys, or the signed Intelligence cookie. Bare service credentials and preview viewers are rejected.
Reads every saved update version across all instruction revisions. Application and optional review status are independent. Paired entityKind and entityId select one exact record, excluding child records. retainedOnly=true selects versions applied at least once that reference a screenshot whose image remains retained, including later reverted versions. Partially expired versions preserve all evidence references and unavailable capture metadata. With either a record or retention scope, counts cover every matching version before pagination; other requests retain global history counts. Cursors bind all filters. Evidence images are served separately through the authenticated evidence endpoint.
Current user-backed staff or ADMIN operator; account credentials or eligible machine keys, or the signed Intelligence cookie. Cookie decisions require a trusted origin and session-bound CSRF token. Bare service credentials and preview viewers are rejected.
GET /api/overseer/reviews/{id}
Returns one immutable update version, actual application receipt, baseline and applied values, current canonical context, source evidence metadata, and optional review feedback. Historical versions remain independently addressable after later updates.
Current user-backed staff or ADMIN operator; account credentials or eligible machine keys, or the signed Intelligence cookie. Cookie decisions require a trusted origin and session-bound CSRF token. Bare service credentials and preview viewers are rejected.
POST /api/overseer/reviews/{id}/decision
Targets the exact expectedVersion. Mark reviewed changes audit state only. Request changes targets the latest version, leaves its contribution live, and wakes the existing Overseer worker for a fresh correction. Revert queues the version contribution batch through the existing reversal worker and suppresses further automatic updates for the record until request_changes explicitly reopens it. A queued reversal is reverting, not completed. Request IDs bind the exact normalized body; identical replay recovers its durable receipt or definitive rejection without executing again. Screenshot expiry does not block these actions. Current staff account authority and cookie CSRF remain required.
Current staff or ADMIN human account using an account credential or the signed Intelligence cookie. Cookie decisions require a trusted origin and session-bound CSRF token. Machine credentials, service delegation, bare service credentials and preview viewers cannot make review decisions.
GET /api/sessions
Lists canonical sessions with Activity, Organization, location, independent session category, registration status, date, sorting, and pagination filters. Sessions own registration_open_date, registration_close_date, `prices`, and nullable freeform `discounts` text; attendance dates and clocks come from schedule_configurations. Session pricing uses ordered `prices` entries of `{price, basis, currency, description?, before_care_pricing?, after_care_pricing?}`. Optional source-grounded descriptions, such as Members or Non-members, may describe any option, are trimmed and omitted when blank, and never establish a price alone or receive an inferred default label. `discounts` preserves source-stated terms as text, independently of the quotes and the early-bird flag. Each tuition value may be null when unknown, but an entry with all three tuition values null must include a care quote; `[]` means no listed price, and zero means explicitly free. includeRows=false returns metadata and counts with an empty sessions array. Unfiltered total and unfilteredTotal use durable incrementally maintained totals after relevant background work settles and fall back to live counts while committed changes are pending; filtered totals and returned rows remain live. canonical_url is the verified official primary source; relevant_urls is an array of supporting or unverified pages and does not establish an official site. Both fields require Team data access. Normal JSON records omit bookkeeping and ingestion history, null and blank record fields, and unknown availability, registration-status, and coordinate-precision values. Zero, false, empty arrays, masked values, positional range bounds, and structured schedule states retain their meanings. Related entity summaries follow the same projection; CSV and SQL retain their existing contracts. Bookkeeping and provenance are available separately at /api/sessions/{id}/provenance. Age ranges use age_range_years; normal JSON records and column metadata omit the redundant age_range_months alias. CSV, SQL, and the legacy ageMonths filter retain month compatibility. Records and column metadata omit score. Fetch /api/sessions/{id}/score for live detail or /api/sessions/scores?ids=UUID,UUID&cached=true for persisted summaries; ordinary list/detail reads do not load scores. Each ordinary JSON row includes image_urls, a deduplicated array of its own active image URLs, ordered by selected cover, selected logo where applicable, then gallery position. Empty galleries return an empty array. Remote URLs are preserved verbatim; uploaded images retain protected organization brand-asset URLs. Documents, deleted or misowned uploads, inactive memberships, other entities’ media, and source provenance are excluded. Investor previews omit image_urls, CSV columns remain unchanged, and includeRows=false skips media reads.
This endpoint requires an API key; fields and result windows are masked to the account plan
Returns canonical Session category, city, and registration-status facets. Categories are recorded on the session itself and are not inherited from its activity or organization. With ?field= it returns one facet scoped to the list's filters; field=start_week counts upcoming sessions by their starting week (Mondays, ascending), and field=updated_day counts them by the day of their latest update over the last 30 days. Optional snapshot=true with a single field serves incrementally maintained chart counts when no list filters or search are active; filtered requests and the default remain live. Pending source changes or due calendar updates fall back to live counts, so committed edits, deletion, and blacklisting remain visible before background refresh completes. Authentication and field access remain live.
Returns one tier-masked canonical Session and its parent Activity summary. canonical_url is the verified official primary source; relevant_urls is an array of supporting or unverified pages and does not establish an official site. Both fields require Team data access. Sessions own registration_open_date, registration_close_date, `prices`, and nullable freeform `discounts` text. Attendance dates and clocks come from schedule_configurations. Session pricing uses ordered `prices` entries of `{price, basis, currency, description?, before_care_pricing?, after_care_pricing?}`. Optional source-grounded descriptions, such as Members or Non-members, may describe any option, are trimmed and omitted when blank, and never establish a price alone or receive an inferred default label. `discounts` preserves source-stated terms as text, independently of the quotes and the early-bird flag. Each tuition value may be null when unknown, but an entry with all three tuition values null must include a care quote; `[]` means no listed price, and zero means explicitly free. Session care-pricing quotes follow price access rules; schedule hours never grant access to nested care prices. Normal JSON records omit bookkeeping and ingestion history, null and blank record fields, and unknown availability, registration-status, and coordinate-precision values. Zero, false, empty arrays, masked values, positional range bounds, and structured schedule states retain their meanings. Related entity summaries follow the same projection; CSV and SQL retain their existing contracts. Moved fields are configuration_id, external_ids, created_at, updated_at, source_created_at, field_provenance, source_records, merge_jobs, and relationship_jobs. Read permitted bookkeeping and provenance at /api/sessions/{id}/provenance. Age ranges use age_range_years; normal JSON records and column metadata omit the redundant age_range_months alias. CSV, SQL, and the legacy ageMonths filter retain month compatibility. Records and column metadata omit score. Fetch /api/sessions/{id}/score for live detail or /api/sessions/scores?ids=UUID,UUID&cached=true for persisted summaries; ordinary list/detail reads do not load scores. Normal detail responses include data.media, the first gallery page of up to 100 images, with entity, items, coverId, logoId, total, limit, and offset. Images include displayUrl, label, kind, width, and height; source attribution is omitted even for ADMIN. Previews omit media. Optional includeMedia=false skips the gallery; omitted or true includes it. The parameter accepts one exact true or false value, with malformed or repeated values returning 400. Use the existing /media endpoint for additional pages or authorized source attribution.
Account cookie or header, or permitted machine key, from Starter up.
Returns one session's paginated media gallery and selected cover ID with available image dimensions and permitted source attribution. limit defaults to 30 and caps at 100; offset defaults to 0 and caps at 1000000. Media belongs only to the requested session; parent activity and organization media are not inherited. Optional includeProvenance=false skips source attribution and its database read. Omitted or true preserves existing ADMIN source access; this flag never grants authority. Accepts one exact true or false value; malformed or repeated values return 400. Gallery-only pagination can use false; one image’s source disclosure can use true with limit=1 and that image’s offset.
Account cookie or header, or permitted machine key, from Starter up. Previews cannot read media. Source provenance is limited to non-preview ADMIN account viewers.
Returns data.session with id and visible bookkeeping moved from normal reads. Only authorized ADMIN readers additionally receive field_provenance, source_records, merge_jobs, and relationship_jobs. Moved fields are configuration_id, external_ids, created_at, updated_at, source_created_at, field_provenance, source_records, merge_jobs, and relationship_jobs. Field provenance separates current value ownership (state, sources, value_state) from source checks and field decisions. Entries cover visible fields before sparse projection, including cleared and unknown values. A found or checked_not_found observation can exist without a value change; absence of observation means no recorded check. Sources, latest_observation, and history share a flat record shape; history is newest first. The latest observation follows observed time, while decision history follows contribution time. Omitted field_provenance means unavailable; an absent entry in an available map is unknown. Field citations are distinct from site-scoped scrape URLs.
Account cookie or header, or permitted machine key, from Starter up. Bookkeeping follows the existing field masks. Field provenance and raw histories require an ADMIN user-backed viewer, including an ADMIN-owned machine key; staff, bare service credentials, and membership upgrades do not grant this authority. All previews are denied.
Creates a 30-minute one-time access code. domain REDIRECT_SCHOOLS_OUT_INTELLIGENCE returns the Intelligence /auth-callback; REDIRECT_SCHOOLS_OUT_APP returns the app deep link (APP_AUTH_CALLBACK, default schoolsout://auth-callback) and requires challenge, a base64url SHA-256 of the app's verifier. Outside production the app may pass callback exp://<host>:<port>/--/auth-callback for Expo Go
This endpoint requires an API key for an account with a verified email (or ADMIN / staff)
Consumes an interstitial code once and returns the identity plus sessionKey for the product’s session. A code issued with a challenge also requires verifier; a missing or wrong verifier still consumes the code
This endpoint requires a one-time interstitial code
Email a one-shot sign-in link. Always answers success for a well-formed address — no account enumeration. intent (signin|signup) only changes the email copy. The redirect, domain, and optional challenge ride the link's handoff; domain REDIRECT_SCHOOLS_OUT_APP requires a challenge
GET lists the adult's dated care requirements with currently permitted children. POST saves dates or a range, weekdays, local hours, time zone, recurrence, exceptions, flexibility, coverage and optional budget/travel overrides for one to ten permitted children.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
POST /api/users/viewer/care-requirements
GET lists the adult's dated care requirements with currently permitted children. POST saves dates or a range, weekdays, local hours, time zone, recurrence, exceptions, flexibility, coverage and optional budget/travel overrides for one to ten permitted children.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
PATCH {version, changes} corrects named parts and revalidates the whole requirement. DELETE {version} removes it.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
POST /api/users/viewer/care-requirements/{requirementId}/confirmations
POST {version, keys} reconfirms named unchanged parts of the requirement.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
POST /api/users/viewer/child-invitations/accept
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.
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 /api/users/viewer/child-invitations/preview
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.
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.
GET /api/users/viewer/children
GET lists private child profiles with role owner or guardian. POST creates one of up to 20 owned profiles from approved values: a planning label or first and last names is required, and registration reuse (default false) needs both names. Fields contain canonical registration strings; preferences contain up to 20 interests with an unknown/none/listed status and notes; planning contains a label, an age with its as-of date (instead of a birthdate), the grade's school year and participation requirements (required or preference, by category, without diagnoses). Current-value provenance records source (manual, or assistant with the viewer's own conversation turn), actor, approval time and record version. No previous values are retained.
Current account cookie or account credential only; machine keys and delegated service requests are rejected. Lists owned profiles and profiles with an accepted, active guardian membership.
POST /api/users/viewer/children
GET lists private child profiles with role owner or guardian. POST creates one of up to 20 owned profiles from approved values: a planning label or first and last names is required, and registration reuse (default false) needs both names. Fields contain canonical registration strings; preferences contain up to 20 interests with an unknown/none/listed status and notes; planning contains a label, an age with its as-of date (instead of a birthdate), the grade's school year and participation requirements (required or preference, by category, without diagnoses). Current-value provenance records source (manual, or assistant with the viewer's own conversation turn), actor, approval time and record version. No previous values are retained.
Current account cookie or account credential only; machine keys and delegated service requests are rejected. Lists owned profiles and profiles with an accepted, active guardian membership.
DELETE /api/users/viewer/children/{childId}
PUT fully replaces fields and preferences with the current version; omitted or blank optional fields are removed, while omitting planning keeps it. PATCH {version, changes} corrects only named values; null removes one and reuseAllowed is not correctable there. Only the owner may change reuseAllowed or DELETE. DELETE {version} permanently removes the profile, guardian memberships, invitations, care requirement links and child-linked remembered facts; registration snapshots and receipts remain. All writes lock the actor account and profile before rechecking current membership. Stale versions return 409.
Current profile owner or accepted active guardian for PUT; DELETE is owner-only. Mutations require cookie CSRF. Unrelated accounts receive 404 regardless of plan or staff role.
PATCH /api/users/viewer/children/{childId}
PUT fully replaces fields and preferences with the current version; omitted or blank optional fields are removed, while omitting planning keeps it. PATCH {version, changes} corrects only named values; null removes one and reuseAllowed is not correctable there. Only the owner may change reuseAllowed or DELETE. DELETE {version} permanently removes the profile, guardian memberships, invitations, care requirement links and child-linked remembered facts; registration snapshots and receipts remain. All writes lock the actor account and profile before rechecking current membership. Stale versions return 409.
Current profile owner or accepted active guardian for PUT; DELETE is owner-only. Mutations require cookie CSRF. Unrelated accounts receive 404 regardless of plan or staff role.
PUT /api/users/viewer/children/{childId}
PUT fully replaces fields and preferences with the current version; omitted or blank optional fields are removed, while omitting planning keeps it. PATCH {version, changes} corrects only named values; null removes one and reuseAllowed is not correctable there. Only the owner may change reuseAllowed or DELETE. DELETE {version} permanently removes the profile, guardian memberships, invitations, care requirement links and child-linked remembered facts; registration snapshots and receipts remain. All writes lock the actor account and profile before rechecking current membership. Stale versions return 409.
Current profile owner or accepted active guardian for PUT; DELETE is owner-only. Mutations require cookie CSRF. Unrelated accounts receive 404 regardless of plan or staff role.
POST /api/users/viewer/children/{childId}/confirmations
POST {version, keys} records a fresh approval of named unchanged values and advances the version. A key without a saved value returns 409.
Current profile owner or accepted active guardian; account cookie or account credential only. Cookie requests require CSRF.
GET /api/users/viewer/children/{childId}/guardians
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.
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.
Current profile owner only; machine, service and delegated-service credentials are rejected. Cookie mutations require CSRF.
POST /api/users/viewer/children/{childId}/invitations
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.
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.
Current profile owner only; machine, service and delegated-service credentials are rejected. Cookie mutations require CSRF.
GET /api/users/viewer/family-context
Returns version family-context-v1: permitted children (label, computed age or bounded reported age, grade with school-year currency, interests, required and preferred participation conditions, preference notes), the adult's own household preferences, active and upcoming care requirements with their effective time zone and inherited budget/travel, hard conflicts that need clarification, up to 25 saved tracking options (not enrollment or availability), up to 20 of the adult's own School's Out registrations for permitted children (complete:false; not a calendar), unexpired approved facts, and per-value source, approval time and version. Omits last names, birthdates, allergies, registration notes (child.notes), guardian and emergency contacts, reuse permission, invitations and account IDs. Approved preference notes remain in the projection. Generic table, canonical SQL and research Ask never read these records. Booked-session context includes `prices`, scheduleConfigurations, and independently recorded care quotes. Effective date bounds and uniform clocks summarize explicit configurations; sparse dates never imply daily attendance.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
GET /api/users/viewer/family-memory
GET lists explicitly approved remembered facts. POST {approve:true, statement, category, childId?, expiresOn?, source?} remembers the exact approved statement; conversations never save facts automatically. Assistant source is {type:assistant, conversationId, turnId} and must name the viewer’s completed turn. Repeating that source and the same exact values returns the existing fact without changing its approval; deleted sources are refused.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
POST /api/users/viewer/family-memory
GET lists explicitly approved remembered facts. POST {approve:true, statement, category, childId?, expiresOn?, source?} remembers the exact approved statement; conversations never save facts automatically. Assistant source is {type:assistant, conversationId, turnId} and must name the viewer’s completed turn. Repeating that source and the same exact values returns the existing fact without changing its approval; deleted sources are refused.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
DELETE /api/users/viewer/family-memory/{factId}
PATCH {version, changes} corrects a fact. DELETE {version} forgets it.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
PATCH /api/users/viewer/family-memory/{factId}
PATCH {version, changes} corrects a fact. DELETE {version} forgets it.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
POST /api/users/viewer/family-memory/{factId}/confirmations
POST {version} renews approval of the unchanged statement.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
GET /api/users/viewer/feed
School’s Out users/viewer/feed service.
Account or permitted machine credential required
POST /api/users/viewer/feed/camp-views
School’s Out users/viewer/feed/camp-views service.
Account or permitted machine credential required
PUT /api/users/viewer/feed/posts/{postId}/signal
School’s Out users/viewer/feed/posts/{postId}/signal service.
Account or permitted machine credential required
DELETE /api/users/viewer/gmail
GET returns { connected, emailAddress, scope, connected_at } for the viewer's own Gmail grant, with nulls when no inbox is connected; the token is never returned. DELETE revokes the refresh token at Google, then deletes the grant and returns { connected: false, revoked }. A token Google already considers invalid counts as revoked. Any other Google failure returns 502 and keeps the grant so the request can be retried; a grant reconnected during the request returns 409.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Cookie requests require CSRF from a trusted or API_ASSISTANT_ORIGINS origin.
GET /api/users/viewer/gmail
GET returns { connected, emailAddress, scope, connected_at } for the viewer's own Gmail grant, with nulls when no inbox is connected; the token is never returned. DELETE revokes the refresh token at Google, then deletes the grant and returns { connected: false, revoked }. A token Google already considers invalid counts as revoked. Any other Google failure returns 502 and keeps the grant so the request can be retried; a grant reconnected during the request returns 409.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Cookie requests require CSRF from a trusted or API_ASSISTANT_ORIGINS origin.
POST /api/users/viewer/gmail/authorization
Starts Gmail access and returns {url}. Web requests use an optional parent-site redirect and receive the browser nonce cookie. Native requests send {redirect, challenge}, an exact schoolsout://gmail-callback (or exp://host:port/--/gmail-callback outside production) and unpadded base64url SHA-256 app proof. Their URL is a one-use 10-minute API browser bootstrap that sets its own cookie before Google consent. No account key or verifier belongs in that URL.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Cookie requests require CSRF from a trusted or API_ASSISTANT_ORIGINS origin.
POST /api/users/viewer/gmail/complete
Native completion accepts {code, verifier}. Consumes the five-minute ticket only for its starting adult; the original S256 proof must match a 43–128 character verifier. Exchanges the sealed Google authorization code and saves the grant only after validation, then returns top-level {connected, emailAddress, scope, connected_at}. Missing/wrong proof, expiry and replay return 400; a different adult cannot consume the ticket. No token is returned.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Cookie requests require CSRF from a trusted or API_ASSISTANT_ORIGINS origin.
DELETE /api/users/viewer/household
GET returns household search area, time zone, default budget, travel limits and caregiver availability with provenance. Version 0 means never saved. PATCH {version, changes} saves named preferences using the current version; null removes one. DELETE {version} erases preferences and provenance, retaining an empty record with an incremented version to reject stale edits. Read the current version before saving again.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
GET /api/users/viewer/household
GET returns household search area, time zone, default budget, travel limits and caregiver availability with provenance. Version 0 means never saved. PATCH {version, changes} saves named preferences using the current version; null removes one. DELETE {version} erases preferences and provenance, retaining an empty record with an incremented version to reject stale edits. Read the current version before saving again.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
PATCH /api/users/viewer/household
GET returns household search area, time zone, default budget, travel limits and caregiver availability with provenance. Version 0 means never saved. PATCH {version, changes} saves named preferences using the current version; null removes one. DELETE {version} erases preferences and provenance, retaining an empty record with an incremented version to reject stale edits. Read the current version before saving again.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
POST /api/users/viewer/household/confirmations
POST {version, keys} reconfirms named unchanged household preferences.
Signed-in adult only; account cookie or account credential, never delegated service or machine keys. Private to that adult even for shared children.
GET /api/users/viewer/registrations
Lists the current parent’s latest 200 profile-backed registration disclosures, including camp, receipt, profile version, shared field names and exact accepted consent statements. Does not expose another parent’s records. Removing a reusable profile leaves its already-submitted camp registration and disclosure intact.
Current account only; machine, service and delegated-service credentials are rejected
POST /api/users/viewer/registrations/{id}
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.
Current account owns the child profile or has accepted active guardian access; cookie mutations require CSRF. Machine keys and service impersonation are rejected.