Authentication

The Batchmates API supports two authentication methods: JWT tokens for mobile and stateless clients, and session-based authentication for web admin interfaces.


Authentication Methods

JWT Tokens (Mobile)

Stateless token-based authentication for mobile apps and third-party integrations.

Best for:

  • Mobile applications
  • Third-party integrations
  • Stateless API clients

Features:

  • 30-minute access token expiration
  • 6-month refresh token for seamless renewal
  • No server-side session storage

Session-based (Web)

Cookie-based authentication for browser applications.

Best for:

  • Web admin panels
  • Browser-based dashboards

Features:

  • Automatic cookie handling
  • No tokens returned
  • Server session lifetime

JWT Authentication (Mobile)

Base URL: https://batchmates-v2.revlv.com/api/v1/mobile/auth

Register

Creates a new user account and sends a 6-digit verification code to their email.

POST
/v1/mobile/auth/register
curl -X POST https://batchmates-v2.revlv.com/api/v1/mobile/auth/register \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "first_name": "Juan",
    "last_name": "Dela Cruz",
    "email": "donor@example.com",
    "password": "your-password",
    "password_confirmation": "your-password",
    "device_name": "iPhone 15"
  }'

Request Body

  • Name
    first_name
    Type
    string
    Description

    User's first name (max 255 characters)

  • Name
    last_name
    Type
    string
    Description

    User's last name (max 255 characters)

  • Name
    email
    Type
    string
    Description

    Must be unique across all users

  • Name
    password
    Type
    string
    Description

    Minimum 8 characters

  • Name
    password_confirmation
    Type
    string
    Description

    Must match password

  • Name
    device_name
    Type
    string
    Description

    Device name for identification (max 255 characters)

  • Name
    device_token
    Type
    string
    Description

    Push notification token (max 500 characters)

Response 201

{
  "success": true,
  "data": {
    "id": 5,
    "email": "donor@example.com",
    "email_verified": false,
    "can_resend_at": "2026-03-02T10:01:00.000000Z"
  },
  "message": "Registration successful. Please check your email for verification code."
}

The email contains a 6-digit numeric code (e.g. 482910) — not a clickable link. The code expires in 15 minutes.


Verify Email

Verifies the 6-digit code. On success, the user is immediately logged in — no separate login step required.

POST
/v1/mobile/auth/verify-email
curl -X POST https://batchmates-v2.revlv.com/api/v1/mobile/auth/verify-email \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "email": "donor@example.com",
    "code": "482910",
    "device_name": "iPhone 15"
  }'

Request Body

  • Name
    email
    Type
    string
    Description

    The email address being verified

  • Name
    code
    Type
    string
    Description

    6-digit code from the verification email. Field name is code, not token.

  • Name
    device_name
    Type
    string
    Description

    Device name used to identify the session. Required for mobile.

Response 200

{
  "success": true,
  "data": {
    "user": {
      "id": 5,
      "first_name": "Juan",
      "last_name": "Dela Cruz",
      "name": "Juan Dela Cruz",
      "email": "donor@example.com",
      "phone": null,
      "avatar": null,
      "status": "active",
      "email_verified": true,
      "roles": ["donor"],
      "permissions": [],
      "created_at": "2026-03-02T10:00:00.000000Z",
      "updated_at": "2026-03-02T10:00:00.000000Z"
    },
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
    "refresh_token": "def50200e8d...",
    "token_type": "bearer",
    "expires_in": 1800
  },
  "message": "Email verified successfully. You are now logged in."
}

Resend Verification Code

Sends a new 6-digit code. A 60-second cooldown is enforced between requests.

POST
/v1/mobile/auth/resend-verification
curl -X POST https://batchmates-v2.revlv.com/api/v1/mobile/auth/resend-verification \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "email": "donor@example.com" }'

Response 200

{
  "success": true,
  "message": "Verification code sent successfully.",
  "can_resend_at": "2026-03-02T10:02:00.000000Z"
}

Response 429 — Cooldown active

{
  "success": false,
  "message": "Please wait 47 seconds before requesting a new code.",
  "can_resend_at": "2026-03-02T10:01:47.000000Z",
  "seconds_left": 47
}

