Executable contract cookbook
This page is the implementation-facing companion to the route overviews. Its paths, methods, request keys, limits, and non-JSON transports are derived from the current Go handlers. Replace every uppercase placeholder before running a command. A trailing slash is part of the public contract.
Base command and response shape
export KIBEGI_API="https://api.example.com/api/v1"
export ACCESS_TOKEN="<access JWT from login>"
curl --fail-with-body "$KIBEGI_API/auth/profile/" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json"Protected routes require Authorization: Bearer <access-token>. JSON writes require Content-Type: application/json; upload and profile-image writes are multipart. Successful resource writes normally use this envelope, while list routes return either the same envelope or a page object with count, next, previous, and results.
{
"success": true,
"message": "…",
"data": {},
"errors": null
}Transport rule:
uploads/*/download/, acceptedsharing/*/download/, library downloads, and public schedule subscriptions return bytes or ICS text, not the JSON envelope. Do not callresponse.json()for those routes.
Authentication and profile
All routes below are public unless the Token column says Access. Passwords must contain at least eight characters and cannot be entirely numeric. Registration and password-reset OTPs expire after five minutes; phone OTPs expire after ten minutes.
| Method and path | Token | Data to send | Source behavior |
|---|---|---|---|
POST /auth/register/ | — | {"email":"learner@example.com","full_name":"Asha Mrema","user_type":"student","password":"SecurePass9","confirm_password":"SecurePass9"} | user_type defaults to student; only student and lecturer are accepted. Returns 201 and initiates email OTP verification. |
POST /auth/register/verify/ | — | {"email":"learner@example.com","otp":"123456","university":"Kibegi University","phone_number":"+255712345678"} | user_type is optional here. A verified student gets data.user and data.tokens.access/refresh; a lecturer remains pending approval. |
POST /auth/register/resend/ | — | {"email":"learner@example.com"} | Resends registration OTP; the handler rate-limits recent resends. |
POST /auth/login/ | — | {"email":"learner@example.com","password":"SecurePass9"} | Returns data.user and data.tokens with access and refresh. |
POST /auth/token/refresh/ | — | {"refresh":"<refresh JWT>"} | Returns a rotated { "access": "…", "refresh": "…" } object. This endpoint intentionally returns that object directly rather than the standard envelope. |
POST /auth/logout/ | — | {"refresh":"<refresh JWT>"} | Blacklists the supplied refresh token; Redis must be available for durable revocation. |
POST /auth/change-password/ | Access | {"current_password":"SecurePass9","new_password":"NewSecurePass9","confirm_password":"NewSecurePass9"} | Changes the authenticated user’s password. |
GET /auth/profile/ | Access | — | Returns the current profile: id, email, username, user_type, approval and phone fields, image fields, and date_joined. |
PUT or PATCH /auth/profile/ | Access | Any subset of {"username":"Asha Mrema","university":"Kibegi University","phone_number":"+255712345678"} | username cannot be blank. |
POST /auth/profile/image/ | Access | Multipart field profile_image (or compatibility field image) | Accepts JPEG, PNG, GIF, or WebP only, at most 5MB; returns the updated profile. |
DELETE /auth/profile/image/ | Access | — | Removes the authenticated user’s image. |
POST /auth/google/ | — | One of {"access_token":"…"}, {"credential":"…"}, or {"id_token":"…"}; optional user_type | Accepts a verified Google identity; a newly created non-student lecturer is pending approval. |
GET /auth/lecturers/pending/ | Staff access | — | Lists active, unapproved lecturers. |
POST /auth/lecturers/pending/ | Staff access | {"user_id":42,"action":"approve"} or {"user_id":42,"action":"reject"} | Only staff or superusers may act. |
POST /auth/phone/send-otp/ | Access | {"phone_number":"0712345678"}; phone is also accepted | Normalizes Tanzania numbers to +255…; returns the normalized phone. |
POST /auth/phone/verify/ | Access | {"phone_number":"+255712345678","otp":"123456"} | Marks the number verified after a matching non-expired OTP. |
POST /auth/password-reset/ | — | {"email":"learner@example.com"} | Starts reset OTP delivery. |
POST /auth/password-reset/verify/ | — | {"email":"learner@example.com","otp":"123456"} | Returns data.reset_token. |
POST /auth/password-reset/confirm/ | — | {"token":"<reset token>","new_password":"NewSecurePass9","confirm_password":"NewSecurePass9"} | /auth/password-reset-confirm/ is an equivalent compatibility path. |
POST /auth/password-reset/resend/ | — | {"email":"learner@example.com"} | Resends a password-reset OTP. |
The root compatibility shortcuts POST /register/ and POST /login/ dispatch to the same registration and login handlers.
Classes, uploads, files, and shares
Classes
| Method and path | Data to send | Result |
|---|---|---|
GET /classes/?page=1&page_size=20 | Access header; page_size is capped at 100. | Paginated visible classes. |
POST /classes/ | {"name":"Distributed Systems","description":"Term 1","is_public":false} | Creates a class and returns a detail object containing its class_code and join QR payload. name is required. |
GET /classes/search/?q=systems&page=1&page_size=20 | Access header. | Searches classes visible to the caller. |
POST /classes/join/ | {"class_code":"ABCD12"} | Joins an existing class by code. |
GET /classes/{class_uuid}/ | Access header. | Returns class detail. |
PUT or PATCH /classes/{class_uuid}/ | Any subset of {"name":"…","description":"…","is_public":true} | Only the class creator may update. |
DELETE /classes/{class_uuid}/ | Access header. | Only the creator may delete. |
GET /classes/{class_uuid}/members/?page=1&page_size=20 | Access header. | Paginated members for an accessible class. |
POST /classes/{class_uuid}/leave/ | Access header; no body. | The creator cannot leave their own class. |
Uploads and file compatibility
curl --fail-with-body -X POST "$KIBEGI_API/uploads/" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-F "class_obj=11111111-2222-3333-4444-555555555555" \
-F "file=@./lecture-notes.pdf" \
-F "file_name=week-01-notes.pdf"| Method and path | Data to send | Result |
|---|---|---|
GET /uploads/?page=1&page_size=20&class_id={uuid} | Access header; class_id is optional. | Paginated accessible uploads. |
POST /uploads/ | Multipart file and UUID class_obj; optional file_name. | Maximum file size is 50MB. Returns the created upload, including file_code, file_url, uploader, class_obj, deletion flags, and timestamps. Go then calls the indexer with { "force": false }. |
GET /uploads/search/?q=notes&page=1&page_size=20 | Access header. | Paginated matching uploads. |
GET /uploads/trash/ or GET /uploads/recent/ | Access header; standard page parameters. | Paginated trashed or recent uploads. |
GET /uploads/{file_code}/ | Access header. | Full upload JSON envelope. |
DELETE /uploads/{file_code}/ | Access header. | Soft-deletes an upload owned by the caller. |
GET /uploads/{file_code}/download/ | Access header; request bytes. | Binary stream with Content-Disposition, Content-Length, and X-Content-Type-Options: nosniff. |
POST /uploads/{upload_uuid}/restore/ | Access header; no body. | Restores a trashed upload by UUID. |
DELETE /uploads/{upload_uuid}/permanent-delete/ | Access header; no body. | Permanently deletes the database record and object after authorization. |
GET /files/all/, /files/my-uploads/, /files/shared-with-me/, or /files/deleted/ | Access header. | Legacy unified-file responses; each list is bounded to 100 records. |
GET /files/{file_code}/ | Access header. | Legacy compatibility file payload. |
POST /files/{upload_uuid}/restore/ or DELETE /files/{upload_uuid}/permanent-delete/ | Access header; no body. | Legacy wrappers for the corresponding upload state changes. |
Shares
| Method and path | Data to send | Result |
|---|---|---|
POST /sharing/ | {"file_code":"ABC123","shared_with_id":42,"message":"Please review"} | Creates one share; duplicate recipient/file pairs are rejected. |
POST /sharing/bulk/ | {"file_code":"ABC123","user_ids":[42,43],"message":"Please review"} | Accepts from 1 to 50 recipient IDs; returns 202 with processing metadata. |
GET /sharing/requests/, /sharing/shared-with-me/, or /sharing/my-shares/ | Access header; optional status, page, and page_size. | Paginated shares. |
GET /sharing/{share_uuid}/ | Access header. | Share detail with upload metadata and access state. |
POST /sharing/{share_uuid}/accept/ or /reject/ | Access header; no body. | Transitions the recipient’s share state. |
GET /sharing/{share_uuid}/download/ | Access header. | Binary file stream; only the accepted recipient may download. |
Notifications, friends, schedules, and storage
| Method and path | Data to send | Result |
|---|---|---|
GET /notifications/?is_read=false&type={type}&page=1&page_size=20 | Access header; filters are optional. | Returns a notification page plus unread_count. |
GET /notifications/unread-count/ | Access header. | Returns { "unread_count": n }. |
POST /notifications/read-all/ | Access header; no body. | Marks every notification read. |
POST /notifications/{notification_id}/read/ | Access header; no body. | Marks one notification read. |
DELETE /notifications/{notification_id}/ | Access header; no body. | Deletes one notification. |
GET /friends/, /friends/requests/incoming/, or /friends/requests/sent/ | Access header; optional page and page_size. | Paginated friendships or requests. |
GET /friends/search/?q={query} | Access header. | Searches eligible users. |
POST /friends/add/ | {"user_id":42} or {"email":"friend@example.com"} | Creates a friend request. |
POST /friends/{friendship_id}/accept/, /decline/, or /cancel/ | Access header; no body. | Changes a request state. |
PATCH /friends/{friendship_id}/nickname/ | {"nickname":"Study partner"} | Nickname is limited to 100 characters. |
DELETE /friends/{friendship_id}/ | Access header; no body. | Removes a friendship. |
GET /schedule/calendars/ or POST /schedule/calendars/ | For creation send {"name":"Lectures","description":"Term plan","is_public_sync":false}. | List or create caller-owned calendars. |
GET, PUT, PATCH, or DELETE /schedule/calendars/{calendar_uuid}/ | On update send any subset of name, description, and is_public_sync. | Reads, updates, or deletes a calendar. |
GET or POST /schedule/calendars/{calendar_uuid}/events/ | Create event with {"title":"Lecture","description":"Optional","location":"Room A","start_at":"2026-09-01T08:00:00Z","end_at":"2026-09-01T10:00:00Z","event_type":"class","recurrence":"weekly","days":["monday"],"reminder_minutes":30}. | title, start_at, and end_at drive validation; timestamps are RFC3339. |
GET, PUT, PATCH, or DELETE /schedule/events/{event_uuid}/ | Use the same event object for update. | Reads or changes one event. |
GET or POST /schedule/calendars/{calendar_uuid}/share/ | Access header; no client body. | Returns share-token URLs including subscribe_url, download_url, and webcal_url. |
GET /schedule/calendars/{calendar_uuid}/qr/ | Access header. | Returns the calendar QR representation. |
GET or PUT /schedule/sms-account/ | Update with {"phone_number":"+255712345678"}. | Reads or saves the schedule SMS account. |
GET /public/schedule/code/{calendar_code}/info/ or GET /public/schedule/{share_token}/info/ | Public; no header. | JSON public calendar metadata. |
GET /public/schedule/{share_token}/subscribe/ or /download/ | Public; no header. | ICS text; download is an attachment while subscribe is a calendar feed. |
GET /storage/ or /storage/info/ | Access header. | Current quota and usage data. |
POST /storage/recalculate/ | Access header; no body. | Recalculates durable usage from non-deleted uploads. |
GET /storage/history/?limit=30 | Access header; limit defaults to 30 and caps at 100. | Usage history. |
Marketplace, library, channels, class communications, and assignments
| Method and path | Data to send | Result |
|---|---|---|
GET /marketplace/listings/ | Access header; standard list filters/pagination. | Marketplace listings visible to the caller. |
GET /marketplace/listings/{listing_uuid}/ | Access header. | Listing detail. |
POST /marketplace/listings/{listing_uuid}/purchase/ | {"quantity":1} | Purchases the requested quantity. |
GET /library/items/ | Access header. | Paginated library items. |
POST /library/items/ | Multipart form that carries the library metadata plus file content. | Creates a library item and stores its object. |
GET, PUT, PATCH, or DELETE /library/items/{item_code}/ | For update send the serialised library Item resource accepted by the handler. | Detail increments views; update/delete are uploader-only. |
GET /library/items/{item_code}/download/ | Access header. | Binary library object with Content-Disposition. |
GET or POST /channel/channels/ | Create with the serialised Channel resource accepted by the handler. | Lists or creates channels. |
GET, PUT, or PATCH /channel/channels/{channel_uuid}/ | Update with the serialised Channel resource. | Channel detail/update. |
POST /channel/channels/{channel_uuid}/join/ | Access header; no body. | Joins the caller through the channel route. |
GET or POST /channel/channels/{channel_uuid}/members/ | Add member with {"identifier":"user@example.com","role":"member"}. | Lists or adds channel members. |
GET /channel/channels/{channel_uuid}/wallet/ | Access header. | Channel SMS wallet detail. |
POST /channel/channels/{channel_uuid}/broadcasts/ | {"subject":"Update","message":"Class moved online","venue":"Zoom"} | message is required; creates a channel broadcast. |
DELETE /channel/members/{member_uuid}/ or GET /channel/broadcasts/{broadcast_uuid}/ | Access header; no body. | Removes a member or reads a broadcast. |
GET /public/channel/{invite_token}/info/ | Public; no body. | Compact public channel metadata. |
POST /public/channel/{invite_token}/join/ | Access token required by the underlying join action; no JSON body. | Joins through an invite token. |
GET or PUT /class-comms/classes/{class_uuid}/profile/ | Update with any subset of {"public_registration_enabled":true,"default_sender_name":"Kibegi","registration_hint":"Register below"}. | Reads or saves class communications profile. |
GET /class-comms/classes/{class_uuid}/wallet/ | Access header. | Class communications wallet. |
GET or POST /class-comms/classes/{class_uuid}/contacts/ | Upsert a Contact JSON resource; full_name and phone_number are validated. | Lists or creates contacts. |
POST /class-comms/classes/{class_uuid}/representatives/ | {"user_id":42,"role":"representative"} | Assigns a representative. |
POST /class-comms/classes/{class_uuid}/broadcasts/ | {"subject":"Reminder","message":"Submit by Friday","venue":"Portal"} | message is required. |
DELETE /class-comms/contacts/{contact_uuid}/ or GET /class-comms/broadcasts/{broadcast_uuid}/ | Access header; no body. | Deletes a contact or reads a broadcast. |
GET /public/class-comms/{token}/info/ | Public; no body. | Public registration configuration when enabled. |
POST /public/class-comms/{token}/register/ | {"full_name":"Asha Mrema","phone_number":"+255712345678","consent_granted":true,"notes":"Parent contact"} | full_name and phone_number are required; the contact is upserted by class and phone. |
GET or POST /assignments/classes/{class_uuid}/ | Create with a serialised Assignment object; path class ID is authoritative. | Lists or creates assignments. |
GET, PUT, PATCH, or DELETE /assignments/{assignment_uuid}/ | Use a serialised Assignment object for updates. | Assignment detail and owner-authorized changes. |
GET or POST /assignments/{assignment_uuid}/submissions/ | Save with {"response_text":"…","attachment":"…","status":"draft"}; status is draft or submitted. | Lists or creates/updates a submission. |
POST /assignments/submissions/{submission_uuid}/grade/ | {"score":85,"feedback":"Strong reasoning","action":"grade"}; action is grade or return. | Lecturer/manager grading transition. |
/api/v1/classcomms/ remains a compatibility alias of /api/v1/class-comms/.
AI, search, SMS, and health
| Method and path | Data to send | Result |
|---|---|---|
GET /health/ | No token. | Readiness report; an unhealthy dependency can produce 503. |
GET or PUT /ai/settings/ | Save {"api_key":"provider-key","chat_model":"model-name"}; chat_model is optional. | Reads settings with the provider key masked, or saves settings. |
GET /ai/usage/ | Access header. | Current AI usage accounting. |
GET /ai/conversations/ | Access header. | Conversation list. |
GET or DELETE /ai/conversations/{conversation_uuid}/ | Access header; no body. | Reads or deletes a conversation. |
POST /ai/chat/ | {"message":"Summarize this class","conversation_id":"optional UUID","class_id":"optional UUID"} | message is required and capped at 2,000 characters. |
GET /ai/status/{upload_uuid}/ | Access header. | Durable processing job, or synthetic { "status":"not_started", "chunks_created":0 } when no job exists. |
GET /search/?q=network&categories=classes,files&limit=10 | Access header. | q must contain at least two characters; categories may only be users, classes, files, friends, or library; limit caps at 50. |
GET /search/suggestions/?q=net&limit=10 | Access header. | Suggestion list; limit caps at 10. |
GET /search/history/ or DELETE /search/history/ | Access header. | Reads or clears the caller’s search history. |
GET /sms/accounts/{owner_type}/{owner_id}/ | Access header. | SMS account detail for the authorized generic owner. |
POST /sms/accounts/{owner_type}/{owner_id}/topup/ | {"amount":100} | Adds credits and returns the authoritative balance. |
GET /sms/deliveries/?limit=20 | Access header; limit defaults to 20 and caps at 100. | SMS delivery history. |
Contract checks before integration
Use an access token issued by the current service, keep a literal trailing slash, and send only fields the active handler decodes. For every mutation, assert both the HTTP status and success value. For lists, read results; for streamed downloads, inspect headers and bytes. When calling a write through the agent gateway, add the gateway’s mandatory confirm: true even though that flag is not sent directly to Go.