Admin
System-admin-only endpoints — aggregate stats, user billing, and subscription management used by the admin dashboard in TerraGoNewUI (src/views/admin/). Every endpoint here requires a System Admin bearer token and reads/writes across all schools rather than being scoped to a single Administrative Staff account.
Schools
GET Get Schools Stats Schools {{url}}/users/schools/stats ▸
Aggregate counts of Administrative Staff (school) accounts by status — used to power the admin dashboard's school stat cards.
Response
- total — total school accounts.
- active — accounts with
is_active = 1. - pilot — accounts with
is_active = -1(pilot/trial status). - inactive — accounts with
is_active = 0.
Notes
- System-admin endpoint — aggregates across all schools.
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": {
"total": 42,
"active": 35,
"pilot": 4,
"inactive": 3
},
"message": "Schools stats retrieved successfully"
}
Billing
GET Get User Billing Billing {{url}}/users/admin/users/95db0ec4-83ab-4a7a-aff4-383e738e8d34/billing ▸
Full billing picture for a single user (invoices, subscriptions, and any subscription voucher codes redeemed) — powers the "Billing & Transactions" tab on a parent's admin profile page.
Response
- invoices — each with
total_amountand its lineitems. - subscriptions — the user's subscriptions, with
dependantinfo. - voucher_codes — subscription voucher codes redeemed by this user.
Notes
- System-admin endpoint.
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": {
"invoices": [
{
"id": "9c3e2f1a-1234-4a11-8888-abcde1234567",
"status": "Paid",
"total_amount": 1500,
"items": [
{
"id": "a1b2c3d4-0000-4a11-8888-abcde1234567",
"type": "Subscription",
"amount": 1500
}
],
"created_at": "Monday 06 Jul 2026 09:12 AM"
}
],
"subscriptions": [
{
"id": "b2c3d4e5-0000-4a11-8888-abcde1234567",
"status": "Active",
"start_date": "2026-01-01",
"end_date": null,
"is_forever": true,
"dependant": {
"id": "c3d4e5f6-0000-4a11-8888-abcde1234567",
"name": "Amani Otieno"
}
}
],
"voucher_codes": []
},
"message": "User Billing Retrieved Successfully"
}
Subscriptions
GET List Subscriptions Subscriptions {{url}}/admin/subscriptions ▸
Paginated, filterable list of every parent-dependant subscription — the admin dashboard's Subscriptions tab.
Query Parameters
- status (
string, optional):Active,Expired,Disabled, orCancelled. - search (
string, optional): matches parent or dependant name/email. - sortby (
string, optional): one ofamount,startdate,enddate,status,createdat,parent,child,school(defaultcreated_at). - sort_dir (
string, optional):ascordesc(defaultdesc). - per_page (
integer, optional, max 100). - page (
integer, optional).
Notes
- System-admin endpoint.
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": {
"data": [
{
"id": "b2c3d4e5-0000-4a11-8888-abcde1234567",
"status": "Active",
"start_date": "2026-01-01",
"end_date": null,
"is_forever": true,
"has_lifetime_voucher": false,
"amount": 1500,
"created_at": "2026-01-01 08:00:00",
"parent": {
"id": "35438f0b-b488-4ea0-b69d-73e214910ab9",
"first_name": "LaParent",
"last_name": "Ente",
"email": "laparent@gmail.com",
"phone": "+25470000001"
},
"dependant": {
"id": "c3d4e5f6-0000-4a11-8888-abcde1234567",
"name": "Amani Otieno"
},
"school": {
"id": "448e2ae4-48b4-4842-84b1-a73c84d21de1",
"name": "Alliance High School"
}
}
],
"total": 1,
"per_page": 15,
"current_page": 1,
"last_page": 1
},
"message": "Subscriptions retrieved successfully"
}
POST Create Subscription Subscriptions {{url}}/admin/subscriptions ▸
Manually create a subscription for a parent/dependant pair — used by admins to grant subscriptions outside the normal payment flow.
Request Body
- parent_id (
stringUUID, required): must exist inusers. - dependant_id (
stringUUID, required): must exist independants. - status (
string, required):Active,Expired,Disabled, orCancelled. - amount (
number, required, min 0). - start_date (
date, required). - end_date (
date, optional, nullable). - isforever (
boolean, optional): when true,enddateis stored asnullregardless of what was passed.
Notes
- System-admin endpoint.
- Requires a bearer token (
auth:api).
{
"parent_id": "35438f0b-b488-4ea0-b69d-73e214910ab9",
"dependant_id": "c3d4e5f6-0000-4a11-8888-abcde1234567",
"status": "Active",
"amount": 1500,
"start_date": "2026-01-01",
"end_date": null,
"is_forever": true
}{
"status": 200,
"data": {
"id": "b2c3d4e5-0000-4a11-8888-abcde1234567",
"status": "Active",
"start_date": "2026-01-01",
"end_date": null,
"is_forever": true,
"amount": 1500,
"created_at": "2026-07-13 10:00:00",
"parent": {
"id": "35438f0b-b488-4ea0-b69d-73e214910ab9",
"first_name": "LaParent",
"last_name": "Ente",
"email": "laparent@gmail.com",
"phone": "+25470000001"
},
"dependant": {
"id": "c3d4e5f6-0000-4a11-8888-abcde1234567",
"name": "Amani Otieno"
}
},
"message": "Subscription created successfully"
}
PATCH Update Subscription Subscriptions {{url}}/admin/subscriptions/b2c3d4e5-0000-4a11-8888-abcde1234567 ▸
Partially update a single subscription.
Request Body (all optional)
- status (
string):Active,Expired,Disabled, orCancelled. - amount (
number, min 0). - start_date (
date). - end_date (
date, nullable). - isforever (
boolean): when true,enddateis set tonullregardless of what was passed forend_date.
Response
Also returns haslifetimevoucher — whether the parent holds a lifetime (forever-duration) subscription voucher code.
Notes
- System-admin endpoint.
- Requires a bearer token (
auth:api).
{
"status": "Active",
"amount": 1800
}{
"status": 200,
"data": {
"id": "b2c3d4e5-0000-4a11-8888-abcde1234567",
"status": "Active",
"start_date": "2026-01-01",
"end_date": null,
"is_forever": true,
"has_lifetime_voucher": false,
"amount": 1800
},
"message": "Subscription updated successfully"
}
DELETE Delete Subscription Subscriptions {{url}}/admin/subscriptions/b2c3d4e5-0000-4a11-8888-abcde1234567 ▸
Soft-delete a single subscription.
Notes
- System-admin endpoint.
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": [],
"message": "Subscription deleted successfully"
}
PATCH Bulk Update Subscriptions Subscriptions {{url}}/admin/subscriptions/bulk ▸
Apply the same field changes to many subscriptions at once — used by the admin Subscriptions table's multi-select actions.
Request Body
- ids (
string[]UUIDs, required, min 1): subscription IDs to update. - status (
string, optional):Active,Expired,Disabled, orCancelled. - amount (
number, optional, min 0). - start_date (
date, optional). - end_date (
date, optional, nullable). - is_forever (
boolean, optional).
Notes
- System-admin endpoint.
- Requires a bearer token (
auth:api).
{
"ids": [
"b2c3d4e5-0000-4a11-8888-abcde1234567",
"d4e5f6a7-0000-4a11-8888-abcde1234567"
],
"status": "Active"
}{
"status": 200,
"data": {
"updated": 2
},
"message": "Subscriptions updated successfully"
}
POST Bulk Delete Subscriptions Subscriptions {{url}}/admin/subscriptions/bulk-delete ▸
Soft-delete many subscriptions at once.
Request Body
- ids (
string[]UUIDs, required, min 1): subscription IDs to delete.
Notes
- System-admin endpoint.
- Requires a bearer token (
auth:api).
{
"ids": [
"b2c3d4e5-0000-4a11-8888-abcde1234567"
]
}{
"status": 200,
"data": {
"deleted": 1
},
"message": "Subscriptions deleted successfully"
}
Auth
PUT Logout Auth {{url}}/auth/logout ▸
Log out the currently authenticated user. Invalidates the current access token (JWT) and deletes all of the user's refresh tokens, so neither the access token nor any refresh token can be used again.
Notes
- Requires a valid bearer token (
auth:api). - After logout, the client should discard both the access and refresh tokens; continuing to use them returns
401 Unauthorized. - Idempotent from the client's perspective — calling it again after logout simply fails auth.
{
"status": 200,
"data": [],
"message": "Successfully logged out"
}
Roles
GET List Roles Roles {{url}}/roles ▸
This endpoint returns an array of role objects, each representing a user role within the system. It is typically used to display available roles for user management or assignment purposes.
{
"data": [
{
"id": "184ad925-a873-445e-a140-1edc1b11c2db",
"name": "System Admin",
"created_at": "2025-11-27 13:35:56"
},
{
"id": "582c5dc9-4ef6-4238-98e3-7318c57349fb",
"name": "Parent",
"created_at": "2025-11-27 13:35:56"
},
{
"id": "989d4025-e939-41f7-b032-356c7269612c",
"name": "Staff",
"created_at": "2025-11-27 13:35:56"
},
{
"id": "4583792c-7951-4bf5-ba5f-68c374a12420",
"name": "Administrative Staff",
"created_at": "2025-11-27 13:35:56"
}
],
"links": {
"first": "https://terragostg.terrasofthq.com/api/roles?page=1",
"last": "https://terragostg.terrasofthq.com/api/roles?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "https://terragostg.terrasofthq.com/api/roles?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "https://terragostg.terrasofthq.com/api/roles",
"per_page": 15,
"to": 4,
"total": 4
},
"status": 200,
"message": "Roles Retrieved Successfully"
}
Users
POST Signup Users {{url}}/users/sign-up ▸
Public self-registration — lets a new user create their own TerraGo account without being authenticated (no bearer token required). Supports roles such as PARENT and SCHOOL. Runs the shared CreateAccount action.
Request Body (common fields)
- firstname, lastname (
string, required); middle_name (string, optional). - email (
string, required), phone (string, required, e.g.+254733556677). - identificationdocument / identificationnumber (
string): required for role PARENT. - dob (
string,DD-MM-YYYY), gender (MALE|FEMALE|OTHER|N/A). - role_id (
stringUUID, required). - School fields (school, school_type, location) for a school signup.
Behavior
- The account is created active and email-verified.
Notes
- This is the unauthenticated counterpart to Register. Use Register when an admin creates the account; use Signup for self-service.
- Unlike Register, it does not refresh a supervisor's onboarding setup status.
Response
The newly created user (data) — same shape as Register.
{
"first_name": "Kevin",
"middle_name": "Mwangi",
"last_name": "Kariuki",
"email": "kevin.kariuki@example.com",
"phone": "+254733556677",
"identification_document": "national_id", //(required) for role PARENT
"identification_number": "49283746", //(required) for role PARENT
"dob": "22-03-1998",
"gender": "MALE", //only use: MALE, FEMALE, OTHER, N/A :: //(required) for role PARENT
"school": "Greenwood International School", //optional
"location": "Nakuru, Kenya", //optional
"school_type": "INTERNATIONAL", //(optional) only use: PRIMARY_SCHOOL, SECONDARY_SCHOOL, HIGH_SCHOOL, KINDERGARTEN, INTERNATIONAL, N/A
"role_id": "582c5dc9-4ef6-4238-98e3-7318c57349fb",
"supervisor_id": "7b92e1e0-f73c-4c68-8873-ada9182dd3cc", //administrative staff id, required for creating staff i.e drivers/bus minders
"password": "Password@2025", //optional
"password_confirmation": "Password@2025"
}{
"status": 200,
"data": {
"id": "cf7a3821-e6be-490f-85b9-6f7ca3585aad",
"first_name": "Kevin",
"middle_name": "Mwangi",
"last_name": "Kariuki",
"phone": "+254733556677",
"email": "kevin.kariuki@example.com",
"identification_document": "national_id",
"identification_number": "49283746",
"dob": "1998-03-22 00:00:00",
"gender": "Male",
"third_party_id": "24521db0-7a8d-45fa-9306-d9efc8a8e8aa",
"is_active": true,
"role": "Parent",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-12-05 12:47:21"
},
"message": "Account Created Successfully"
}
GET List Users {{url}}/users ▸
Retrieve a paginated list of all users in the system, across every role, with optional filtering and search. Primarily an admin/back-office listing endpoint.
Query Parameters
- per_page (
integer, optional, default 10, max 20): Page size. - page (
integer, optional): Page number. - is_active (
boolean, optional): Filter by active (1) / inactive (0). - role_id (
stringUUID, optional): Filter to a single role (matched by the role's UUID). - search (
string, optional): Matches against full name, email, institution, or phone.
Response
Paginated collection of users (UserResource).
Notes
- Unlike
GET /users/staffandGET /users/parents, this is not role-scoped — it returns users of every role. - Requires a bearer token (
auth:api).
{
"data": [
{
"id": "a0dceac9-cf75-41fd-aed3-608510ade207",
"first_name": "System",
"middle_name": null,
"last_name": "Admin",
"phone": null,
"email": "admin@application.com",
"identification_document": null,
"identification_number": null,
"dob": "2025-12-03 22:10:21",
"gender": null,
"third_party_id": null,
"is_active": true,
"role": "System Admin",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-11-27 13:35:56"
},
{
"id": "0e02c849-6f36-4fb9-81d3-64a22908ee5a",
"first_name": "Collins",
"middle_name": null,
"last_name": "Muriuki",
"phone": null,
"email": "muriukicollins@gmail.com",
"identification_document": null,
"identification_number": null,
"dob": "2025-12-03 22:10:21",
"gender": null,
"third_party_id": null,
"is_active": true,
"role": "System Admin",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-11-27 16:05:01"
},
{
"id": "95db0ec4-83ab-4a7a-aff4-383e738e8d34",
"first_name": "LaParent",
"middle_name": null,
"last_name": "Ente",
"phone": "+254706347307",
"email": "laparent@gmail.com",
"identification_document": "national_id",
"identification_number": "40123123",
"dob": "1995-11-06 00:00:00",
"gender": "Male",
"third_party_id": "cccf3a09-62e2-4026-947d-97aedfa6d6a7",
"is_active": true,
"role": "Parent",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-12-03 01:14:03"
},
{
"id": "c31727b0-0130-46f0-92f2-d2b0bec124ab",
"first_name": "LaParent",
"middle_name": null,
"last_name": "Ente",
"phone": "+2547063473076",
"email": "laparent12@gmail.com",
"identification_document": "passport",
"identification_number": "401231239",
"dob": "1995-11-06 00:00:00",
"gender": "Male",
"third_party_id": "f8af6128-9545-41d9-94bc-94bfacc78e66",
"is_active": true,
"role": "Parent",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-12-03 02:00:19"
},
{
"id": "472df951-54eb-4b3b-b224-105d8b9428b4",
"first_name": "Likg",
"middle_name": "K",
"last_name": "Last",
"phone": "+254794237685",
"email": "ling12@gmail.com",
"identification_document": "passport",
"identification_number": "123456712",
"dob": "1996-12-03 00:00:00",
"gender": "Male",
"third_party_id": "83be7d0b-389c-4129-a0f3-3dbf45ba58b6",
"is_active": true,
"role": "Parent",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-12-03 02:09:26"
},
{
"id": "9dcc2d36-5297-4c26-98db-660af8e00909",
"first_name": "Le",
"middle_name": null,
"last_name": "Parent",
"phone": "+254794237684",
"email": "ling@gmqil.com",
"identification_document": "national_id",
"identification_number": "12312345",
"dob": "1998-12-03 00:00:00",
"gender": "Male",
"third_party_id": "f92de02b-f29d-4a46-a442-313fbec8faa7",
"is_active": true,
"role": "Parent",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-12-03 02:16:38"
},
{
"id": "c011772f-7677-4b6b-bd6e-21a2ba6d1841",
"first_name": "Francis",
"middle_name": "Njenga",
"last_name": "Chege",
"phone": "+254722626879",
"email": "fchege2015ios@gmail.com",
"identification_document": "national_id",
"identification_number": "36898912",
"dob": "2000-11-06 00:00:00",
"gender": "Male",
"third_party_id": "ddab60b7-cab6-4ff7-82ef-c8a2fe31698f",
"is_active": true,
"role": "Staff",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-12-03 12:58:11"
}
],
"links": {
"first": "https://terragostg.terrasofthq.com/api/users?page=1",
"last": "https://terragostg.terrasofthq.com/api/users?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "https://terragostg.terrasofthq.com/api/users?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "https://terragostg.terrasofthq.com/api/users",
"per_page": 15,
"to": 7,
"total": 7
},
"status": 200,
"message": "Accounts Retrieved Successfully"
}
GET List Parents Users {{url}}/users/parents ▸
Retrieve a paginated list of Parent users, including how many students (children) each parent has.
Query Parameters
- per_page (
integer, optional, default 10, max 20) - page (
integer, optional) - is_active (
boolean, optional) - supervisor_id (
stringUUID, optional): Filter to parents belonging to a specific school/Administrative Staff. - sourcechannel (
string, optional): How the parent signed up —whatsapp,schoolcreation, oradmin_upload. - search (
string, optional): Matches name, email, or phone; supports multi-word search.
Response
Paginated list of parents, each including a dependants_count field.
Notes
- Only returns users whose role is
Parent. - Requires a bearer token (
auth:api).
{
"data": [
{
"id": "95db0ec4-83ab-4a7a-aff4-383e738e8d34",
"first_name": "LaParent",
"middle_name": null,
"last_name": "Ente",
"phone": "+25470000001",
"email": "laparent@gmail.com",
"identification_document": "national_id",
"identification_number": "40123123",
"dob": "1995-11-06 00:00:00",
"gender": "Male",
"third_party_id": "cccf3a09-62e2-4026-947d-97aedfa6d6a7",
"is_active": 1,
"role": "Parent",
"institution": "Alliance High School",
"institution_type": "High School",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": "Nairobi, Kenya",
"created_at": "2025-12-03 04:14:03",
"dependants_count": 3
},
{
"id": "c31727b0-0130-46f0-92f2-d2b0bec124ab",
"first_name": "LaParent",
"middle_name": null,
"last_name": "Ente",
"phone": "+2547063473076",
"email": "laparent12@gmail.com",
"identification_document": "passport",
"identification_number": "401231239",
"dob": "1995-11-06 00:00:00",
"gender": "Male",
"third_party_id": "f8af6128-9545-41d9-94bc-94bfacc78e66",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-03 05:00:19",
"dependants_count": 2
},
{
"id": "472df951-54eb-4b3b-b224-105d8b9428b4",
"first_name": "Likg",
"middle_name": "K",
"last_name": "Last",
"phone": "+254794237685",
"email": "ling12@gmail.com",
"identification_document": "passport",
"identification_number": "123456712",
"dob": "1996-12-03 00:00:00",
"gender": "Male",
"third_party_id": "83be7d0b-389c-4129-a0f3-3dbf45ba58b6",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-03 05:09:26",
"dependants_count": 0
},
{
"id": "9dcc2d36-5297-4c26-98db-660af8e00909",
"first_name": "Le",
"middle_name": null,
"last_name": "Parent",
"phone": "+254700000111",
"email": "ling@gmqil.com",
"identification_document": "national_id",
"identification_number": "12312345",
"dob": "1998-12-03 00:00:00",
"gender": "Male",
"third_party_id": "f92de02b-f29d-4a46-a442-313fbec8faa7",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-03 05:16:38",
"dependants_count": 3
},
{
"id": "418ebdb0-79db-4b57-99ab-7a54cfb3a365",
"first_name": "Likg",
"middle_name": "K",
"last_name": "Last",
"phone": "+254700000000",
"email": "ling@gmail.com",
"identification_document": "passport",
"identification_number": "12345678",
"dob": "1996-12-03 00:00:00",
"gender": "Male",
"third_party_id": "6b5e8655-d17c-474a-abd3-08008b3eec67",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-05 02:08:18",
"dependants_count": 0
},
{
"id": "cf7a3821-e6be-490f-85b9-6f7ca3585aad",
"first_name": "Kevin",
"middle_name": "Mwangi",
"last_name": "Kariuki",
"phone": "+254733556677",
"email": "kevin.kariuki@example.com",
"identification_document": "national_id",
"identification_number": "49283746",
"dob": "1998-03-22 00:00:00",
"gender": "Male",
"third_party_id": "24521db0-7a8d-45fa-9306-d9efc8a8e8aa",
"is_active": 1,
"role": "Parent",
"institution": "Greenwood International School",
"institution_type": "International",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": "Nakuru, Kenya",
"created_at": "2025-12-05 15:47:21",
"dependants_count": 1
},
{
"id": "912823ed-97e9-4b0c-b4d4-34423726426c",
"first_name": "Likg",
"middle_name": "K",
"last_name": "Last",
"phone": "+254700000099",
"email": "ling19@gmail.com",
"identification_document": "passport",
"identification_number": "12345679",
"dob": "1996-12-03 00:00:00",
"gender": "Male",
"third_party_id": "5416b2a1-3b7e-4999-8a27-76e8e6c2a242",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-09 18:45:47",
"dependants_count": 0
},
{
"id": "2f41e784-9fe4-4bb3-9cae-c2592d8d0d6c",
"first_name": "Linng",
"middle_name": null,
"last_name": "Ladt",
"phone": "+2547063473071",
"email": "blackling20+0011@gmail.com",
"identification_document": "passport",
"identification_number": "565677461",
"dob": "1996-12-09 00:00:00",
"gender": "Male",
"third_party_id": "e1c51209-0107-4eeb-9ef0-44a2357583a9",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-09 19:11:03",
"dependants_count": 1
},
{
"id": "81ed76e7-bd5c-4cba-90d9-cffa34aa8dae",
"first_name": "Kip",
"middle_name": null,
"last_name": "Ling",
"phone": "+2547942376841",
"email": "blackling20+00041@gmail.com",
"identification_document": "passport",
"identification_number": "251737281",
"dob": "1995-12-11 00:00:00",
"gender": "Male",
"third_party_id": "d12ec6cd-faa0-4a58-893c-1f9b4132f372",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": {
"assigned_zone": null
},
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-11 15:15:18",
"dependants_count": 8
},
{
"id": "a92b3004-4628-4ae4-9621-35ebe372ab47",
"first_name": "PM",
"middle_name": null,
"last_name": "Testing",
"phone": "+254793453453",
"email": "tessdufysdt@gmail.com",
"identification_document": "national_id",
"identification_number": "5435348",
"dob": "1996-12-11 00:00:00",
"gender": "Female",
"third_party_id": "5c661a32-b115-453d-a7ac-801a05ecb958",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-11 17:06:29",
"dependants_count": 3
},
{
"id": "35438f0b-b488-4ea0-b69d-73e214910ab9",
"first_name": "Erick",
"middle_name": null,
"last_name": "Mwinyi",
"phone": "+254784976344",
"email": "mwinyierick@gmail.com",
"identification_document": "national_id",
"identification_number": "25252525",
"dob": "2000-12-11 00:00:00",
"gender": "Male",
"third_party_id": "b869289d-16f0-4045-bad5-a241286e5d0d",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-11 17:27:18",
"dependants_count": 1
},
{
"id": "aafdc950-0a95-467a-8cc3-0ee8150c9c45",
"first_name": "Meso",
"middle_name": null,
"last_name": "Wanjiru",
"phone": "+254264263674",
"email": "meso@gmail.com",
"identification_document": "NATIONAL_ID",
"identification_number": "24376462",
"dob": "2002-01-07 00:00:00",
"gender": "Female",
"third_party_id": "584162fb-695c-4d2b-b8e4-147104fd383e",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": {
"assigned_zone": null
},
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-25 21:42:04",
"dependants_count": 0
},
{
"id": "23a1ee6e-0e74-4690-acdc-9c93a8207186",
"first_name": "Wafula",
"middle_name": null,
"last_name": "Jacob",
"phone": "+254713420225",
"email": "wafula.jacob@ymail.com",
"identification_document": "national_id",
"identification_number": "28436278",
"dob": "1989-12-06 00:00:00",
"gender": "Male",
"third_party_id": "818015aa-265a-4e67-b183-8bb7018ffa8c",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": {
"assigned_zone": null
},
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-28 14:43:36",
"dependants_count": 0
},
{
"id": "4c46ea3b-ec18-4339-9ab9-a8ce44c4dad7",
"first_name": "Mat",
"middle_name": "Nyaga",
"last_name": "Kabinga",
"phone": "+254720210128",
"email": "matnyaga@gmail.com",
"identification_document": "national_id",
"identification_number": "27274494",
"dob": "1989-06-16 00:00:00",
"gender": "Male",
"third_party_id": "b6de7f48-fc42-4745-a00b-b5935ec17ba0",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": {
"assigned_zone": null
},
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-28 18:24:55",
"dependants_count": 0
},
{
"id": "916c8cb7-5aae-4e97-bce6-b3172063a547",
"first_name": "Leparentw",
"middle_name": null,
"last_name": "Kipli",
"phone": "+254706347307",
"email": "blackling206@gmail.com",
"identification_document": "national_id",
"identification_number": "62283585",
"dob": "1997-12-29 00:00:00",
"gender": "Male",
"third_party_id": "d6cbf2c0-1ddb-4168-89da-70068d573d55",
"is_active": 1,
"role": "Parent",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": {
"assigned_zone": null
},
"image": null,
"notes": null,
"location": null,
"created_at": "2025-12-29 20:37:01",
"dependants_count": 14
}
],
"links": {
"first": "http://127.0.0.1:8000/api/users/parents?page=1",
"last": "http://127.0.0.1:8000/api/users/parents?page=37",
"prev": null,
"next": "http://127.0.0.1:8000/api/users/parents?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 37,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=2",
"label": "2",
"page": 2,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=3",
"label": "3",
"page": 3,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=4",
"label": "4",
"page": 4,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=5",
"label": "5",
"page": 5,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=6",
"label": "6",
"page": 6,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=7",
"label": "7",
"page": 7,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=8",
"label": "8",
"page": 8,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=9",
"label": "9",
"page": 9,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=10",
"label": "10",
"page": 10,
"active": false
},
{
"url": null,
"label": "...",
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=36",
"label": "36",
"page": 36,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=37",
"label": "37",
"page": 37,
"active": false
},
{
"url": "http://127.0.0.1:8000/api/users/parents?page=2",
"label": "Next »",
"page": 2,
"active": false
}
],
"path": "http://127.0.0.1:8000/api/users/parents",
"per_page": 15,
"to": 15,
"total": 544
},
"status": 200,
"message": "Parents Accounts Retrieved Successfully"
}
GET Get Parent by ID Users {{url}}/users/parents/95db0ec4-83ab-4a7a-aff4-383e738e8d34 ▸
Retrieve a single Parent user along with their students (children).
Path Parameters
- id (
stringUUID, required): The parent's user UUID.
Response
Returns the parent's profile plus a students array (id, name fields, image, metadata, active status, third-party/the registry ID).
Notes
- Returns 404 if the user doesn't exist or isn't a Parent.
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": {
"id": "95db0ec4-83ab-4a7a-aff4-383e738e8d34",
"first_name": "LaParent",
"middle_name": null,
"last_name": "Ente",
"phone": "+25470000001",
"email": "laparent@gmail.com",
"identification_document": "national_id",
"identification_number": "40123123",
"dob": "1995-11-06 00:00:00",
"gender": "Male",
"third_party_id": "cccf3a09-62e2-4026-947d-97aedfa6d6a7",
"is_active": 1,
"role": "Parent",
"institution": "Alliance High School",
"institution_type": "High School",
"staff_type": null,
"supervisor_id": null,
"addresses": [],
"metadata": [],
"image": null,
"notes": null,
"location": "Nairobi, Kenya",
"created_at": "2025-12-03 04:14:03",
"dependants": [
{
"id": "c75f2355-2f88-40b2-916b-999744d1d1f5",
"first_name": "Lincoln",
"last_name": "Chege",
"is_active": 1,
"metadata": {
"grade": "9th Grade"
}
},
{
"id": "2b37465e-2cc1-4b2f-b702-611505702c7f",
"first_name": "Leo",
"last_name": "Junior",
"is_active": 0,
"metadata": {
"grade": "PP1"
}
},
{
"id": "9474fd38-dce1-4abc-b4a9-946dc5d841bc",
"first_name": "Michael",
"last_name": "Kid",
"is_active": 1,
"metadata": {
"grade": "PP1"
}
}
]
},
"message": "Parent Retrieved Successfully"
}
PUT Update Users {{url}}/users/update/448e2ae4-48b4-4842-84b1-a73c84d21de1 ▸
Purpose: This endpoint updates an existing user's profile information. It is used to modify user details such as name, contact information, identification, and role assignments.
Request Body: Send a JSON object with the following fields:
first_name(string, required): User's first name.middle_name(string, required): User's middle name.last_name(string, required): User's last name.email(string, required): User's email address.phone(string, required): User's phone number (e.g., +254713452890).identificationdocument(string, required): Type of identification document (e.g., "nationalid").identification_number(string, required): Identification document number.dob(string, required): Date of birth (format: DD-MM-YYYY).gender(string, required): Gender of the user. Allowed values:MALE,FEMALE,OTHER,N/A.school(string, optional): Name of the user's school.location(string, optional): User's location (e.g., city, country).schooltype(string, optional): Type of school. Allowed values:PRIMARYSCHOOL,SECONDARYSCHOOL,HIGHSCHOOL,KINDERGARTEN,INTERNATIONAL,N/A.role_id(string, required): Role identifier for the user.beneficiary_id(string, required for staff): Beneficiary identifier. Required when creating staff (e.g., drivers, bus minders).
Expected Response:
- On success, returns a JSON object with the updated user profile and a status code of 200 OK.
- On error, returns an error message and the appropriate HTTP status code (e.g., 400 for validation errors).
Notes:
- Ensure all required fields are provided.
- Use only the allowed values for
genderandschool_type. - The endpoint URL requires the user's unique ID as a path parameter.
References:
{
"first_name" : "Alex",
"middle_name": "Kimani",
"last_name" : "Maina",
"email": "alex.kimani@example.com",
"phone" : "+254713452890",
"identification_document": "national_id",
"identification_number": "42876543",
"dob":"14-09-1998",
"gender": "MALE", //only use: MALE, FEMALE, OTHER, N/A
"school": "Ridgeways Academy", //optional
"location": "Thika, Kenya", //optional
"school_type": "KINDERGARTEN", //(optional) only use: PRIMARY_SCHOOL, SECONDARY_SCHOOL, HIGH_SCHOOL, KINDERGARTEN, INTERNATIONAL, N/A
"metadata" : {
"assigned_zone" : {
"id": "ada52fc6-d481-48d9-82b3-fddb07d5d44f",
"name": "Terra Hq",
"zone_type": null,
"description": "nnn",
"main_zone": null,
"is_sub_zone": 0,
"tags": [],
"whitelist_tags": []
}
}
}{
"status": 200,
"data": {
"id": "448e2ae4-48b4-4842-84b1-a73c84d21de1",
"first_name": "Alex",
"middle_name": "Kimani",
"last_name": "Maina",
"phone": "+254713452890",
"email": "alex.kimani@example.com",
"identification_document": "national_id",
"identification_number": "42876543",
"dob": "1998-09-14 00:00:00",
"gender": "Male",
"third_party_id": "503804f8-6a9b-495c-adb2-91e8228febbd",
"is_active": true,
"role": "Administrative Staff",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-12-04 12:33:26"
},
"message": "Account Updated Successfully"
}
Students
GET List Students {{url}}/dependants ▸
Retrieves a paginated list of students filtered by optional criteria such as tags, parent ID, or phone number. This endpoint is useful for applications that need to display or manage students associated with a parent entity (such as a user or account).
Parameters
- tags (
array[string], optional): List of tag UUIDs to filter students.
- parent_id (
string, optional): UUID of the parent entity to filter students.
- phone_number (
string, optional): Phone number to filter students.
- per_page (
integer, optional): Number of results per page. Default is 10.
- page (
integer, optional): Page number to retrieve. Default is 1.
Notes
- All filter parameters are optional; omitting them returns all students.
- Ensure the
tokenenvironment variable is set with a valid bearer token.
- The endpoint may return an empty array if no students match the filters.
{
"tags": ["d4b18b8f-eaa6-44e1-8655-d75f146c0933"], // filter by tag
// "parent_id": "cf7a3821-e6be-490f-85b9-6f7ca3585aad", // filter by parent id
// "phone_number": "+254733556677", // filter by phone number
"per_page": 10,
"page": 1
}{
"data": [
{
"id": "184c32e2-878d-4e89-99c3-91dda70c03e9",
"first_name": "Simon",
"middle_name": "Kimani",
"last_name": "Chege",
"phone": null,
"email": null,
"identification_document": null,
"identification_number": null,
"dob": "2010-11-06 00:00:00",
"gender": "Male",
"third_party_id": "a2fce066-519c-4cba-968d-18aa70446411",
"is_active": 1,
"metadata": {
"grade": null
},
"parent": {
"uuid": "cf7a3821-e6be-490f-85b9-6f7ca3585aad",
"name": "Kevin Kariuki",
"email": "kevin.kariuki@example.com",
"phone": "+254733556677"
},
"guardians": [
{
"uuid": "d651190e-5e68-43e0-82db-9c53426311ab",
"name": "Peter Chege"
}
],
"image": null,
"notes": null,
"created_at": "2025-12-05 15:51:29",
"addresses": [
{
"uuid": "93d41cd9-1b01-4bb3-95ce-56175ce7f493",
"is_primary": 1,
"latitude": "-1.2921",
"longitude": "36.8219"
}
],
"tags": [
{
"id": "d4b18b8f-eaa6-44e1-8655-d75f146c0933",
"name": "PM School Tag",
"description": "PM School Tag",
"entity": "universal",
"color_code": "#EA0D8D"
}
]
},
{
"id": "0b1475e9-f811-4683-8a90-a94468dc7ef1",
"first_name": "Junior",
"middle_name": null,
"last_name": "Pm",
"phone": "",
"email": null,
"identification_document": null,
"identification_number": null,
"dob": "2005-01-05 00:00:00",
"gender": "Female",
"third_party_id": "2b8effd8-0782-4e66-b93f-d0ba8c4464c9",
"is_active": 1,
"metadata": {
"grade": "8th Grade"
},
"parent": {
"uuid": "d23b811d-368c-44fd-a031-6663bed28fc0",
"name": "Pm Parent",
"email": "Parent@gmail.com",
"phone": "+254791472893"
},
"guardians": [
{
"uuid": "41e5db5b-2ab2-4d8a-a8e2-6fe3fdaca078",
"name": "Nanny Guardian"
},
{
"uuid": "4d95c454-67aa-47ce-80c3-973f0d3750cb",
"name": "Maggy Grants"
}
],
"image": null,
"notes": null,
"created_at": "2026-01-05 14:26:56",
"addresses": [],
"tags": [
{
"id": "d4b18b8f-eaa6-44e1-8655-d75f146c0933",
"name": "PM School Tag",
"description": "PM School Tag",
"entity": "universal",
"color_code": "#EA0D8D"
},
{
"id": "0655ca5b-1c6b-437d-b38a-7f4596fb038f",
"name": "Kiota School Rongai Route",
"description": "Kiota School Rongai Route for students",
"entity": "dependant",
"color_code": null
}
]
}
],
"links": {
"first": "http://127.0.0.1:8001/api/dependants?page=1",
"last": "http://127.0.0.1:8001/api/dependants?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "http://127.0.0.1:8001/api/dependants?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "http://127.0.0.1:8001/api/dependants",
"per_page": 10,
"to": 2,
"total": 2
},
"status": 200,
"message": "Dependants Retrieved Successfully"
}
POST Add Students {{url}}/dependants/add ▸
This endpoint creates a new student record associated with a parent user.
Sample Fields
first_name(string): Student's first name.
middle_name(string): Student's middle name.
last_name(string): Student's last name.
dob(string): Date of birth (format:DD-MM-YYYY).
gender(string): Gender of the student. Allowed values:MALE,FEMALE,OTHER,N/A.
parent_id(string): Unique identifier of the parent user.
Response
The created student (data) — id (uuid), names, grade, parent, guardians, addresses.
{
"first_name" : "Simon",
"middle_name": "Kimani",
"last_name" : "Chege",
"dob":"06-11-2010",
"gender": "MALE", //only use: MALE, FEMALE, OTHER, N/A
"parent_id" : "cf7a3821-e6be-490f-85b9-6f7ca3585aad",
"metadata" : {
"grade" : "Grade 6" //only use: PP1, PP2, PLAYGROUP, GRADE_1 , GRADE_2, GRADE_3, GRADE_4, GRADE_5, GRADE_6, GRADE_7, GRADE_8, GRADE_9, GRADE_10, GRADE_11, GRADE_12, COLLEGE, N/A,
},
"tags" : ["cf7a3821-e6be-490f-85b9-6f7ca3585aad", "cf7a3821-e6be-490f-85b9-6f7ca3585aad"],
"search_term": "Malaika School Thika Road" //optional for the search term used by the parent to find the dependants institution
}{
"status": 200,
"data": {
"id": "184c32e2-878d-4e89-99c3-91dda70c03e9",
"first_name": "Simon",
"middle_name": "Kimani",
"last_name": "Chege",
"phone": null,
"email": null,
"identification_document": null,
"identification_number": null,
"dob": "2010-11-06 00:00:00",
"gender": "Male",
"third_party_id": "a2fce066-519c-4cba-968d-18aa70446411",
"is_active": true,
"metadata": {
"grade": null
},
"image": null,
"notes": null,
"created_at": "2025-12-05 12:51:29"
},
"message": "Dependant Created Successfully"
}
PUT Update Students {{url}}/dependants/update/30a250fe-0cbe-4afb-bb48-606e5d8f887f ▸
This endpoint updates the details of an existing student identified by their unique ID in the URL path.
Sample Body Fields:
first_name(string, required): Student's first name. Example: "Lincoln"
middle_name(string, optional): Student's middle name. Example: "Hungi"
last_name(string, required): Student's last name. Example: "Chege"
dob(string, required): Date of birth inDD-MM-YYYYformat. Example: "06-11-2010"
gender(string, required): Gender of the student. Allowed values:MALE,FEMALE,OTHER,N/A
parent_id(string, required): UUID of the parent.
metadata(object, optional): Additional information. Example:{ "grade": "Grade 6" }
Response
The updated student (data).
{
"first_name" : "Lincoln",
"middle_name": "Hungi",
"last_name" : "Chege",
"dob":"06-11-2010",
"gender": "MALE", //only use: MALE, FEMALE, OTHER, N/A
"parent_id" : "b3b62c15-1409-4713-81b2-d4f6744039e6",
"metadata" : {
"grade" : "Grade 6" //only use: PP1, PP2, PLAYGROUP, GRADE_1 , GRADE_2, GRADE_3, GRADE_4, GRADE_5, GRADE_6, GRADE_7, GRADE_8, GRADE_9, GRADE_10, GRADE_11, GRADE_12, COLLEGE, N/A,
},
"tags" : ["cf7a3821-e6be-490f-85b9-6f7ca3585aad", "cf7a3821-e6be-490f-85b9-6f7ca3585aad"],
"search_term": "Malaika School Thika Road" //optional for the search term used by the parent to find the dependants institution
}{
"status": 200,
"data": {
"id": "30a250fe-0cbe-4afb-bb48-606e5d8f887f",
"first_name": "Lincoln",
"middle_name": "Hungi",
"last_name": "Chege",
"phone": null,
"email": null,
"identification_document": null,
"identification_number": null,
"dob": "2010-11-06 00:00:00",
"gender": "Male",
"third_party_id": "6e09a88f-de47-455b-a1bd-dc83a6e8efc6",
"is_active": true,
"metadata": {
"grade": "Grade 6"
},
"image": null,
"notes": null,
"created_at": "2025-11-21 11:07:08"
},
"message": "Dependant Updated Successfully"
}
GET Show Students {{url}}/dependants/184c32e2-878d-4e89-99c3-91dda70c03e9 ▸
Retrieve a single student (child) by UUID with their full context, enriched from the registry.
Path Parameters
- id (
stringUUID, required): The student's UUID.
Response includes
- Local relationships: parent, guardians, and addresses.
- the registry enrichment: the student's live the registry record is fetched and merged in, providing route/zone/location tags and assigned Band/Bag Tags devices (mapped onto the response).
Notes
- Returns
404if no student matches the UUID. - If the registry is unreachable the local data is still returned; the the registry-derived tags/IOTs are simply omitted.
- Requires a bearer token (
auth:api).
{
"data": {
"id": "184c32e2-878d-4e89-99c3-91dda70c03e9",
"first_name": "Simon",
"middle_name": "Kimani",
"last_name": "Chege",
"phone": null,
"email": null,
"identification_document": null,
"identification_number": null,
"dob": "2010-11-06 00:00:00",
"gender": "Male",
"third_party_id": "a2fce066-519c-4cba-968d-18aa70446411",
"is_active": 1,
"metadata": {
"grade": null
},
"parent": {
"uuid": "cf7a3821-e6be-490f-85b9-6f7ca3585aad",
"name": "Kevin Kariuki",
"email": "kevin.kariuki@example.com",
"phone": "+254733556677"
},
"guardians": [
{
"uuid": "d651190e-5e68-43e0-82db-9c53426311ab",
"name": "Peter Chege"
}
],
"image": null,
"notes": null,
"created_at": "2025-12-05 15:51:29",
"addresses": [
{
"uuid": "93d41cd9-1b01-4bb3-95ce-56175ce7f493",
"is_primary": 1,
"latitude": "-1.2921",
"longitude": "36.8219"
},
{
"uuid": "da879dcb-b062-462e-b935-5c1b879e3c0d",
"is_primary": 1,
"latitude": "-1.2921",
"longitude": "36.8219"
}
]
},
"status": 200,
"message": "Dependant Retrieved Successfully"
}
DELETE Delete Students {{url}}/dependants/delete/c3d4e5f6-0000-4a11-8888-abcde1234567 ▸
Delete a student (dependant) record.
Notes
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": [],
"message": "Dependant Deleted Successfully"
}
Tags
GET List Tags {{url}}/utility/tags ▸
List beneficiary/route tags available for the organisation. This is read directly from TerraGo's registry — TerraGo forwards the query to the registry, then re-paginates the result so the pagination links point back at TerraGo (not the registry).
Query Parameters
- search (
string, optional): Filter tags by text. - entity (
string, optional): Entity scope (combined with&universalserver-side). - per_page (
integer, optional, default 10, max 20), page (integer, optional).
Response
Paginated collection of tags (TagResource).
Notes
- Depends on the registry; if the registry is unreachable or errors, returns a warning ("Tag Fetching Failed due to WAAS failure").
- Requires a bearer token (
auth:api).
{
"per_page": 10, // Page size — how many results per page
"page": 1 // Page number (starts at 1)
}{
"data": [
{
"id": "323c3700-6a64-4b17-acc5-baf7f4f9dc11",
"name": "School Z",
"description": "desc",
"entity": "zone",
"color_code": "#FF9500"
},
{
"id": "fc49ed94-1521-49cf-9314-99639ec8757c",
"name": "Carrefour Employee",
"description": "Account Tag for carrefour employee",
"entity": "account",
"color_code": "#EA0D8D"
},
{
"id": "036ad366-7a0a-46b1-8eb3-8dcec63c7589",
"name": "Carrefour Store",
"description": "Carrefour Stores",
"entity": "beneficiary",
"color_code": "#FF9500"
},
{
"id": "c50eb33d-822e-4409-9196-9867114bf2c2",
"name": "Carrefour Customer",
"description": "Tag to describe functions for carrefour Customer",
"entity": "account",
"color_code": "#EA0D8D"
},
{
"id": "a6cc8f59-4b10-45fa-b87d-17dd04438bad",
"name": "Carrefour Embakasi Zone",
"description": "Carrefour Embakasi Zone Tag",
"entity": "zone",
"color_code": "#FF9500"
},
{
"id": "8ec1a686-5c08-4e82-93c6-9bd082605860",
"name": "BEN WA FISHARY TAG",
"description": null,
"entity": "account",
"color_code": "#FF9500"
},
{
"id": "b85861df-eb37-4b3d-980a-0de36c3a5085",
"name": "Marcus",
"description": "marcus",
"entity": "dependant",
"color_code": "#FF9500"
},
{
"id": "d625d7bc-81a4-4815-ab0d-b288e47e7d28",
"name": "Dependant Backstage Access",
"description": "Dependant Backstage Access",
"entity": "dependant",
"color_code": "#EA0D8D"
},
{
"id": "415e9fc8-4206-4dd9-8de7-484dd23a83fa",
"name": "Backstage Access",
"description": "Backstage Access",
"entity": "account",
"color_code": "#FF9500"
},
{
"id": "82556e35-4593-4fba-9ba5-df73bcb27b61",
"name": "BACKSTAGE ACCESS",
"description": "BACKSTAGE ACCESS",
"entity": "zone",
"color_code": "#9351E8"
}
],
"links": {
"first": "http://localhost:8001/api/tags?page=1",
"last": "http://localhost:8001/api/tags?page=5",
"prev": null,
"next": "http://localhost:8001/api/tags?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 5,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "http://localhost:8001/api/tags?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": "http://localhost:8001/api/tags?page=2",
"label": "2",
"page": 2,
"active": false
},
{
"url": "http://localhost:8001/api/tags?page=3",
"label": "3",
"page": 3,
"active": false
},
{
"url": "http://localhost:8001/api/tags?page=4",
"label": "4",
"page": 4,
"active": false
},
{
"url": "http://localhost:8001/api/tags?page=5",
"label": "5",
"page": 5,
"active": false
},
{
"url": "http://localhost:8001/api/tags?page=2",
"label": "Next »",
"page": 2,
"active": false
}
],
"path": "http://localhost:8001/api/tags",
"per_page": 10,
"to": 10,
"total": 41
},
"status": 200,
"message": "Tags Retrieved Successfully"
}
Devices
GET List Devices Devices {{url}}/devices ▸
Retrieve a paginated list of hardware devices (trackers, scanners, gateways) registered with TerraGo.
Query Parameters
- per_page (
integer, optional, default 10, max 20) - status (
string, optional):ONLINE,OFFLINE,LOWBATTERY,PENDINGAPPROVAL,OTHER, orN/A. - assigned (
integer, optional):1for devices assigned to a school/staff,0for unassigned. - supervisor_id (
stringUUID, optional): Filter by Administrative Staff (school). - search (
string, optional): Matches name, description, or serial number.
Response
Paginated list of devices (data) with meta; each includes live-derived status, battery, NFC/location flags, signal and data usage.
{
"status": "ONLINE", //only use: ONLINE, OFFLINE, LOW_BATTERY, OTHER, N/A
"search": "",
"supervisor_id": "68b86410-e6e8-4b5f-9a3d-8265a7773c0e",
"per_page": 10,
"page": 1
}{
"data": [
{
"id": "e91c072b-508c-448d-a368-f22c34434742",
"name": "name",
"description": "description",
"sim_phone_number": "+254725544432",
"serial_number": "serial_number",
"waas_zone_uuid": "ada52fc6-d481-48d9-82b3-fddb07d5d44f",
"waas_zone_name": "Terra HQ",
"est_data_usage": "2000",
"type": "Scanner",
"administrative_staff": {
"id": "68b86410-e6e8-4b5f-9a3d-8265a7773c0e",
"first_name": "Jane",
"last_name": "Otieno"
},
"status": "Online",
"last_activity": "2025-12-27 14:10:56",
"battery_level": 100,
"metadata": null
}
],
"links": {
"first": "http://localhost:8001/api/devices?page=1",
"last": "http://localhost:8001/api/devices?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "http://localhost:8001/api/devices?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "http://localhost:8001/api/devices",
"per_page": 15,
"to": 1,
"total": 1
},
"status": 200,
"message": "Devices Retrieved Successfully"
}
POST Register Device (Admin) Devices {{url}}/devices/add ▸
Register a new device directly (as an admin), immediately creating it in both TerraGo and TerraGo's zone/location and tag registry with ONLINE status. Compare to Request Registration, which is the self-service flow devices use before an admin approves them.
Request Body
Same fields as Update Device: name, description, serialnumber (required, unique), type (required), estdatausage (required), plus optional imei, simphonenumber, waaszoneuuid, waaszonename, status, batterylevel, administativestaffid.
Notes
- Also seeds an initial device-status-log entry so uptime tracking has a baseline from creation time.
Response
The created device (data).
{
"name": "name",
"description": "description",
"serial_number": "serial_number",
"administative_staff_id":"68b86410-e6e8-4b5f-9a3d-8265a7773c0e",
"sim_phone_number": "+254725544432",
"waas_zone_uuid":"ada52fc6-d481-48d9-82b3-fddb07d5d44f",
"waas_zone_name":"Terra HQ",
"est_data_usage":"2000", //In MBs
"type": "SCANNER" //only use: SCANNER, GATEWAY, TRACKER, OTHER, N/A
}{
"status": 200,
"data": {
"id": "e91c072b-508c-448d-a368-f22c34434742",
"name": "name",
"description": "description",
"sim_phone_number": "+254725544432",
"serial_number": "serial_number",
"waas_zone_uuid": "ada52fc6-d481-48d9-82b3-fddb07d5d44f",
"waas_zone_name": "Terra HQ",
"est_data_usage": "2000",
"type": "Scanner",
"administrative_staff": {
"id": "68b86410-e6e8-4b5f-9a3d-8265a7773c0e",
"first_name": "Jane",
"last_name": "Otieno"
},
"status": "Online",
"last_activity": "2025-12-27 14:10:56",
"battery_level": 100,
"metadata": null
},
"message": "Device registered successfully"
}
POST Approve Device Registration Devices {{url}}/devices/approve/1aa126a1-f056-4b4e-bd59-9b821ff50bb9 ▸
Admin approval step that completes onboarding for a device previously created via Request Registration: fills in the remaining details, syncs it to the registry, and moves it out of PENDING_APPROVAL.
Path Parameters
- id (
stringUUID, required): Device UUID (the one created by the self-registration call).
Request Body
Same fields as Update Device — administativestaffid is effectively required here since it determines which school the device is registered to in the registry.
Response
The approved/activated device (data).
{
"name": "name",
"description": "description",
"serial_number": "serial_number",
"administative_staff_id":"46b0a803-39c8-4113-8b29-8ef30438c5bd",
"sim_phone_number": "+254725544432",
"waas_zone_uuid":"ada52fc6-d481-48d9-82b3-fddb07d5d44f",
"waas_zone_name":"Terra HQ",
"est_data_usage":"2000", //In MBs
"type": "SCANNER" //only use: SCANNER, GATEWAY, TRACKER, OTHER, N/A
}{
"status": 200,
"data": {
"id": "38de6cc4-9043-454b-b3f2-ac67b591b09e",
"name": "name",
"description": "description",
"sim_phone_number": "+254725544432",
"serial_number": "serial_number",
"waas_zone_uuid": "ada52fc6-d481-48d9-82b3-fddb07d5d44f",
"waas_zone_name": "Terra HQ",
"est_data_usage": "2000",
"type": "Scanner",
"administrative_staff": {
"id": "46b0a803-39c8-4113-8b29-8ef30438c5bd",
"first_name": "PM",
"last_name": "School"
},
"status": "Low Battery",
"last_activity": "2026-02-27 10:52:15",
"battery_level": 100,
"metadata": null
},
"message": "Device registered successfully"
}
PUT Update Device Devices {{url}}/devices/update/e91c072b-508c-448d-a368-f22c34434742 ▸
Update a device's configuration. Changes are pushed to TerraGo's zone/location and tag registry in the same request (transactional — if the registry call fails, nothing is saved locally).
Path Parameters
- id (
stringUUID, required): Device UUID.
Request Body
- name (
string, required) - description (
string, required) - serial_number (
string, required, must be unique) - type (
string, required):TRACKER,SCANNER,GATEWAY,OTHER, orN/A. - estdatausage (
integer, required, min 1): Estimated monthly data usage. - imei (
string, optional, max 32) - simphonenumber (
string, optional) - waaszoneuuid / waaszonename (
string, optional): Reassign to a different vehicle/zone/location. - status (
string, optional):ONLINE,OFFLINE,LOWBATTERY,PENDINGAPPROVAL,OTHER, orN/A. - battery_level (
integer, optional, 0–100, default 100) - administativestaffid (
stringUUID, optional): School the device belongs to; resolved from the authenticated user if omitted.
Notes
- If
batterylevelis 25 or below,statusis forced toLOWBATTERYregardless of what's passed in. administativestaffidmust be resolvable (from the request or from your own account) or this returns a422.
{
"name": "name",
"description": "description",
"serial_number": "serial_number",
"administative_staff_id":"68b86410-e6e8-4b5f-9a3d-8265a7773c0e",
"sim_phone_number": "+254725544432",
"waas_zone_uuid":"ada52fc6-d481-48d9-82b3-fddb07d5d44f",
"waas_zone_name":"Terra HQ",
"est_data_usage":"2000", //In MBs
"type": "SCANNER", //only use: SCANNER, GATEWAY, TRACKER, OTHER, N/A
"battery_level":"100",
"status": "ONLINE" //only use: ONLINE, OFFLINE, LOW_BATTERY, OTHER, N/A
}{
"status": 200,
"data": {
"id": "e91c072b-508c-448d-a368-f22c34434742",
"name": "name",
"description": "description",
"sim_phone_number": "+254725544432",
"serial_number": "serial_number",
"waas_zone_uuid": "ada52fc6-d481-48d9-82b3-fddb07d5d44f",
"waas_zone_name": "Terra HQ",
"est_data_usage": "2000",
"type": "Scanner",
"administrative_staff": {
"id": "68b86410-e6e8-4b5f-9a3d-8265a7773c0e",
"first_name": "Jane",
"last_name": "Otieno"
},
"status": "Online",
"last_activity": "2025-12-27 14:56:47",
"battery_level": "100",
"metadata": null
},
"message": "Device updated successfully"
}
IoT Stock
GET List Stock IoT Stock {{url}}/iot-stock ▸
Paginated, filterable list of wearable (band/tag) inventory — the admin dashboard's Device Stock → Wearables tab.
Query Parameters
- supervisor_id (
stringUUID, optional): filter to stock allocated to one school. - status (
string, optional): e.g.available,in_use. - assigned (
boolean, optional):1for stock assigned to a dependant,0for unassigned. - batch_number / color (
string, optional). - date_range (
string, optional):"YYYY-MM-DD,YYYY-MM-DD". - search (
string, optional): matches serial number or earth code. - per_page (
integer, optional, max 20).
Response
Paginated stock items, plus a top-level batches array of every distinct batch_number currently in stock (for the batch filter dropdown).
Notes
- Requires a bearer token (
auth:api).
{
"status": 200,
"batches": [
"BATCH-2026-01",
"BATCH-2026-02"
],
"message": "Stock fetched successfully",
"data": [
{
"id": "e5f6a7b8-0000-4a11-8888-abcde1234567",
"serial_number": "WB-10023",
"earth_code": "EC-88231",
"batch_number": "BATCH-2026-01",
"color": "Blue",
"type": "wearable_band",
"status": "available",
"school": null,
"dependant": null,
"assigned_at": null,
"created_at": "2026-01-05 09:00:00"
}
]
}
POST Add Stock Item IoT Stock {{url}}/iot-stock ▸
Register a single wearable device into stock.
Request Body
- serial_number (
string, required, unique). - earth_code (
string, optional, unique). - batch_number (
string, optional). - color (
string, optional). - type (
string, optional). - administrative_id (
stringUUID, optional): pre-allocate the item to a school.
Notes
- Requires a bearer token (
auth:api).
{
"serial_number": "WB-10099",
"earth_code": "EC-88299",
"batch_number": "BATCH-2026-02",
"color": "Red",
"type": "wearable_band"
}{
"status": 200,
"message": "Wearable added to stock",
"data": {
"id": "f6a7b8c9-0000-4a11-8888-abcde1234567",
"serial_number": "WB-10099",
"earth_code": "EC-88299",
"batch_number": "BATCH-2026-02",
"color": "Red",
"type": "wearable_band",
"status": "available",
"school": null,
"dependant": null,
"assigned_at": null,
"created_at": "2026-07-13 10:00:00"
}
}
POST Bulk Add Stock IoT Stock {{url}}/iot-stock/bulk ▸
Register many wearable devices into stock in one call — used by the bulk-upload flow.
Request Body
- items (
array, required, min 1), each with: - serial_number (
string, required, unique across all items and existing stock). - earthcode / batchnumber / color / type (
string, all optional;typedefaults towearable_band). - administrative_id (
stringUUID, optional).
Notes
- Requires a bearer token (
auth:api).
{
"items": [
{
"serial_number": "WB-20001",
"batch_number": "BATCH-2026-03",
"color": "Green"
},
{
"serial_number": "WB-20002",
"batch_number": "BATCH-2026-03",
"color": "Green"
}
]
}{
"status": 200,
"data": [],
"message": "Bulk stock added successfully"
}
GET Get Stock Item IoT Stock {{url}}/iot-stock/e5f6a7b8-0000-4a11-8888-abcde1234567 ▸
Retrieve a single wearable stock item by UUID.
Notes
- Requires a bearer token (
auth:api).
{
"status": 200,
"message": "Stock item fetched successfully",
"data": {
"id": "e5f6a7b8-0000-4a11-8888-abcde1234567",
"serial_number": "WB-10023",
"earth_code": "EC-88231",
"batch_number": "BATCH-2026-01",
"color": "Blue",
"type": "wearable_band",
"status": "available",
"school": null,
"dependant": null,
"assigned_at": null,
"created_at": "2026-01-05 09:00:00"
}
}
PUT Update Stock Item IoT Stock {{url}}/iot-stock/update/e5f6a7b8-0000-4a11-8888-abcde1234567 ▸
Update a stock item's metadata, status, or school allocation.
Request Body (all optional)
- earth_code (
string, unique). - batch_number / color / type (
string). - status (
string). - administrative_id (
stringUUID, nullable): reassign (or unassign, if omitted/null) the school this stock belongs to.
Notes
- Requires a bearer token (
auth:api).
{
"status": "available",
"color": "Blue"
}{
"status": 200,
"message": "Stock item updated successfully",
"data": {
"id": "e5f6a7b8-0000-4a11-8888-abcde1234567",
"serial_number": "WB-10023",
"earth_code": "EC-88231",
"batch_number": "BATCH-2026-01",
"color": "Blue",
"type": "wearable_band",
"status": "available",
"school": null,
"dependant": null,
"assigned_at": null,
"created_at": "2026-01-05 09:00:00"
}
}
POST Assign Stock to School IoT Stock {{url}}/iot-stock/assign-to-school ▸
Bulk-assign wearable stock (by serial number) to a school. Only stock that isn't already tied up with a dependant is reassignable.
Request Body
- serial_numbers (
string[], required, min 1). - administrative_id (
stringUUID, required): the school to assign to.
Response
- assigned — how many items were reassigned.
- skipped — serial numbers that didn't match any reassignable stock item.
Notes
- Requires a bearer token (
auth:api).
{
"serial_numbers": [
"WB-10023",
"WB-10024"
],
"administrative_id": "448e2ae4-48b4-4842-84b1-a73c84d21de1"
}{
"status": 200,
"data": {
"assigned": 2,
"skipped": []
},
"message": "Assigned 2 device(s) to school successfully"
}
Zones/Locations
GET List Zones/Locations {{url}}/utility/zones ▸
List zones/locations (vehicles / pickup areas) for the organisation. Like Tags, this is read directly from TerraGo's registry, re-paginated so the links point back at TerraGo.
Query Parameters
- showonlymain (
boolean, optional): Return only main zones/locations. - zone_id (
string, optional): Filter by a main zone/location id. - status (
string, optional), zone_type (string, optional), tags (optional): Additional the registry filters. - search (
string, optional): Text search. - per_page (
integer, optional, default 10, max 20), page (integer, optional).
Response
Paginated collection of zones/locations (ZoneResource).
Notes
- Depends on the registry; returns a warning if the registry call fails.
- Requires a bearer token (
auth:api).
{
// "search": "Terra Hq",
"show_only_main": false,
"zone_type": "Mobile", // Fixed for fixed zone
"per_page": 10,
"page": 1
}{
"data": [
{
"id": "ada52fc6-d481-48d9-82b3-fddb07d5d44f",
"name": "Terra Hq",
"zone_type": null,
"description": "nnn",
"main_zone": null,
"is_sub_zone": 0,
"tags": [],
"whitelist_tags": [],
"metadata": {
"Age": "12"
}
},
{
"id": "69a977c0-69f0-461e-b09d-b098a2cf412d",
"name": "WS3 Main Gate",
"zone_type": null,
"description": "Main Gate Access",
"main_zone": null,
"is_sub_zone": 0,
"tags": [],
"whitelist_tags": []
},
{
"id": "10a7b5f5-6fef-4b60-a265-000cb9c862d8",
"name": "WS3 Dining Hall",
"zone_type": null,
"description": "Dining hall access",
"main_zone": null,
"is_sub_zone": 0,
"tags": [],
"whitelist_tags": []
},
{
"id": "ffda0655-6d6b-4341-b666-1e3429066e27",
"name": "Park View Room 101",
"zone_type": null,
"description": "Notes",
"main_zone": null,
"is_sub_zone": 0,
"tags": [],
"whitelist_tags": []
},
{
"id": "e6a21cd2-d7cc-4671-b20b-0020fdcf5847",
"name": "SITE A",
"zone_type": null,
"description": "sdsaa",
"main_zone": null,
"is_sub_zone": 0,
"tags": [],
"whitelist_tags": []
},
{
"id": "0d8a8f14-6e16-48bc-9485-facff301725b",
"name": "Backstage",
"zone_type": null,
"description": "Backstage",
"main_zone": null,
"is_sub_zone": 0,
"tags": [],
"whitelist_tags": []
},
{
"id": "dfa84171-df4f-43ab-89cb-5d86ab55f6ab",
"name": "Main Entrance",
"zone_type": null,
"description": "Main Entrance",
"main_zone": null,
"is_sub_zone": 0,
"tags": [],
"whitelist_tags": []
},
{
"id": "33029d12-b2db-4def-acdf-ea75cb84427e",
"name": "SAFARI-WALK",
"zone_type": null,
"description": "ZONE ENTRY",
"main_zone": null,
"is_sub_zone": 0,
"tags": [
{
"id": "2bf8b100-e0df-41e0-96c6-453d7c0f079e",
"name": "Zephr Dyer",
"description": "Quis porro tempore",
"entity": "zone",
"color_code": null
}
],
"whitelist_tags": []
},
{
"id": "fb3d18ca-1688-46d6-8efe-bafbf02a8335",
"name": "KBJ 101D",
"zone_type": null,
"description": "Ruaka Ndenderu Route",
"main_zone": null,
"is_sub_zone": 0,
"tags": [
{
"id": "2bf8b100-e0df-41e0-96c6-453d7c0f079e",
"name": "Zephr Dyer",
"description": "Quis porro tempore",
"entity": "zone",
"color_code": null
}
],
"whitelist_tags": [
{
"id": "7b18d5ea-346e-4b5c-8936-4a1f4cdaabb0",
"name": "Universal Tag",
"description": "This is a Universal Tag",
"entity": "universal",
"color_code": "#34C759",
"whitelisted_under": "account"
},
{
"id": "7b18d5ea-346e-4b5c-8936-4a1f4cdaabb0",
"name": "Universal Tag",
"description": "This is a Universal Tag",
"entity": "universal",
"color_code": "#34C759",
"whitelisted_under": "dependant"
},
{
"id": "5f9cbe4d-8274-47f4-84cc-8ba83f247c1e",
"name": "WS3",
"description": "World Service #3 schools",
"entity": "dependant",
"color_code": "#FF9500",
"whitelisted_under": "dependant"
},
{
"id": "dced7eab-c41a-4053-a38f-60b52b859f04",
"name": "Wasee",
"description": "Wathee",
"entity": "account",
"color_code": "#FF9500",
"whitelisted_under": "account"
}
]
}
],
"links": {
"first": "http://localhost:8001/api/zones?page=1",
"last": "http://localhost:8001/api/zones?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "http://localhost:8001/api/zones?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "http://localhost:8001/api/zones",
"per_page": 10,
"to": 9,
"total": 9
},
"status": 200,
"message": "Zones Retrieved Successfully"
}
Communication
GET List Communication Logs Communication {{url}}/communication/logs ▸
Retrieve a paginated log of messages sent to parents (SMS/WhatsApp), including delivery status.
Query Parameters
- per_page (
integer, optional, default 10, max 20) - senderuserid (
stringUUID, optional) - supervisor_id (
stringUUID, optional): Scope to a school. - status (
string, optional): e.g.SENT,DELIVERED,FAILED,PENDING. - channel (
string, optional):SMSorWHATSAPP. - category (
string, optional): Message category, matching a template category. - date_range (
string, optional):"YYYY-MM-DD,YYYY-MM-DD". - search (
string, optional): Matches phone number, subject, or content.
Response
Paginated list of communication-log entries (data) with meta; each: channel, recipient, status and timestamp.
{
// "date_range": "2025-10-23 14:03:21, 2025-10-23 14:03:21",
"status": "PENDING", //only use: PENDING, SENT, DELIVERED, FAILED
"channel": "WHATSAPP", //only use: SMS, WHATSAPP
"category": "CHECK_IN", //only use: GENERAL, CHECK_IN, CHECK_OUT, TRAFFIC_DELAY, VEHICLE_ISSUE, WEATHER_ISSUE, SAFETY_INCIDENT
"search": "",
"supervisor_id": "0e02c849-6f36-4fb9-81d3-64a22908ee5a",
"sender_user_id": "0e02c849-6f36-4fb9-81d3-64a22908ee5a",
"per_page": 10,
"page": 1
}{
"data": [
{
"id": "b449cadf-e469-49d6-b9d5-28db605c2aba",
"phone": "+254791472893",
"parent": {
"id": "d23b811d-368c-44fd-a031-6663bed28fc0",
"phone": "+254791472893",
"email": "Parent@gmail.com",
"first_name": "Pm",
"last_name": "Parent"
},
"dependant": {
"id": "18435a9c-5822-4f31-9a56-aca28e2a038d",
"first_name": "Middle",
"last_name": "PM"
},
"sender": {
"id": "0e02c849-6f36-4fb9-81d3-64a22908ee5a",
"first_name": "Collins",
"last_name": "Muriuki"
},
"category": "Check In",
"subject": "Terra Go Check In",
"content": "Mzazi Mpendwa Mtoto amepanda basi!",
"channel": "WhatsApp",
"status": "Pending",
"response": null,
"created_at": "2026-01-14 10:38:15"
},
{
"id": "e493e3dd-ea82-4207-bc06-7e0b325b3099",
"phone": "+254791472893",
"parent": {
"id": "d23b811d-368c-44fd-a031-6663bed28fc0",
"phone": "+254791472893",
"email": "Parent@gmail.com",
"first_name": "Pm",
"last_name": "Parent"
},
"dependant": {
"id": "18435a9c-5822-4f31-9a56-aca28e2a038d",
"first_name": "Middle",
"last_name": "PM"
},
"sender": {
"id": "0e02c849-6f36-4fb9-81d3-64a22908ee5a",
"first_name": "Collins",
"last_name": "Muriuki"
},
"category": "Check In",
"subject": "Terra Go Check In",
"content": "Mzazi Mpendwa Mtoto amepanda basi!",
"channel": "WhatsApp",
"status": "Pending",
"response": null,
"created_at": "2026-01-14 10:36:25"
},
{
"id": "fab6ae02-0be7-4ff5-bf03-033e1890d801",
"phone": "+254791472893",
"parent": {
"id": "d23b811d-368c-44fd-a031-6663bed28fc0",
"phone": "+254791472893",
"email": "Parent@gmail.com",
"first_name": "Pm",
"last_name": "Parent"
},
"dependant": {
"id": "18435a9c-5822-4f31-9a56-aca28e2a038d",
"first_name": "Middle",
"last_name": "PM"
},
"sender": {
"id": "0e02c849-6f36-4fb9-81d3-64a22908ee5a",
"first_name": "Collins",
"last_name": "Muriuki"
},
"category": "Check In",
"subject": "Terra Go Check In",
"content": "Mzazi Mpendwa Mtoto amepanda basi!",
"channel": "WhatsApp",
"status": "Pending",
"response": null,
"created_at": "2026-01-14 10:34:04"
},
{
"id": "e00614ce-400c-4047-ba72-b00ff3a9d54b",
"phone": "+254791472893",
"parent": {
"id": "d23b811d-368c-44fd-a031-6663bed28fc0",
"phone": "+254791472893",
"email": "Parent@gmail.com",
"first_name": "Pm",
"last_name": "Parent"
},
"dependant": {
"id": "18435a9c-5822-4f31-9a56-aca28e2a038d",
"first_name": "Middle",
"last_name": "PM"
},
"sender": {
"id": "0e02c849-6f36-4fb9-81d3-64a22908ee5a",
"first_name": "Collins",
"last_name": "Muriuki"
},
"category": "Check In",
"subject": "Terra Go Check In",
"content": "Mzazi Mpendwa Mtoto amepanda basi!",
"channel": "WhatsApp",
"status": "Pending",
"response": null,
"created_at": "2026-01-13 16:58:16"
},
{
"id": "96539eca-cc0b-4a13-94d1-a88b2b54c10b",
"phone": "+254791472893",
"parent": {
"id": "d23b811d-368c-44fd-a031-6663bed28fc0",
"phone": "+254791472893",
"email": "Parent@gmail.com",
"first_name": "Pm",
"last_name": "Parent"
},
"dependant": {
"id": "18435a9c-5822-4f31-9a56-aca28e2a038d",
"first_name": "Middle",
"last_name": "PM"
},
"sender": {
"id": "0e02c849-6f36-4fb9-81d3-64a22908ee5a",
"first_name": "Collins",
"last_name": "Muriuki"
},
"category": "Check In",
"subject": "Terra Go Check In",
"content": "Mzazi Mpendwa Mtoto amepanda basi!",
"channel": "WhatsApp",
"status": "Pending",
"response": null,
"created_at": "2026-01-13 16:56:49"
}
],
"links": {
"first": "http://terrago.test/api/communication/logs?page=1",
"last": "http://terrago.test/api/communication/logs?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "http://terrago.test/api/communication/logs?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "http://terrago.test/api/communication/logs",
"per_page": 10,
"to": 5,
"total": 5
},
"status": 200,
"message": "Communication Logs Retrieved Successfully"
}
GET Get Communication Stats Communication {{url}}/communication/stats ▸
SMS-channel engagement stats for the admin dashboard cards — sent this month, sent today, and month-to-date failure rate.
Query Parameters
- supervisor_id (
stringUUID, optional): scope stats to a single school; omitted for the org-wide (System Admin) view.
Response
- channel — always
SMS(this endpoint only covers the SMS channel). - sentmtd / senttoday — messages sent or delivered, month-to-date / today.
- failure_rate — percentage of this month's messages that failed.
- total_mtd — total messages (any status) this month.
Notes
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": {
"channel": "SMS",
"sent_mtd": 1204,
"sent_today": 38,
"failure_rate": 1.4,
"total_mtd": 1221
},
"message": "Communication stats retrieved successfully"
}
Templates
GET List Message Templates Templates {{url}}/templates ▸
Retrieve a paginated list of reusable SMS/WhatsApp/email message templates (e.g. "Child checked in", "Trip delayed").
Query Parameters
- per_page (
integer, optional, default 10, max 20) - supervisor_id (
stringUUID, optional): Templates scoped to a school; org-wide templates have no owner. - categories (
string, optional): Comma-separated category keys to filter by. - is_active (
boolean, optional) - channel (
string, optional): e.g.SMS,WHATSAPP,EMAIL. - search (
string, optional): Matches name, category, subject, or content.
Response
List of message templates (data).
{
"is_active": true, //only use: rue, false
"search": "",
"supervisor_id": "d0672893-35bb-4fe5-819f-dc073b786d23",
"per_page": 10,
"page": 1
}{
"data": [
{
"id": "909e0d32-1913-4847-94fc-747fd5727513",
"name": "Test Template",
"category": "CHECK_IN",
"subject": "Child Checkin",
"content": "Mzazi Mpendwa Mtoto amepanda basi"
}
],
"links": {
"first": "http://terrago.test/api/templates?page=1",
"last": "http://terrago.test/api/templates?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "http://terrago.test/api/templates?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "http://terrago.test/api/templates",
"per_page": 10,
"to": 1,
"total": 1
},
"status": 200,
"message": "Templates Retrieved Successfully"
}
POST Create/Update Message Template Templates {{url}}/templates/add ▸
Create a message template, or update it in place if one already exists for the same category + channel + target + school (upsert).
Request Body
- name (
string, required) - category (
string, required): Must be a known template category. - subject (
string, required) - content (
string, required): The message body, likely supporting placeholders. - channel (
string, optional, defaultSMS) - target (
string, optional): Who the template is aimed at. - is_active (
boolean, optional, defaulttrue) - administrativestaffid (
stringUUID, optional): School that owns this template; omit for an org-wide template.
Notes
- The uniqueness key for the upsert is
(category, channel, target, administrativestaffid)— posting the same combination again edits the existing template instead of creating a duplicate.
Response
The created/updated template (data).
{
"administrative_staff_id" : "d0672893-35bb-4fe5-819f-dc073b786d23",
"name": "Test Template",
"category": "CHECK_IN", //only use: GENERAL, CHECK_IN, CHECK_OUT, TRAFFIC_DELAY, VEHICLE_ISSUE, WEATHER_ISSUE, SAFETY_INCIDENT
"subject": "Child Checkin",
"content": "Mzazi Mpendwa Mtoto amepanda basi",
"target": "SINGLE", //only use: GLOBAL, SINGLE
"suffix": "powered by us."
}{
"status": 200,
"data": {
"id": "909e0d32-1913-4847-94fc-747fd5727513",
"name": "Test Template",
"category": "CHECK_IN",
"subject": "Child Checkin",
"content": "Mzazi Mpendwa Mtoto amepanda basi"
},
"message": "Template created successfully"
}
PUT Update Message Template Templates {{url}}/templates/update/909e0d32-1913-4847-94fc-747fd5727513 ▸
Update an existing message template.
Path Parameters
- id (
stringUUID, required): Template UUID.
Request Body
Same fields as Create/Update Message Template.
⚠️ Notes
- If
contentis submitted empty/blank, the template is deleted instead of being updated — this endpoint doubles as a soft "clear to delete" action, so don't send an empty content field by accident.
Response
The updated template (data).
{
"name": "Test Template",
"category": "CHECK_IN", //only use: GENERAL, CHECK_IN, CHECK_OUT, TRAFFIC_DELAY, VEHICLE_ISSUE, WEATHER_ISSUE, SAFETY_INCIDENT
"subject": "Child Checkin",
"content": "Mzazi Mpendwa Mtoto amepanda basi saa hii",
"target": "SINGLE", //only use: GLOBAL, SINGLE
"suffix": "powered by us."
}{
"status": 200,
"data": {
"id": "909e0d32-1913-4847-94fc-747fd5727513",
"name": "Test Template",
"category": "CHECK_IN",
"subject": "Child Checkin",
"content": "Mzazi Mpendwa Mtoto amepanda basi saa hii"
},
"message": "Template updated successfully"
}
POST Download Import Template Templates {{url}}/templates/import-template ▸
Download a pre-filled .xlsx bulk-import template for parents, children, devices, or wearables — used by the admin dashboard's bulk-upload flow.
Query Parameters
- entity (
string, required): one ofparents,dependants,devices,wearables. - administrative_id (
stringUUID): the school (Administrative Staff) to scope the template to. Required whenentity=dependants(needed to populate route/tag choices); optional for the other entity types.
Response
Binary .xlsx file download (Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet), not JSON.
Notes
- Also reachable via
GETon the same path (Route::match(['get','post'], ...)); the frontend calls it asPOST. - Does not require auth at the route level, but is only ever called from within the authenticated admin dashboard.
WhatsApp Flow Menus
GET List Menus WhatsApp Flow Menus {{url}}/whatsapp-flow-menus ▸
List every WhatsApp flow menu item, ordered by sort_order — the admin dashboard's System Settings → WhatsApp Flow Menu tab.
Notes
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": [
{
"id": 1,
"name": "main_menu",
"description": "Top-level WhatsApp flow menu shown to parents.",
"enabled": true,
"sort_order": 0,
"created_at": "2026-05-19T00:00:00.000000Z",
"updated_at": "2026-05-19T00:00:00.000000Z"
}
],
"message": "success"
}
POST Create Menu WhatsApp Flow Menus {{url}}/whatsapp-flow-menus/add ▸
Create a new WhatsApp flow menu item.
Request Body
- name (
string, required, unique). - description (
string, required). - enabled (
boolean, optional, defaulttrue). - sort_order (
integer, optional, default0).
Notes
- Requires a bearer token (
auth:api).
{
"name": "book_a_ride",
"description": "Lets a parent book an ad-hoc ride.",
"enabled": true,
"sort_order": 1
}{
"status": 200,
"data": {
"id": 2,
"name": "book_a_ride",
"description": "Top-level WhatsApp flow menu shown to parents.",
"enabled": true,
"sort_order": 0,
"created_at": "2026-05-19T00:00:00.000000Z",
"updated_at": "2026-05-19T00:00:00.000000Z"
},
"message": "Menu created successfully"
}
PUT Update Menu WhatsApp Flow Menus {{url}}/whatsapp-flow-menus/update/1 ▸
Update a WhatsApp flow menu item.
Request Body (all optional)
- name (
string, unique). - description (
string). - enabled (
boolean). - sort_order (
integer).
Notes
- Requires a bearer token (
auth:api).
{
"description": "Updated description."
}{
"status": 200,
"data": {
"id": 1,
"name": "main_menu",
"description": "Top-level WhatsApp flow menu shown to parents.",
"enabled": true,
"sort_order": 0,
"created_at": "2026-05-19T00:00:00.000000Z",
"updated_at": "2026-05-19T00:00:00.000000Z"
},
"message": "Menu updated successfully"
}
DELETE Delete Menu WhatsApp Flow Menus {{url}}/whatsapp-flow-menus/delete/1 ▸
Delete a WhatsApp flow menu item.
Notes
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": [],
"message": "Menu deleted successfully"
}
POST Reorder Menus WhatsApp Flow Menus {{url}}/whatsapp-flow-menus/reorder ▸
Bulk-update the sort_order of many menu items at once (drag-to-reorder in the UI).
Request Body
- menus (
array, required), each with: - id (
integer, required, must exist). - sort_order (
integer, required).
Notes
- Requires a bearer token (
auth:api).
{
"menus": [
{
"id": 1,
"sort_order": 0
},
{
"id": 2,
"sort_order": 1
}
]
}{
"status": 200,
"data": [
{
"id": 1,
"name": "main_menu",
"description": "Top-level WhatsApp flow menu shown to parents.",
"enabled": true,
"sort_order": 0,
"created_at": "2026-05-19T00:00:00.000000Z",
"updated_at": "2026-05-19T00:00:00.000000Z"
}
],
"message": "Order updated successfully"
}
PUT Toggle Menu WhatsApp Flow Menus {{url}}/whatsapp-flow-menus/toggle/1 ▸
Enable or disable a menu item without touching its other fields.
Request Body
- enabled (
boolean, required).
Notes
- Requires a bearer token (
auth:api).
{
"enabled": false
}{
"status": 200,
"data": {
"id": 1,
"name": "main_menu",
"description": "Top-level WhatsApp flow menu shown to parents.",
"enabled": false,
"sort_order": 0,
"created_at": "2026-05-19T00:00:00.000000Z",
"updated_at": "2026-05-19T00:00:00.000000Z"
},
"message": "Menu status updated successfully"
}
Subscription Voucher Codes
GET List Vouchers Subscription Voucher Codes {{url}}/subscription-voucher-codes ▸
Paginated, filterable list of subscription voucher codes — the admin dashboard's System Settings → Vouchers tab.
Query Parameters
- status (
string, optional):pendingorused. - code (
string, optional): partial match. - parent_id (
integer, optional): filter by the redeeming user's internal id. - search (
string, optional): matches code or the redeeming user's name/email. - per_page (
integer, optional, max 100).
Notes
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": {
"data": [
{
"id": "a1b2c3d4-1111-4a11-8888-abcde1234567",
"code": "TERRA-FREE-2026",
"amount": 1500,
"duration": 1,
"user_id": null,
"status": "pending",
"created_at": "2026-07-13T10:00:00.000000Z",
"updated_at": "2026-07-13T10:00:00.000000Z"
}
],
"total": 1,
"per_page": 20,
"current_page": 1,
"last_page": 1
},
"message": "success"
}
POST Create Voucher Subscription Voucher Codes {{url}}/subscription-voucher-codes/add ▸
Create a new subscription voucher code.
Request Body
- code (
string, required, unique). - amount (
number, required, min 0). - duration (
integer, required): number of billing cycles the voucher discounts;-1means forever (seeSubscriptionVoucherCode::DURATION_FOREVER).
Notes
- Requires a bearer token (
auth:api).
{
"code": "TERRA-FREE-2026",
"amount": 1500,
"duration": 1
}{
"status": 200,
"data": {
"id": "a1b2c3d4-1111-4a11-8888-abcde1234567",
"code": "TERRA-FREE-2026",
"amount": 1500,
"duration": 1,
"user_id": null,
"status": "pending",
"created_at": "2026-07-13T10:00:00.000000Z",
"updated_at": "2026-07-13T10:00:00.000000Z"
},
"message": "Voucher created successfully"
}
DELETE Delete Voucher Subscription Voucher Codes {{url}}/subscription-voucher-codes/delete/a1b2c3d4-1111-4a11-8888-abcde1234567 ▸
Soft-delete a voucher code.
Notes
- Requires a bearer token (
auth:api).
{
"status": 200,
"data": null,
"message": "Voucher deleted successfully"
}
Reports
GET Admin — Overview Stats Reports {{url}}/reports/admin/overview-stats ▸
Org-wide week-over-week growth for the four admin-dashboard cards: parents, schools, kids, and buses (bus minders). Compares records created this week (Monday→now) against the previous full week.
Response
For each of parents, schools, kids, buses:
- this_week — count created this week.
- last_week — count created the prior week.
- delta_pct — percentage change vs last week, or
nullwhen there's no prior-week baseline (UI then shows the raw "+N this week").
Also returns period.thisweekstart / period.lastweekstart.
Notes
- "schools" = Administrative Staff users; "buses" = Staff with
stafftype = BUSMINDER; "kids" = all students. - System-admin endpoint — aggregates across all schools.
- Requires a bearer token (
auth:api).
GET Admin — Daily Taps Reports {{url}}/reports/admin/daily-taps ▸
Org-wide (not school-scoped) admin-dashboard metric: how many distinct active students were marked PRESENT in a window, versus the total active student base — i.e. daily platform "tap" adoption.
Query Parameters
- period_start (
date, optional, default today): Start of the window (start of day). - period_end (
date, optional, default today): End of the window (end of day).
Response
uniquestudents, totalstudents (all active students), percentage (unique / total × 100), and the resolved periodstart / periodend.
Notes
- Counts distinct students with a
PRESENTroll-call in the window. - This is a system-admin endpoint — it aggregates across all schools, not just the caller's.
- Requires a bearer token (
auth:api).
GET Admin — List Report Schedule Settings Reports {{url}}/reports/admin/report-schedule-settings ▸
List the automated report delivery schedules, one entry per frequency (Daily, Weekly, Monthly). Returns the configured settings if present, or defaults for any frequency not yet configured.
Response
An array of settings, each with frequency, sendat, dayofweek, dayofmonth, weekdaysonly, timezone, is_enabled, and a human-readable schedule summary.
Notes
- Pairs with Update Report Schedule Setting to enable/disable and tune each schedule.
- System-admin endpoint. Requires a bearer token (
auth:api).
PUT Admin — Update Report Schedule Setting Reports {{url}}/reports/admin/report-schedule-settings/Daily ▸
Create or update the automated report schedule for a given frequency (Daily, Weekly, or Monthly). Upserts the setting for that frequency.
Path Parameters
- frequency (
string, required): One ofDaily,Weekly,Monthly. An invalid value returns a validation error.
Request Body
- send_at (
string, required): Delivery time,HH:mm(send hour must be within the allowed window, 05:00–20:00). - timezone (
string, required): Must be one of the allowed timezones. - dayofweek (
integer, optional):1–7(Mon–Sun) — used forWeekly. - dayofmonth (
integer, optional):1–28— used forMonthly. - weekdays_only (
boolean, optional): ForDaily, restrict sends to weekdays. - is_enabled (
boolean, optional): Turn this schedule on/off.
Response
The upserted schedule setting.
Notes
- System-admin endpoint. Requires a bearer token (
auth:api).
{
"send_at": "07:00", // Time of day to send (HH:mm, 24-hour)
"timezone": "Africa/Nairobi", // Timezone, e.g. Africa/Nairobi
"weekdays_only": true, // Only send on weekdays (Mon–Fri)
"is_enabled": true // Turn this schedule on/off
}