Check Verification Status

Check whether an email has been verified and whether a resend is currently allowed.

POST
/v1/mobile/auth/check-verification-status
curl -X POST https://batchmates-v2.revlv.com/api/v1/mobile/auth/check-verification-status \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "email": "donor@example.com" }'

Response 200

{
  "success": true,
  "data": {
    "email_verified": false,
    "can_resend": true,
    "seconds_until_resend": 0
  }
}

Login

Authenticates an existing verified user and returns JWT tokens.

POST
/v1/mobile/auth/login
curl -X POST https://batchmates-v2.revlv.com/api/v1/mobile/auth/login \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "email": "donor@example.com",
    "password": "your-password",
    "device_name": "iPhone 15"
  }'

Request Body

  • Name
    email
    Type
    string
    Description

    User's email address

  • Name
    password
    Type
    string
    Description

    User's password

  • Name
    device_name
    Type
    string
    Description

    Device name for session identification. Required for mobile — omitting returns a 422 error.

  • Name
    device_token
    Type
    string
    Description

    Push notification token (max 500 characters)

Response 200

{
  "success": true,
  "data": {
    "user": {
      "id": 5,
      "first_name": "Juan",
      "last_name": "Dela Cruz",
      "name": "Juan Dela Cruz",
      "email": "donor@example.com",
      "phone": null,
      "avatar": null,
      "status": "active",
      "email_verified": true,
      "roles": ["donor"],
      "permissions": [],
      "last_login_at": "2026-03-02T10:00:00.000000Z"
    },
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
    "refresh_token": "def50200e8d...",
    "token_type": "bearer",
    "expires_in": 1800
  },
  "message": "Logged in successfully"
}
  • Name
    access_token
    Type
    string
    Description

    JWT for API authentication. Valid for 30 minutes (expires_in: 1800).

  • Name
    refresh_token
    Type
    string
    Description

    Used to get a new access token. Valid for 6 months.

  • Name
    token_type
    Type
    string
    Description

    Always "bearer"


Using the Access Token

Include the JWT in every authenticated request:

curl https://batchmates-v2.revlv.com/api/v1/profile \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc..." \
  -H "Accept: application/json"
const response = await fetch('https://batchmates-v2.revlv.com/api/v1/profile', {
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Accept': 'application/json',
  },
})

Refresh Token

When the access token expires after 30 minutes, use the refresh token to get a new one.

POST
/v1/mobile/auth/refresh
curl -X POST https://batchmates-v2.revlv.com/api/v1/mobile/auth/refresh \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "refresh_token": "def50200e8d..." }'

Response 200

{
  "success": true,
  "data": {
    "user": { "..." },
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
    "token_type": "bearer",
    "expires_in": 1800
  }
}

The response does not include a new refresh token. The original refresh token remains valid for 6 months from when it was issued.


Logout

Invalidates the current access and refresh tokens.

POST
/v1/mobile/auth/logout
curl -X POST https://batchmates-v2.revlv.com/api/v1/mobile/auth/logout \
  -H "Authorization: Bearer {access_token}" \
  -H "Accept: application/json"

Response 200

{ "success": true, "message": "Logged out successfully" }

Session Authentication (Web)

Base URL: https://batchmates-v2.revlv.com/api/v1/web/auth

Session-based auth uses HTTP-only cookies. No tokens are returned — the browser stores and sends the cookie automatically.

Register

POST
/v1/web/auth/register
curl -X POST https://batchmates-v2.revlv.com/api/v1/web/auth/register \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "first_name": "Juan",
    "last_name": "Dela Cruz",
    "email": "admin@institution.edu",
    "password": "your-password",
    "password_confirmation": "your-password"
  }'

Response 201

{
  "success": true,
  "data": {
    "id": 10,
    "email": "admin@institution.edu",
    "email_verified": false,
    "can_resend_at": "2026-03-02T10:01:00.000000Z"
  },
  "message": "Registration successful. Please check your email for verification code."
}

Verify Email

POST
/v1/web/auth/verify-email
curl -X POST https://batchmates-v2.revlv.com/api/v1/web/auth/verify-email \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -c cookies.txt \
  -d '{
    "email": "admin@institution.edu",
    "code": "482910"
  }'

