Admin Endpoints
All admin endpoints are under /api/v1/admin/. They use a separate JWT namespace — tokens issued by these routes (type: "admin") are rejected by all customer routes, and customer tokens are rejected here.
Base URL: https://staging-arc.semalink.africa/api/v1/admin (staging)
Authentication
Admin tokens are short-lived access tokens (15 min) paired with a longer-lived refresh token. All authenticated endpoints require:
Authorization: Bearer <accessToken>Auth Routes
POST /auth/login
Authenticate a staff member. Returns an access token, refresh token, and staff profile.
No authentication required.
Request body:
{
"email": "evanson@semalink.africa",
"password": "your-password"
}Response 200:
{
"accessToken": "<jwt>",
"refreshToken": "<opaque-token>",
"staff": {
"id": "uuid",
"name": "Evanson Biwott",
"email": "evanson@semalink.africa",
"role": "owner",
"phone": "+254700000000",
"isActive": true,
"createdAt": "2026-04-27T..."
}
}Errors:
401— Invalid credentials or account locked403— Account inactive
POST /auth/refresh
Rotate the session. The old refresh token is invalidated and a new token pair is returned.
No authentication required.
Request body:
{ "refreshToken": "<opaque-token>" }Response 200:
{
"accessToken": "<new-jwt>",
"refreshToken": "<new-opaque-token>"
}Errors:
401— Token not found or already used (replay detected)
POST /auth/logout
Revoke the refresh token. Subsequent refresh attempts with this token will fail.
No authentication required.
Request body:
{ "refreshToken": "<opaque-token>" }Response 204: No content.
Staff — Own Profile
These routes operate on the authenticated staff member's own account.
GET /staff/me
Get the authenticated staff member's profile.
Response 200: Staff object (same shape as login response).
PATCH /staff/me
Update own profile. Currently only phone is patchable.
Request body:
{ "phone": "+254712345678" }Response 200:
{ "staff": { ... } }POST /staff/me/change-password
Change own password. Requires current password verification.
Request body:
{
"currentPassword": "old-password",
"newPassword": "new-password-min-8-chars"
}Response 200:
{ "message": "Password updated." }Errors:
401— Current password incorrect
Staff Management
These routes are owner only — staff members with role: "staff" receive 403.
GET /staff
List all staff members.
Response 200:
[
{
"id": "uuid",
"name": "...",
"email": "...",
"role": "owner" | "staff",
"phone": "..." | null,
"isActive": true,
"createdAt": "..."
}
]POST /staff
Create a new staff member. If password is omitted, the account is created without a password and the staff member must set one via a reset flow before logging in.
Request body:
{
"name": "Jane Smith",
"email": "jane@semalink.africa",
"role": "staff",
"password": "initial-password"
}Response 201: Created staff object.
Errors:
409— Email already exists
PATCH /staff/:id
Update a staff member's name or phone.
Request body:
{
"name": "Updated Name",
"phone": "+254700000001"
}Response 200:
{ "staff": { ... } }PATCH /staff/:id/permissions
Set per-module permission levels for a staff member. Send only the modules you want to change — omitted modules are left unchanged.
Request body:
{
"permissions": {
"crm": "admin",
"sender-ids": "read",
"billing": "none",
"pricing": "none"
}
}Valid levels: "owner" | "admin" | "read" | "none"
Response 200:
{ "message": "Permissions updated." }PATCH /staff/:id/active
Activate or deactivate a staff member. Deactivated accounts cannot log in.
Request body:
{ "active": false }Response 200:
{ "message": "Staff member deactivated." }