Login and sessions
How church users, head office users and Bitwise staff sign in: email and password, a six-digit code by email, then a session JWT.
These routes serve the portal at account.mychurch.co.ke and anything else that signs a person in. Members of a congregation never use them; they sign in through the member app API. For an unattended integration, ask for an mc_ token instead of storing a person's password (see the overview).
| Route | Permission | Purpose |
|---|---|---|
| POST/auth/login | Public | Check email and password; email a six-digit code and return a challenge. |
| POST/auth/verify-otp | Public | Exchange the challenge and code for a session JWT. |
| POST/auth/refresh | Session JWT | Issue a fresh JWT with a new expiry and the current permissions. |
| POST/auth/logout | Session JWT | Revoke the current session. |
| POST/auth/forgot-password | Public | Email a password reset link. Always answers success. |
| POST/auth/reset-password | Public | Set a new password with the token from the reset link. |
| GET/v1/me | Session JWT | The signed-in user, its scope, role, permissions and organisation. |
| POST/v1/me/password | Session JWT | Change the signed-in user's password. |
1. Password
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | The user's email address (case does not matter). | |
| password | string | Yes | The user's password. |
curl -X POST https://uat-api.mychurch.co.ke/auth/login \
-H "Content-Type: application/json" \
-d '{ "email": "[email protected]", "password": "correct horse battery" }'{
"success": true,
"data": {
"otp_required": true,
"challenge": "c2b9f7d0e4a14f5b8a3e6d1c0f9b2a7e",
"sent_to": "ja***@example.org"
}
}Wrong details answer 401 invalid email or password, the same for an unknown email and a wrong password. Repeated failures lock the account for a while (403 user account is locked), and an inactive account or church answers 403. A user whose church is still on the previous MyChurch system is told so with a 403; that church signs in on the previous portal until it moves. Codes are limited per email per hour; past the limit the api answers 429.
2. Code
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| challenge | string | Yes | The challenge from /auth/login. |
| code | string | Yes | The six-digit code from the email. Valid for 10 minutes, usable once. |
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.placeholder.placeholder",
"expires_at": "2026-11-01T10:42:00Z",
"user": {
"uuid": "6f0d2a8e-0000-4000-8000-00000000a001",
"name": "Jane Wanjiku",
"email": "[email protected]",
"scope": "church",
"role": "admin",
"permissions": [],
"church": {
"uuid": "11111111-2222-4333-8444-555555555555",
"name": "Example Fellowship Church",
"status": "active",
"plan": "premium"
}
}
}
}A wrong code answers 422; after too many wrong codes the challenge is spent and the user starts again at step 1. Send the token as Authorization: Bearer <token> on every /v1 call.
Why permissions can be empty
admin and a staff super_admin hold every permission of their scope, so the token does not list them. Only the user role (and mc_ tokens) carry an explicit list. Check access the way the api does: admin means every church:* permission, otherwise look the name up in the list.Refresh and logout
Sessions expire after an idle window (60 minutes by default). Call refresh with the current token before it expires to get a new token, a new expires_at and the user's current permissions, in the same body as verify-otp. A token issued before a password change, a role change or a forced sign-out is refused with 401.
Revokes the token at once. The body is empty.
Password reset
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| string | Yes | The account email. The answer is success whether or not it exists. |
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| token | string | Yes | The token from the link in the reset email. Valid for one hour, usable once. |
| password | string | Yes | The new password, at least 8 characters. |
An unknown or used token answers 422 with “this reset link is invalid or has already been used”, an old one “this reset link has expired”.
The signed-in user
Returns the same user object as above. Head office users get head_office (and subsidiary when bound to one) instead of church; staff get neither.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| current_password | string | Yes | The password in use now. |
| new_password | string | Yes | At least 8 characters. Every session of the user, this one included, is signed out; sign in again. |