Field name is code, not token. On success the session cookie is set automatically — no separate login step needed.

Response 200

{
  "success": true,
  "data": {
    "id": 10,
    "first_name": "Juan",
    "last_name": "Dela Cruz",
    "name": "Juan Dela Cruz",
    "email": "admin@institution.edu",
    "status": "active",
    "email_verified": true,
    "roles": ["institution_admin"],
    "permissions": []
  },
  "message": "Email verified successfully. You are now logged in."
}

Resend Verification Code

POST
/v1/web/auth/resend-verification
curl -X POST https://batchmates-v2.revlv.com/api/v1/web/auth/resend-verification \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "email": "admin@institution.edu" }'

Response 200

{
  "success": true,
  "message": "Verification code sent successfully.",
  "can_resend_at": "2026-03-02T10:02:00.000000Z"
}

Check Verification Status

POST
/v1/web/auth/check-verification-status
curl -X POST https://batchmates-v2.revlv.com/api/v1/web/auth/check-verification-status \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "email": "admin@institution.edu" }'

Response 200

{
  "success": true,
  "data": {
    "email_verified": false,
    "can_resend": true,
    "seconds_until_resend": 0
  }
}

Login

POST
/v1/web/auth/login
curl -X POST https://batchmates-v2.revlv.com/api/v1/web/auth/login \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -c cookies.txt \
  -d '{
    "email": "admin@institution.edu",
    "password": "your-password"
  }'

Response 200

{
  "success": true,
  "data": {
    "id": 10,
    "first_name": "Juan",
    "last_name": "Dela Cruz",
    "name": "Juan Dela Cruz",
    "email": "admin@institution.edu",
    "institution_id": 1,
    "institution": { "id": 1, "name": "Xavier University" },
    "status": "active",
    "email_verified": true,
    "roles": ["institution_admin"],
    "permissions": [],
    "last_login_at": "2026-03-02T10:00:00.000000Z"
  },
  "message": "Logged in successfully"
}

No tokens in the response. The session cookie is set automatically by the browser.


Using Sessions

Include credentials: 'include' so the cookie is sent automatically:

const response = await fetch('https://batchmates-v2.revlv.com/api/v1/campaigns', {
  credentials: 'include',
  headers: { 'Accept': 'application/json' },
})
# With cURL, use the cookie file saved during login
curl https://batchmates-v2.revlv.com/api/v1/campaigns \
  -H "Accept: application/json" \
  -b cookies.txt

Logout

POST
/v1/web/auth/logout
curl -X POST https://batchmates-v2.revlv.com/api/v1/web/auth/logout \
  -H "Accept: application/json" \
  -b cookies.txt

Response 200

{ "success": true, "message": "Logged out successfully" }

Role-Based Access Control

  • Name
    system_admin
    Description

    Full access across all institutions — manage users, campaigns, institutions, and system settings

  • Name
    institution_admin
    Description

    Manage campaigns, users, bank accounts, and committees within their institution

  • Name
    committee_member
    Description

    Approve and reject campaigns, manage withdrawals, view institution data

  • Name
    donor
    Description

    Browse campaigns, create donations, manage their own profile

Permission Matrix

Permissionsystem_admininstitution_admincommittee_memberdonor
Manage institutions
Manage users
Suspend users
View campaigns
Create campaigns
Approve/reject campaigns
Create donations
View donations
View withdrawals
Create withdrawals
Approve withdrawals
View audit logs

Roles are returned in every auth response under "roles": ["role_name"]. The permissions array is always empty — permissions are enforced server-side only.


Password Reset

Base URL: https://batchmates-v2.revlv.com/api/v1/auth

These endpoints are unauthenticated and work for both mobile and web clients.

Password reset uses a 3-step OTP flow: request a code → verify the code → submit new password. No deep links or email links are used.

Forgot Password

Generates a 6-digit OTP and emails it to the user. Always returns a generic success response to prevent email enumeration.

POST
/v1/auth/forgot-password
curl -X POST https://batchmates-v2.revlv.com/api/v1/auth/forgot-password \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "email": "donor@example.com" }'

