MyChurchDocs

Member App API (/api/v2)

The API behind the MyChurch mobile apps: login, profile, giving, check-ins, groups and church content for one member of one church. It is frozen: paths, fields and response shapes stay exactly as they are.

This is the surface the shipped MyChurch app and the church white-label apps call today. It is documented here as it behaves, for the app team and for anyone building a member-facing client. New member features will arrive on the platform API as /v1/member/* routes with JWT authentication; until then everything a member does goes through these routes.

Base URL

Base URL
https://uat-api.mychurch.co.ke/api/v2/

The apps call /api/v2/. Most routes are also answered under /api/v1/ for older app builds; where the two differ (login, sermons, pledges, transaction history, department exit, merchandise, parking payment) this reference documents v2 and notes the v1 shape only when it matters. Some routes exist on v2 only: age groups, the church list behind the access key, services, fundraisers, classes, verse of the day, appointments, contribution programs, department meetings and the public web/* routes.

Authentication

A successful login returns a member token: a random uuid tied to that member. Send it on every authenticated route as a Bearer token:

An authenticated call
curl https://uat-api.mychurch.co.ke/api/v2/profile \
  -H "Authorization: Bearer 11111111-2222-4333-8444-555555555555" \
  -H "Accept: application/json"

One token is live per member: logging in again replaces it, and POST logout removes it. A missing, unknown or revoked token answers HTTP 401:

HTTP 401
{"message": "Unauthenticated", "status": false}

The same routes also accept a platform JWT with scope=member, which is what future app releases will send. See Authentication for every principal.

Routes with a church in the path

Routes such as checkin/{church}, trustees/{church} and send-login-otp/{church} take the church uuid in the path. The api checks that the token belongs to a member of that church; a token from another church gets the 401 above. (The previous system trusted the path. That was a tenancy hole and is closed.) An unknown church uuid answers HTTP 404:

HTTP 404
{"message": "No query results for model [App\\Models\\Church] 00000000-0000-4000-8000-000000000009"}

Legacy behaviour the shipped apps rely on

Two behaviours of the installed apps are kept on this surface until a new app release retires them. A new client should know about them, and should not copy them.

The PIN is encrypted by the app

The shipped apps encrypt the member's 4-digit PIN with AES-256-CBC, using keys built into the apps, before sending it in the pin, old_pin and new_pin fields. The api decrypts it and compares it with the stored bcrypt hash. A PIN that does not decrypt answers {"status": false, "message": "An error occurred"} on login and registration, and You have entered the wrong pin on PIN-guarded routes. The keys are not published: this layer exists only for the installed apps, and the new /v1/member routes take the plain PIN over TLS.

The CHURCHES-ACCESS-KEY header

GET churches and GET all-churches require a CHURCHES-ACCESS-KEY header carrying a key built into the apps. Without it, or with the wrong value, they answer HTTP 401 {"message": "Unauthorized", "status": false}. The key is issued to the MyChurch app team only and is never published. No other route uses it.

Request format

Send JSON (Content-Type: application/json) or form fields; the api accepts JSON with any content type, URL-encoded forms, multipart forms (the photo upload) and query-string fields on POST. Numbers may arrive as strings. Several legacy enumerations travel as small integers:

  • gender: 1 male, 2 female.
  • marital_status: 1 single, 2 married, 3 single parent, 4 widowed.
  • Pledge frequency: 1 once, 2 weekly, 3 monthly, 4 annual.
  • Phone numbers: any Kenyan format (0712345678, 254712345678, +254712345678). They are normalised to 254712345678.

Responses

Every answer is JSON with a boolean status and a message meant for the member, plus the data keys of that route. A failure the member can act on is HTTP 200 with status: false, so always read status, not only the HTTP code:

Success and failure, both HTTP 200
{"message": "Login successful", "status": true, "token": "11111111-2222-4333-8444-555555555555", "photo": null, "account": { "...": "..." }}

{"message": "Invalid credentials", "status": false, "token": null}

A missing or malformed field is HTTP 422 in the Laravel validation shape:

HTTP 422
{
  "message": "The phone field is required.",
  "errors": { "phone": ["The phone field is required."] }
}

Other codes: 401 for a bad token, 404 for an unknown uuid in the path (the Laravel model-not-found message), 500 {"message": "Server Error"} and 503 when the service is briefly unavailable. Responses and errors compares this envelope with the /v1 one.

How values are rendered

  • Date-times are Nairobi wall-clock: 2026-11-01 09:30:00 for stored date-times, 2026-11-01 for dates, 2026-11-01T06:30:00.000000Z for record timestamps.
  • Yes/no flags are 0 or 1, not booleans.
  • Money is a numeric string ("1500.00") or a formatted string ("1,500.00") depending on the route, exactly as each route always answered.
  • Records are identified by uuid. A few integer id fields remain where an installed app reads them (the age group on the profile, appointments, feedback sub-categories, contribution programs, spouse invitations). Do not build on them.
  • Empty strings stay empty strings and missing values are null.
  • Images are absolute URLs under https://uat-api.mychurch.co.ke/storage/scarlet/, or null.

Premium features

Events, the calendar, TV schedules, appointments, merchandise and parking need the church to be on the premium plan. On a basic church these routes answer HTTP 200:

json
{"message": "Your church is currently not subscribed to this feature.", "status": false}

Churches still moving to the new platform

Churches move from the previous MyChurch system one at a time. While a church has not moved yet, its members cannot log in, register or reset a PIN here; those routes answer:

json
{"status": false, "message": "Your church is still on the previous MyChurch system. Please update the app or try again later"}

and authenticated routes answer 401, which sends the app back to its login screen.

This surface never breaks

Paths, request fields, response keys and the HTTP-200-on-failure rule are frozen. Fixes that change behaviour (a security check, a bug that answered 500) are listed on the changelog. See the versioning policy.

Reference