Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.digiflecttech.dev/llms.txt

Use this file to discover all available pages before exploring further.

Every Save App API endpoint that reads or modifies group data requires authentication. You authenticate by exchanging credentials for a short-lived JWT token, then include that token in the Authorization header of each request. Save App supports two login methods: a direct phone/PIN login for testing and development, and Firebase Phone Authentication for production mobile apps.

How authentication works

  1. Your app sends a phone number and 4-digit PIN (or a Firebase ID token) to a login endpoint.
  2. The API returns a JWT bearer token along with the user’s name, role, and creator status.
  3. Your app includes the token on every subsequent request as Authorization: Bearer <token>.
  4. The token expires after 24 hours. After expiry, your app must log in again to get a new token.
The token encodes the user’s phone number as its sub claim. The API verifies the token on each request and loads the corresponding user record from the database.
Tokens expire after 24 hours. Store the expiry time alongside the token in your app and refresh it before it expires to avoid interrupting the user’s session.

Login methods

Phone and PIN login

Use POST /api/auth/login to log in with a phone number and 4-digit numeric PIN. Phone numbers must be in Uganda format (+256XXXXXXXXX). This method is rate-limited. Pass groupName to ensure the user belongs to the correct savings group. Pass loginType ("admin" or "member") to enforce role-based portal access — a member attempting admin login receives 403 Forbidden.
curl -X POST http://localhost:8000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+256701234567",
    "password": "1234",
    "groupName": "Kampala Savers",
    "loginType": "admin"
  }'
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "name": "Amara Osei",
  "role": "admin",
  "is_creator": true
}

Firebase Phone Authentication

Use POST /api/auth/firebase-login to log in with a Firebase ID token obtained after phone verification on the mobile client. This is the recommended flow for production apps because Firebase handles OTP delivery, SIM-swap detection, and phone number verification. If the phone number does not already have an account, the API creates one automatically with role: member.
Use Firebase Phone Auth in production. It handles OTP sending, rate limiting, and phone number verification natively, so your app does not need to manage that complexity.
curl -X POST http://localhost:8000/api/auth/firebase-login \
  -H "Content-Type: application/json" \
  -d '{
    "idToken": "<firebase-id-token>",
    "group_name": "Kampala Savers"
  }'
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "name": "Fatima Nakato",
  "role": "member",
  "is_creator": false
}
The firebase-login endpoint is rate-limited to 10 requests per minute per IP.

Making authenticated requests

Include the token from the login response in the Authorization header of every request that requires authentication:
Authorization: Bearer <token>
curl -X GET http://localhost:8000/api/analytics/summary \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Omitting the header or passing an invalid token returns 401 Unauthorized.

Login response fields

FieldTypeDescription
tokenstringJWT bearer token. Include in Authorization: Bearer <token> on every request.
namestringDisplay name of the authenticated user.
rolestringEither "admin" or "member". Determines which endpoints the user can access.
is_creatorbooleantrue if this admin was the first to register under their group name. Creators have additional privileges over group configuration.

Error codes

CodeMeaning
401 UnauthorizedMissing or invalid token, wrong password, or account not found.
403 ForbiddenValid token but insufficient permissions — for example, a member attempting to access an admin-only endpoint, or a user logging in to the wrong group.

Member onboarding

When an admin adds a new member via POST /api/members, the member’s account is created with status: "pending" and a temporary password. The member must complete onboarding before they can log in normally.
1

Check phone number

The member’s app calls POST /api/auth/onboarding/check-phone to verify their phone number exists in the group as a pending account.
curl -X POST http://localhost:8000/api/auth/onboarding/check-phone \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+256789876543",
    "groupName": "Kampala Savers"
  }'
{
  "success": true,
  "message": "User found"
}
If the phone number is not found as pending in the specified group, the response returns success: false with a message asking the member to contact their admin.
2

Set a password

The member calls POST /api/auth/onboarding/set-password to choose their PIN. This activates the account (status changes from "pending" to "active") and returns a JWT token so the member is immediately logged in.
curl -X POST http://localhost:8000/api/auth/onboarding/set-password \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+256789876543",
    "password": "5678"
  }'
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "name": "Fatima Nakato",
  "role": "member",
  "is_creator": false
}
After this step, the member can log in normally using POST /api/auth/login.
The onboarding endpoints normalize Ugandan phone numbers automatically. A number entered as 0789876543 is treated the same as +256789876543.