Developer docs
API reference

Learner API

Product user JWT API for humans in Campus, the website widget, WordPress, or your own UI. Base path: https://www.tribepeer.com/api/product/v1. Tokens are issued only after email verification — no Sanctum sessions.

Always send X-Institution-Uuid on school-scoped routes so learners only see that institution’s classes. Partner tp_sec_ never belongs in Campus / webview env vars.

Headers

AuthorizationBearer <product_jwt> on authenticated routes
X-Institution-UuidSchool UUID — required for tribes, chat, ask, manage
Content-Typeapplication/json
Acceptapplication/json

Authentication

POST /product/v1/auth/register

Create a TribePeer user. No token until verify.

Request body

first_namestring ≤80, no spaces, required
last_namestring ≤80, no spaces, required
emailunique email, required
passwordmin 8 chars, required
roleoptional user | tutor

Response 201

application/json
{
  "requires_verification": true,
  "email": "learner@school.edu",
  "message": "Account created. Check your email for a verification code. No token until you verify."
}
POST /product/v1/auth/verify

Confirm the 6-digit OTP and receive a product JWT.

emailrequired
otpexactly 6 digits

Response 200

application/json
{
  "access_token": "eyJ…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "expires_at": "2026-09-06T12:00:00+00:00",
  "user": { "uuid": "…", "email": "…", "first_name": "…", "…" : "…" }
}
POST /product/v1/auth/login

Sign in with email or username. Unverified accounts get 403 + fresh OTP.

loginemail or username, required
passwordrequired
POST /api/product/v1/auth/login
curl -s https://www.tribepeer.com/api/product/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "login": "learner@school.edu",
    "password": "••••••••"
  }'

Unverified 403

application/json
{
  "error": "unverified",
  "requires_verification": true,
  "email": "learner@school.edu",
  "message": "Verify your email before signing in. We sent a new code."
}
POST /auth/resend-otpbody: { "email" }
POST /auth/refreshauthenticated · new JWT
GET /meauthenticated · current user

Public branding

GET /product/v1/branding

No auth. Resolve school brand by publishable key or institution UUID.

keyquery · pk_test_ / pk_live_
institutionquery · institution UUID (or header X-Institution-Uuid)

Response includes institution, branding colours/logo, and Campus flags such as Ask enabled / watermark.

Join a class

POST /product/v1/join

Redeem an institution join code for the signed-in user.

join_codestring ≤40, required

Response 200

application/json
{
  "status": "joined",
  "message": "…",
  "tribe_uuid": "…",
  "institution_uuid": "…"
}

Classes & materials

GET /product/v1/tribes/mine

Classes the user enrolled in or owns at this school.

GET /api/product/v1/tribes/mine
curl -s https://www.tribepeer.com/api/product/v1/tribes/mine \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "X-Institution-Uuid: $INSTITUTION_UUID"

Response 200

application/json
{
  "institution": { "uuid": "…", "name": "Lagos High" },
  "enrolled": [ { "uuid": "…", "title": "SS1 Biology", "…" : "…" } ],
  "owned": [ ],
  "is_owner": false
}
GET /tribes/{uuid}Full class payload (modules + published materials + progress)
POST /tribes/{uuid}/materials/{material}/completeMark a text lesson complete → { success, progress, material_id }
POST /tribes/{uuid}/materials/{material}/quizSubmit quiz answers
POST /tribes/{uuid}/materials/{material}/assignmentSubmit assignment (text and/or file)
POST /product/v1/tribes/{uuid}/materials/{material}/quiz

Save quiz results for the signed-in learner.

scoreinteger ≥0, required
max_scoreinteger ≥0, required
needs_reviewboolean, optional
answersobject/array, optional · keyed answers
POST …/quiz
curl -s https://www.tribepeer.com/api/product/v1/tribes/$TRIBE/materials/$MATERIAL/quiz \
  -H "Authorization: Bearer $USER_TOKEN" \
  -H "X-Institution-Uuid: $INSTITUTION_UUID" \
  -H "Content-Type: application/json" \
  -d '{
    "score": 8,
    "max_score": 10,
    "needs_review": false,
    "answers": { "0": "Leaf", "1": "Chloroplast" }
  }'

Response 200

application/json
{
  "message": "Quiz results saved.",
  "submission": {
    "score": 8,
    "max_score": 10,
    "score_pct": 80,
    "needs_review": false,
    "answers_revealed": false
  }
}

Cohort chat

GET /chat/threadsThreads for the school community
GET /chat/threads/{thread}/messagesLatest messages
POST /chat/threads/{thread}/messagesbody: { "body": "…" }

Threads response shape

application/json
{
  "community": { "uuid": "…", "name": "…" },
  "threads": [
    { "uuid": "…", "name": "General", "is_default": true, "message_count": 12 }
  ]
}

Ask TribeMate

POST /ai/ask with { "message": "…" } (max 4000). Returns { reply, usage }. Full quota / error docs: AI · Learner Ask.

School manage (staff in Campus)

Same product JWT. Intended for institution owners / staff driving a Campus-style admin UI — still no Sanctum.

GET /manage/tribesClasses this staff can manage
POST /manage/tribesCreate class
PATCH /manage/tribes/{uuid}Update class
GET /manage/join-codes · POSTList / create join codes
GET /manage/tribes/{uuid}/curriculumModules + materials editor payload
POST …/modules · materials CRUD · publishCurriculum writes
GET …/studentsRoster
GET …/submissionsResults inbox
GET …/submissions/quiz/{id}One quiz attempt
POST …/assignments/{submissionUuid}/gradeGrade assignment
POST …/quiz/{id}/resetAllow retake after answers revealed

Errors

401Missing / invalid product JWT
403Unverified, or Campus grant missing
402Student cap / AI quota / plan
404Tribe / thread / school not found
422Validation (join code, quiz already revealed, etc.)
429Auth / campus / ask throttles

← Organisation API AI docs