Request Body

  • Name
    email
    Type
    string
    Description

    The email address associated with the account

Response 200

{
  "message": "If that email exists, a reset code has been sent"
}

The email contains a 6-digit numeric OTP valid for 15 minutes. The OTP is stored as a SHA-256 hash in password_reset_tokens. Each new request resets the code and clears attempt counters.


Verify Reset Token

Validates the 6-digit OTP before showing the new password form. Use this as step 2 to confirm the code is correct and unexpired before asking the user to enter a new password.

POST
/v1/auth/verify-reset-token
curl -X POST https://batchmates-v2.revlv.com/api/v1/auth/verify-reset-token \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "email": "donor@example.com",
    "token": "123456"
  }'

Request Body

  • Name
    email
    Type
    string
    Description

    The email address associated with the account

  • Name
    token
    Type
    string
    Description

    The 6-digit OTP from the email (exactly 6 numeric characters)

Response 200 — OTP valid

{ "valid": true, "message": "Token is valid" }

Response 422 — OTP invalid, expired, or locked out

{ "valid": false, "message": "Invalid code. 3 attempts remaining." }
{ "valid": false, "message": "Too many failed attempts. Please request a new code." }

Security rules:

  • Maximum 5 attempts per OTP — exceeded attempts lock out the token entirely
  • OTP expires after 15 minutes
  • Failed attempts are counted and returned in the message

Reset Password

Resets the password using the verified 6-digit OTP. The OTP is re-validated on this request as well.

POST
/v1/auth/reset-password
curl -X POST https://batchmates-v2.revlv.com/api/v1/auth/reset-password \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "email": "donor@example.com",
    "token": "123456",
    "password": "newpassword456",
    "password_confirmation": "newpassword456"
  }'

Request Body

  • Name
    email
    Type
    string
    Description

    The email address associated with the account

  • Name
    token
    Type
    string
    Description

    The 6-digit OTP from the email

  • Name
    password
    Type
    string
    Description

    New password (minimum 8 characters)

  • Name
    password_confirmation
    Type
    string
    Description

    Must match password

Response 200

{ "message": "Password has been reset successfully" }

Response 422 — Invalid OTP or validation error

{ "message": "Invalid or expired reset code." }

On success, the password_reset_tokens row is deleted and the user's password is updated immediately.


Common Authentication Errors

StatusMeaning
400Wrong or expired verification code
401Missing or invalid token / session
403Unverified email; suspended account; insufficient role
422Validation error — missing required field
429Resend cooldown active; rate limit hit
// 403 — email not verified
{
  "success": false,
  "message": "Please verify your email before logging in.",
  "requires_verification": true,
  "email": "donor@example.com"
}

// 422 — missing device_name (mobile)
{
  "message": "The given data was invalid.",
  "errors": {
    "device_name": ["The device name field is required."]
  }
}

JWT vs Session — Key Differences

JWT (Mobile)Session (Web)
Tokens returnedaccess_token + refresh_tokenNone
Auth headerAuthorization: Bearer <token>N/A
CookieNoneHTTP-only session cookie
Access token expiry30 minutesServer session lifetime
Refresh token expiry6 monthsN/A
device_nameRequired for login + verifyNot required
Refresh endpointPOST /mobile/auth/refreshN/A

Security Notes

OTP / Verification Code Storage

Verification codes (6-digit email codes) are stored as SHA-256 hashes — never in plaintext. After 5 consecutive failed verification attempts, the code is automatically invalidated and a new one must be requested.

JWT Token Invalidation

Each user has a token_version counter embedded in every issued JWT. The ValidateJwtVersion middleware rejects tokens whose embedded version is lower than the current value.

POST /api/v1/mobile/auth/logout-all increments token_version, immediately invalidating all active tokens for the user across all devices. Use this when a device is lost or compromised.

Password Reset Security (Web Only)

The web reset-password page clears the token and email query params from the browser's URL immediately after reading them, so they do not persist in browser history or appear in referrer headers. This is a web-client-only behaviour — the mobile deep link flow is unaffected.


Was this page helpful?