Integration
Campus API — Wave 1
Learner identity and Grok Bot progress on the live Next campus. Guests stay in the browser. Signed-in watch and quiz writes Postgres. This page is unlisted from search.
OpenAPI: /docs/openapi-wave1.json. Station 01: /c/grok-bot/s/briefing.
01
How it works
- Guest walks Grok Bot. Progress is
localStorage.POST /api/eventsreturns 401. - A signed-in Auth.js member is upserted into
membersand given amembershipsrow on orgfield-school. - Watch, quiz, and field work on station 01 post
learning_events. GET /api/progress?course=grok-botrebuilds that membership’s map. Every query is scoped byorg_idandmembership_id.- Org
householdexists and does not see Field School operator catalog events. User B does not see user A.
Base URL https://portal.fieldschool.ai. university.benjohnson.ai 301s to portal. AUTH_URL is portal. Cookies are Auth.js session cookies.
02
Seed orgs
| slug | kind | isolation | group |
|---|---|---|---|
| field-school | operator | public_catalog | cohort-0 |
| household | family | strict | family |
| sales | company | platform_plus | desk |
New memberships default to stance learner. Staff emails get admin.
03
Postgres schema
Postgres 16 + pgvector. Database campus. Container field-school-campus-db. Data /opt/field-school/postgres. Not the leftover TanStack database field-school-db.
organizations
id uuid PK, slug unique, name, kind, isolation, homeschool_mode, created_by, created_at.
members
id uuid PK, email unique, name, created_at.
memberships
id uuid PK, org_id, member_id, stance, created_at. Unique (org_id, member_id).
groups / group_memberships
groups: org_id + slug unique. group_memberships: (group_id, membership_id).
assignments
org_id, membership_id, object_type, object_id, status, raw jsonb. Table is in Wave 1; station field work currently writes a learning_events row of kind assignment.
learning_events (spine)
org_id, membership_id, actor_membership_id, actor_stance, kind (watch | quiz | assignment), object_type, object_id (grok-bot:briefing), skill_ids text[], score numeric, raw jsonb, created_at.
04
GET /api/me
Guest:
{ "authenticated": false, "guest": true }Signed in:
{
"authenticated": true,
"member": { "id": "uuid", "email": "a@example.com", "name": "Ada" },
"org": { "slug": "field-school", "isolation": "public_catalog" },
"memberships": [
{ "id": "uuid", "stance": "learner", "org": "field-school", "isolation": "public_catalog" }
]
}05
GET /api/progress?course=grok-bot
Guest returns an empty module map. Signed in returns only this membership.
{
"authenticated": true,
"course": "grok-bot",
"org": "field-school",
"membershipId": "uuid",
"modules": {
"briefing": {
"watched": true,
"assignment": { "list8": true },
"notes": "",
"quizScore": 4,
"quizPassed": true,
"passed": true
}
}
}06
POST /api/events
Guest is 401 sign_in_required. Kind must be watch, quiz, or assignment.
{
"kind": "quiz",
"course": "grok-bot",
"station": "briefing",
"score": 4,
"raw": { "passed": true, "answers": { "q1": 1 } }
}Success:
{ "ok": true, "id": "uuid", "org": "field-school", "membershipId": "uuid" }Station 01 sends watch when the clip is marked, assignment when field work is saved, quiz on submit. object_id defaults to {course}:{station}.
07
curl
curl -sS https://portal.fieldschool.ai/api/me
curl -sS "https://portal.fieldschool.ai/api/progress?course=grok-bot"
curl -sS -X POST https://portal.fieldschool.ai/api/events \
-H "content-type: application/json" \
-d '{"kind":"watch","course":"grok-bot","station":"briefing"}'POST without a session cookie is 401. Sign in on /login, then call from that browser.
08
What this is not
- Not Cap or the edit MCP.
- Not campus MCP (Wave 7) or wildcard hosts (Wave 2).
- Not Better Auth / PGlite / migrations 0001–0003.
- AUTH_URL is portal.fieldschool.ai. University 301s there.