Auth & Profile API
Customer registration, login, 2FA, password management, and address CRUD.
/api/v1/auth/register
Public
Create a new customer account. Sends a verification email.
Request Body
{
"name": "Jane Doe", // string, required
"email": "jane@email.com", // string, required, unique
"password": "Secure@123", // string, required, min 8 chars
"phone": "0712345678" // string, optional
}
Response
{
"success": true,
"data": {
"user": { "id": "uuid", "name": "Jane Doe", "email": "jane@email.com", "phone": "0712345678", "email_verified": false },
"access_token": "eyJ..."
},
"message": "Check your email to verify your account."
}
Possible Errors
| Code | HTTP | When |
|---|---|---|
| USER_EXISTS | 409 | Email already registered |
| VALIDATION_ERROR | 422 | Missing required fields |
/api/v1/auth/login
Public
Authenticate. Returns access token and sets HTTP-only refresh cookie. If OTP enabled, returns otp_token instead.
Request Body
{
"email": "jane@email.com", // required
"password": "Secure@123" // required
}
Response
// Without OTP:
{ "success": true, "data": { "requires_otp": false, "access_token": "eyJ...", "user": { "id", "name", "email", "role": null } } }
// With OTP enabled:
{ "success": true, "data": { "requires_otp": true, "otp_token": "eyJ..." } }
Possible Errors
| Code | HTTP | When |
|---|---|---|
| INVALID_CREDENTIALS | 401 | Wrong email or password |
/api/v1/auth/otp/validate
Public
Exchange OTP token + 6-digit code for a full access token (2FA login step 2).
Request Body
{
"otp_token": "eyJ...", // from login response when requires_otp: true
"code": "123456" // 6-digit TOTP code from authenticator app
}
Response
{ "success": true, "data": { "access_token": "eyJ...", "user": { ... } } }
Possible Errors
| Code | HTTP | When |
|---|---|---|
| INVALID_OTP | 401 | Wrong TOTP code |
| INVALID_TOKEN | 401 | Expired otp_token |
/api/v1/auth/logout
Public
Clear the HTTP-only refresh token cookie.
Response
{ "success": true, "message": "Logged out successfully." }
/api/v1/auth/refresh
Public
Get a new access token using the refresh cookie.
Response
{ "success": true, "data": { "access_token": "eyJ..." } }
Possible Errors
| Code | HTTP | When |
|---|---|---|
| AUTH_REQUIRED | 401 | No refresh cookie present |
| INVALID_TOKEN | 401 | Cookie expired |
/api/v1/auth/me
🔒 Auth Required
Get the current authenticated user profile.
Response
{ "success": true, "data": { "id": "uuid", "name": "Jane", "email": "...", "phone": "...", "avatar_url": null, "email_verified": true, "otp_enabled": false, "role": null, "preferences": {} } }
/api/v1/auth/me
🔒 Auth Required
Update name, phone, or avatar_url.
Request Body
{ "name": "Jane Smith", "phone": "0700000000", "avatar_url": "https://..." }
Response
{ "success": true, "data": { "id", "name", "email", "phone", "avatar_url" } }
/api/v1/auth/me/password
🔒 Auth Required
Change password. Requires current password for verification.
Request Body
{ "current_password": "OldPass@1", "new_password": "NewPass@2" }
Response
{ "success": true, "message": "Password updated successfully." }
Possible Errors
| Code | HTTP | When |
|---|---|---|
| INVALID_CREDENTIALS | 401 | Current password wrong |
| VALIDATION_ERROR | 422 | Missing fields |
/api/v1/auth/forgot-password
Public
Send a password reset link to the given email. Always returns success to prevent enumeration.
Request Body
{ "email": "jane@email.com" }
Response
{ "success": true, "message": "If that email exists, a password reset link has been sent." }
/api/v1/auth/reset-password
Public
Reset password using the token from the email link.
Request Body
{ "token": "hex-token", "email": "jane@email.com", "new_password": "NewPass@2" }
Response
{ "success": true, "message": "Password reset successfully. Please log in." }
Possible Errors
| Code | HTTP | When |
|---|---|---|
| INVALID_TOKEN | 400 | Token expired or incorrect |
/api/v1/auth/otp/setup
🔒 Auth Required
Generate a TOTP secret and QR code URL. Save the secret in your authenticator app.
Response
{ "success": true, "data": { "secret": "BASE32SECRET", "qr_code_url": "otpauth://totp/ALOPAY:jane@email.com?secret=...&issuer=ALOPAY" } }
/api/v1/auth/otp/verify
🔒 Auth Required
Confirm OTP setup by submitting the first 6-digit code. Enables 2FA on account.
Request Body
{ "code": "123456" }
Response
{ "success": true, "message": "Two-factor authentication is now enabled." }
Possible Errors
| Code | HTTP | When |
|---|---|---|
| INVALID_OTP | 401 | Wrong code |
/api/v1/auth/otp/disable
🔒 Auth Required
Disable 2FA. Requires password confirmation.
Request Body
{ "password": "Secure@123" }
Response
{ "success": true, "message": "Two-factor authentication disabled." }
Possible Errors
| Code | HTTP | When |
|---|---|---|
| INVALID_CREDENTIALS | 401 | Wrong password |
Addresses /api/v1/addresses
/api/v1/addresses
🔒 Auth Required
List all saved addresses for the current user.
Response
{ "success": true, "data": { "addresses": [ { "id", "label", "recipient_name", "phone", "street", "county", "town", "is_default" } ] } }
/api/v1/addresses
🔒 Auth Required
Add a new delivery address.
Request Body
{
"label": "Home", // required
"recipient_name": "Jane Doe", // required
"phone": "0712345678", // required
"street": "Kimathi Street", // required
"county": "nairobi", // required — must be valid Kenya county
"town": "Nairobi CBD", // optional
"landmark": "Next to KFC", // optional
"coordinates": { "lat": -1.286, "lng": 36.817 }, // optional
"is_default": true // optional
}
Response
{ "success": true, "data": { "address": { "id", "label", ... } } }
/api/v1/addresses/:id
🔒 Auth Required
Update a saved address. Same fields as POST.
Response
{ "success": true, "data": { "address": { ... } } }
/api/v1/addresses/:id
🔒 Auth Required
Delete a saved address.
Response
{ "success": true, "data": null }
/api/v1/addresses/:id/default
🔒 Auth Required
Set this address as the default. Clears default from all other addresses.
Response
{ "success": true, "data": { "address": { "id", "is_default": true } } }