Auth
Authentication and session lifecycle. Covers login (v1, plus the v2 staff zone/location-selection flow), zone/location selection for staff, JWT refresh-token rotation, logout, the password-reset email/reset pair, and the /auth/me bootstrap that returns the current user with their role, assigned zone/location, the registry context and onboarding status. All authenticated endpoints elsewhere expect the bearer access_token issued here.
POST Login V1 {{url}}/auth/access-token ▸
Authenticate a user with email + password and receive a bearer access token plus a refresh token. This is the original (v1) login used by the web dashboard and non-staff clients.
Request Body
- email (
string, required): The user's email address (case-insensitive). - password (
string, required): The user's password. - role_id (
stringUUID, optional): Role to authenticate as. Optional for System Admins; useful when an account maps to a single role. - remember_me (
boolean, optional, defaultfalse): Whentrue, the access token lives 7 days (10080 min) instead of the default TTL.
Response
Returns accesstoken, refreshtoken, tokentype (bearer), and expiresin (minutes).
Notes
- The account must exist and be active (
is_active = true) — deactivated accounts get a 401 with "Your account has been deactivated". - Every successful login rotates the refresh token: any previously issued refresh tokens for the user are deleted and a new 30-day refresh token is created.
- Wrong email or password returns
401 Unauthorized.
{
"email": "yamofaith@gmail.com",
"password": "Terra@2025",
"role_id": "989d4025-e939-41f7-b032-356c7269612c"
}
// {
// "email" : "muriukicollins@gmail.com",
// "password": "Terra2012.",
// "role_id": "184ad925-a873-445e-a140-1edc1b11c2db"//optional if System Admin
// }
// {
// "email":"developermike@gmail.com",
// "password":"Terra@2012",
// "role_id":"4583792c-7951-4bf5-ba5f-68c374a12420"
// }
// {
// "email":"petekim211@gmail.com",
// "password":"Terra@2012",
// "role_id":"989d4025-e939-41f7-b032-356c7269612c"
// }
// {
// "email":"faithyamo@terrasofthq.com",
// "password":"Terrago@2025",
// "role_id":"4583792c-7951-4bf5-ba5f-68c374a12420"
// }
// {
// "email": "achiengyamo98@gmail.com",
// "password": "Terra@2025",
// "role_id": "6d0f4812-c376-47bb-b3db-ab23a0b8b2d0"
// }{
"status": 200,
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvYXBpL2F1dGgvYWNjZXNzLXRva2VuIiwiaWF0IjoxNzc4NDkzMTE2LCJleHAiOjE3Nzg2NjU5MTYsIm5iZiI6MTc3ODQ5MzExNiwianRpIjoiWml0bmJyVDBoSURMQ1pSRyIsInN1YiI6IjEzMiIsInBydiI6IjIzYmQ1Yzg5NDlmNjAwYWRiMzllNzAxYzQwMDg3MmRiN2E1OTc2ZjciLCJyb2xlIjoiU3RhZmYiLCJyb2xlX2lkIjoiOTg5ZDQwMjUtZTkzOS00MWY3LWIwMzItMzU2YzcyNjk2MTJjIn0.AogSE1RgTpEDEX4WYU7M4Ao8Li1bjwxRxBsMEXgnM10",
"refresh_token": "dBQb7TI6wr3DsrjwTI8ZwEzokpxsDbGcsJYAJpPcB8EN6pEVjjJmKeitE6FC6KFp",
"token_type": "bearer",
"expires_in": 2880
},
"message": "Token Refreshed Successfully"
}
POST Login V2 (Staff Zone/Location-Selection Flow) {{url}}/auth/access-token/v2 ▸
Authenticate a user via the v2 login flow. Unlike v1, this endpoint requires a client type and treats Staff users (drivers, bus minders, security) differently from other roles: Staff must finish authentication with a second call to Select Zone/Location before they get a usable access token.
Request Body
- email (
string, required): The user's email address. - password (
string, required): The user's password. - client (
string, required): Eitherapporweb. Staff accounts may only log in withclient=app; non-Staff accounts may only useclient=web. - remember_me (
boolean, optional, defaultfalse): Extends the token lifetime (1440 minutes normally, 10080 minutes / 7 days iftrue).
Response
- Staff (client=app): Returns a short-lived
temptoken(not a usable access token yet) plus the staff's assigned zones/locations. Passtemptokenand the chosen zone/location UUID to Select Zone/Location to get a realaccess_token. - Everyone else: Returns
accesstoken,refreshtoken,tokentype, andexpiresindirectly, same shape as Login V1.
Notes
- The account must be active (
is_active = true). - Any previous refresh tokens for the user are invalidated on login.
{
"email": "yamofaith@gmail.com",
"password": "Terra@2025",
"client": "app" // Required for terrago app only to allow select of zone
}{
"status": 200,
"data": {
"temp_token": "0PHPFgj1OqZQAjT3RY1V2h5eMWW8Yl3floD8zbDjxiXkGNmNL33wmnSfm2CpYXAh",
"staff": {
"id": "818e3ea7-543b-4739-9567-7a5a3e29e6c7",
"name": "Test Driver",
"email": "yamofaith@gmail.com",
"phone": "+254765432144"
},
"zones": [
{
"id": "94e511b2-9a5f-45d2-ac5b-a53d356df6fb",
"name": "Y Zone",
"metadata": null
},
{
"id": "b6b4c801-55ec-4b09-a894-e92794ceec2f",
"name": "Test Zone",
"metadata": null
}
]
},
"message": "Select an assigned zone to continue"
}
POST Select Zone/Location {{url}}/auth/select-zone ▸
Second step of the Staff login flow. Exchanges the temp_token returned by Login V2 plus a chosen zone/location for a full, zone/location-scoped access token. Only used for Staff accounts (drivers, bus minders, security) who select which zone/location/vehicle they're operating for the session.
Request Body
- temp_token (
string, required): The temporary token returned byPOST /auth/access-token/v2. - waaszoneuuid (
string, required): UUID of the zone/location the staff member is selecting. Must be one of the zones/locations assigned to them. - remember_me (
boolean, optional): Extends the session from 30 days to 90 days.
Response
Returns accesstoken, refreshtoken, tokentype, expiresin — use access_token as the Bearer token for all subsequent authenticated requests.
Notes
- The
temp_tokenis single-use and stored server-side (Redis) with a 10 minute TTL; it is deleted once redeemed. - Fails if the temp token has expired or if the staff member doesn't have access to the given zone/location.
{
"waas_zone_uuid": "825e7402-a072-4926-9bfb-27280c2be5eb",
"remember_me": false, //optional
"temp_token": "wVeqNnVP4qRACCN4bzUC60VQRFwTtTWvAcPZxINokgIwA9NC7DXpkDMlU81zLjyz"
}{
"status": 200,
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvYXBpL2F1dGgvc2VsZWN0LXpvbmUiLCJpYXQiOjE3Nzg0OTA2MjgsImV4cCI6MTc3ODY2MzQyOCwibmJmIjoxNzc4NDkwNjI4LCJqdGkiOiJmMG41TzdtTHR6TXdMc1ZaIiwic3ViIjoiMTMyIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyIsInJvbGUiOiJTdGFmZiIsInJvbGVfaWQiOiI5ODlkNDAyNS1lOTM5LTQxZjctYjAzMi0zNTZjNzI2OTYxMmMifQ.-lugNxsQDl7eqmRCnOnVlZLGLt6-5u5bGU5YQaUNDkM",
"refresh_token": "1edGYkABGaDlKTsx3I8NSgNHvR5WzorxLxk7WapBzlt2u4r8N9QFa7mZRGyNfBTS",
"token_type": "bearer",
"expires_in": 2880
},
"message": "Access token generated successfully"
}
PUT Refresh Token {{url}}/auth/refresh ▸
Exchange a valid, unexpired refresh token for a new access token and a new refresh token, so a session can continue without the user re-entering credentials.
Request Body
- refresh_token (
string, required): The refresh token issued at login (or by a previous refresh).
Response
Returns a fresh accesstoken, a new refreshtoken, and expires_in (seconds).
Notes
- Refresh tokens are single-use and rotated: the presented token is deleted and replaced with a new 30-day token on every call. Store the returned
refresh_tokenfor the next refresh. - If the token is missing, expired, or already used, the endpoint returns
401 Unauthorized("Refresh token expired or invalid") and the user must log in again. - For Staff sessions, the refresh preserves the selected-zone/location context (
session_key) so the new access token stays scoped to the same zone/location. - No bearer token is required to call this — the refresh token itself is the credential.
{
"refresh_token" : "ShqMCIEu95RW3UrJPlkDahAiwxqf5oYFtaEyroyNrVD8ApU2mvnOuRa1XkVAqWb3" // The refresh token issued at login
}{
"status": 200,
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3RlcnJhZ29zdGcudGVycmFzb2Z0aHEuY29tL2FwaS9hdXRoL3JlZnJlc2giLCJpYXQiOjE3Njk1OTk2MDksImV4cCI6MTc2OTYwMzIwOSwibmJmIjoxNzY5NTk5NjA5LCJqdGkiOiJ0QWk0c0IxbjFXdVVYOGxPIiwic3ViIjoiNDMiLCJwcnYiOiIyM2JkNWM4OTQ5ZjYwMGFkYjM5ZTcwMWM0MDA4NzJkYjdhNTk3NmY3Iiwicm9sZSI6IkFkbWluaXN0cmF0aXZlIFN0YWZmIiwicm9sZV9pZCI6IjQ1ODM3OTJjLTc5NTEtNGJmNS1iYTVmLTY4YzM3NGExMjQyMCJ9.RsmjkbmFdIgXASVr2_AJTI-4b02woi0TxpOzow1vxaQ",
"refresh_token": "O50H7TPiRRIEArX3EFog075dg33X6BDBKz9lvV8jYxLWt99DN9gOheOcnmSzWCy6",
"expires_in": 3600
},
"message": "Token refreshed"
}
PUT Logout {{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"
}
POST Send Reset Password Email {{url}}/auth/password/email ▸
Sends a password reset email to the specified user.
Required parameters:
- email (string): The user's email address to send the reset link to.
- url (string): The URL to which the user will be redirected to reset their password.
Expected behavior: If the email exists in the system, a password reset link will be sent to the provided email address. The response does not reveal whether the email exists for security reasons.
Response
Empty data; message confirms a reset link was sent. For security it does not reveal whether the email is registered.
{
"email" : "alex.kimani@example.com",
"url": "https://terragoui.terrasofthq.com/reset-password"
}{
"status": 200,
"data": [],
"message": "Password reset link sent successfully"
}
POST PasswordReset {{url}}/auth/password/reset ▸
Resets a user's password by accepting the user's email, new password, password confirmation, and a valid password reset token. All fields are required in the request body:
- email: The user's registered email address
- password: The new password to set
- password_confirmation: Must match the new password
- token: The password reset token provided to the user
On success, the user's password is updated and a confirmation response is returned. Use this endpoint after initiating a password reset process.
Response
Empty data; message confirms the password was updated. A validation error is returned if the token/email is invalid or the passwords don't match.
{
"email" : "alex.kimani@example.com", // User's email address
"password": "Terra@2012.", // User's password
"password_confirmation": "Terra@2012.", // Repeat of the new password (must match)
"token": "036d3679316643feb280c6fc2161fbd7c325693a5083c5e86946f731a7581061" // Password-reset token from the email link
}{
"status": 200,
"data": [],
"message": "Password reset successfully"
}
GET me {{url}}/auth/me ▸
Return the full profile of the currently authenticated user, enriched with the context the app needs on startup. This is the primary "who am I / bootstrap" call after login.
Response includes
- The user's core profile (via
UserResource), withroleandsupervisorloaded. - Assigned zone/location — for Staff, the the registry zone/location selected for the active session (resolved from the session key).
- the registry beneficiary context — the linked the registry account's
beneficiaryappfiltersandtags(keyed by beneficiary id), used to drive route/student filtering. - Setup status — for a school admin (Administrative Staff), a freshly computed onboarding checklist (
location,zones/locations,routes,trips,staff,parents) is calculated and cached on the user. - Pending tasks — e.g. an in-progress (
ACTIVE) trip for the user, so the app can resume it.
Notes
- Requires a valid bearer token (
auth:api). - Returns a warning if the registry is unreachable, since parts of the payload depend on it.
{
"data": {
"id": "68b86410-e6e8-4b5f-9a3d-8265a7773c0e",
"first_name": "Jane",
"middle_name": "Wanjiku",
"last_name": "Otieno",
"phone": "+254711223344",
"email": "jane.otieno@example.com",
"identification_document": "passport",
"identification_number": "A1234567",
"dob": "1995-08-15 00:00:00",
"gender": "Female",
"third_party_id": "eecc4c6b-32c8-4efb-b312-b3d1dac2c4f5",
"is_active": true,
"role": "Administrative Staff",
"metadata": null,
"image": null,
"notes": null,
"created_at": "2025-12-05 10:51:21",
"filters": {
"account": [],
"dependant": [
{
"id": "7b18d5ea-346e-4b5c-8936-4a1f4cdaabb0",
"name": "Universal Tag",
"created_at": "2025-05-21T12:04:02.000000Z"
},
{
"id": "f2c5edd9-f2f1-404e-ba80-44800cb37bc6",
"name": "ATHI RIVER PRIMARY SCHOOL",
"created_at": "2024-04-12T13:23:20.000000Z"
},
{
"id": "5f9cbe4d-8274-47f4-84cc-8ba83f247c1e",
"name": "WS3",
"created_at": "2025-05-21T12:04:48.000000Z"
}
],
"iot": [],
"wallet": [],
"zone": []
}
},
"status": 200,
"message": "User Retrieved Successfully"
}