Skip to content

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:

json
{
  "email": "evanson@semalink.africa",
  "password": "your-password"
}

Response 200:

json
{
  "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 locked
  • 403 — 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:

json
{ "refreshToken": "<opaque-token>" }

Response 200:

json
{
  "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:

json
{ "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:

json
{ "phone": "+254712345678" }

Response 200:

json
{ "staff": { ... } }

POST /staff/me/change-password

Change own password. Requires current password verification.

Request body:

json
{
  "currentPassword": "old-password",
  "newPassword": "new-password-min-8-chars"
}

Response 200:

json
{ "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:

json
[
  {
    "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:

json
{
  "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:

json
{
  "name": "Updated Name",
  "phone": "+254700000001"
}

Response 200:

json
{ "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:

json
{
  "permissions": {
    "crm": "admin",
    "sender-ids": "read",
    "billing": "none",
    "pricing": "none"
  }
}

Valid levels: "owner" | "admin" | "read" | "none"

Response 200:

json
{ "message": "Permissions updated." }

PATCH /staff/:id/active

Activate or deactivate a staff member. Deactivated accounts cannot log in.

Request body:

json
{ "active": false }

Response 200:

json
{ "message": "Staff member deactivated." }

Internal use only — Sema Link Engineering