Guides
Guides walk you through complete, real-world flows — from the first API call to a fully working outcome. Each step shows the exact endpoint, required fields, example request and response, and what to do if something goes wrong. Use the API Reference for detailed field-by-field documentation of each individual endpoint.

All API calls in these guides use the Work Benefit Public API at https://api-v2.workbenefits.app/api/public/v1 and require an X-Api-Key header on every request.

Keys starting with wb_test_ are sandbox keys and keys starting with wb_live_ are production keys. Both use the same base URL. Sandbox requests are routed to the sandbox environment automatically.

Every successful response has the shape { "status": "success", "message": "...", "data": ... }. Errors return { "status": "error", "message": "..." } with the matching HTTP status. List endpoints return data as { total, page, pageSize, totalPages, data: [...] }.

Each API key carries a set of scopes (for example COMPANY_WRITE or EWA_READ). A call without the required scope returns 403. You can only see and act on companies and employees created with your own developer account; anything else returns 404.
Company Registration
Everything you need to bring a new company live on Work Benefit — from creating the account to an approved, configured company with benefits enabled. Follow the steps in order; certain features are gated behind earlier steps.
Steps in this guide
01Create the companyPOST /companies
02Admin accepts invite & logs inWork Benefit platform
03Complete company profilePATCH /companies/:id
04Await platform approvalGET /companies/:id → status
05Enroll benefitsPOST /companies/:id/benefits
06Configure EWAPATCH /companies/:id/ewa/settings

Company status lifecycle

Every company starts as PENDING and must be reviewed by the Work Benefit team. You can keep configuring the company, enrolling benefits and onboarding employees while it is pending, but employees cannot request EWA until the status reaches ACCEPTED.

PENDING
→ platform approves
ACCEPTED
  or  
→ platform rejects
REJECTED
1
Create the company
Developer
Create the company record and provision the first admin account in a single API call. The company is created with PENDING status and the admin is invited by email with temporary login credentials.
POST /api/public/v1/companies COMPANY_WRITE
Request body
{
    "name": "Acme Corp",
    "emailDomain": "acmecorp.com",
    "isEmailDomainEnforced": true,
    "admin": {
        "firstName": "Jane",
        "lastName": "Doe",
        "email": "jane@acmecorp.com",
        "phoneNumber": "+233201234567",
        "position": "HR Manager"
    }
}
FieldTypeNotes
namestringrequired1–200 characters
emailDomainstringrequiredCompany domain, e.g. acmecorp.com
isEmailDomainEnforcedbooleanrequiredWhen true, employees must use a work email on this domain. When false, other domains are allowed but those links need company-admin approval.
admin.firstNamestringrequired1–100 characters
admin.lastNamestringrequired1–100 characters
admin.emailstringrequiredMust be unique across the platform
admin.phoneNumberstringoptionalInternational format — e.g. +233201234567. Must be unique if provided.
admin.positionstringoptionalAdmin's job title. Defaults to HR.
Response 201
{
    "status": "success",
    "message": "Company created successfully",
    "data": {
        "company": {
            "id": 42,
            "name": "Acme Corp",
            "code": "A1B2C3D4",
            "emailDomain": "acmecorp.com",
            "isEmailDomainEnforced": true,
            "status": "PENDING",
            "createdAt": "2026-04-24T09:00:00.000Z",
            "updatedAt": "2026-04-24T09:00:00.000Z",
            "profile": null
        },
        "admin": {
            "firstName": "Jane",
            "lastName": "Doe",
            "email": "jane@acmecorp.com"
        }
    }
}
Save company.id — you will need it for every subsequent company-scoped API call. A temporary password is auto-generated and sent to the admin's email along with an invitation to log in — you do not set it.

Common errors

StatusMessageFix
409An account with this email already existsUse a different admin email
409An account with this phone number already existsUse a different phone number or omit it
2
Admin accepts invite & logs in
Company Admin  ·  Work Benefit platform
After the company is created, Work Benefit sends the company admin an invitation email with a temporary password. This is not a step you call via the public API. The admin uses those credentials to sign in on the Work Benefit platform and can then manage the company account.
No API call required from your side. Communicate to the company admin that they will receive an invitation email from Work Benefit containing a temporary password and sign-in instructions. The admin account is created in an invited state and can access the platform using the credentials from that email.
No other registration steps depend on this. You can continue completing the company profile and configuration via the API while the admin accepts the invite and signs in on the Work Benefit platform.
3
Complete the company profile
Developer
Add business details. The Work Benefit review team uses this information during approval. All profile fields are nested under a profile key.
PATCH /api/public/v1/companies/:id COMPANY_WRITE

All fields are optional — send only what you want to set or update. Top-level name, emailDomain, and isEmailDomainEnforced can also be updated alongside profile.

Request body
{
    "name": "Acme Corp",
    "profile": {
        "industrySector": "TECHNOLOGY",
        "employeeCountRange": "FIFTY_ONE_TO_TWO_HUNDRED",
        "hasPhysicalOffice": true,
        "payrollSystem": "Sage",
        "incorporationCountry": "Ghana",
        "website": "https://acmecorp.com",
        "linkedIn": "https://linkedin.com/company/acme",
        "facebook": "https://facebook.com/acme"
    }
}

Profile fields

FieldTypeNotes
industrySectorstringAGRICULTURE CONSTRUCTION EDUCATION ENERGY FINANCE HEALTHCARE HOSPITALITY MANUFACTURING MEDIA MINING RETAIL TECHNOLOGY TELECOMMUNICATIONS TRANSPORTATION OTHER
employeeCountRangestringONE_TO_FIVE SIX_TO_TWENTY TWENTY_ONE_TO_FIFTY FIFTY_ONE_TO_TWO_HUNDRED TWO_HUNDRED_PLUS
hasPhysicalOfficebooleanWhether the company operates from a physical office
payrollSystemstringName of the payroll software in use, e.g. Sage
incorporationCountrystringCountry of incorporation
website / linkedIn / facebookstring (URL)Online presence links, max 500 characters each
logoUrl / incorporationCertUrlstring (URL)Links to the company logo and certificate of incorporation
workingDayStartDay / workingDayEndDayinteger 1–31Start and end day of the pay cycle. Used to calculate earned wages for EWA.
cutoffDayinteger 1–31Last day of the month employees can request EWA
repaymentDayinteger 1–31Day of the month advances are repaid from salary
maxSalaryExposurePercentageinteger 0–100Maximum share of salary that rent and other benefit repayments plus EWA can take together in a pay period
contactAdminIdintegerMust be the id of an admin of this company, otherwise 400
Response 200
{
    "status": "success",
    "data": {
        "id": 42, "name": "Acme Corp", "status": "PENDING",
        "profile": {
            "industrySector": "TECHNOLOGY",
            "employeeCountRange": "FIFTY_ONE_TO_TWO_HUNDRED",
            "payrollSystem": "Sage",
            "website": "https://acmecorp.com"
        }
    }
}
Set the pay cycle fields (workingDayStartDay, workingDayEndDay, cutoffDay, repaymentDay) before employees start requesting EWA. They control how earned wages, cut-off dates and repayment dates are calculated. The response returns the full company, including profile, locations and _count.
4
Await platform approval
Work Benefit Team
The Work Benefit team reviews the company profile and either approves or rejects. The company admin is notified by email when the status changes. There is no API call to trigger or accelerate this step — poll the company endpoint to detect the change.
GET /api/public/v1/companies/:id COMPANY_READ
Response 200 — poll until status changes
{
    "status": "success",
    "data": {
        "id": 42,
        "status": "ACCEPTED", // was "PENDING"
        "updatedAt": "2026-04-24T09:30:00.000Z",
        ...
    }
}
StatusWhat it meansWhat to do
PENDING Awaiting review by Work Benefit Make sure the profile is complete (Step 3). You can continue with Steps 5 and 6 while you wait.
ACCEPTED Company is live and approved Employees can now request EWA.
REJECTED Application was declined Contact Work Benefit support for the reason and next steps.
5
Enroll benefits
Developer
Enroll one or more benefits for the company. A new enrollment is ACTIVE, or WAITLISTED if the benefit itself is on a waitlist. When it is ACTIVE, every approved employee of the company is enrolled automatically. Start by fetching the available benefits to get the correct benefitId.

1 · List available benefits

GET /api/public/v1/benefits Any valid API key
Response 200
{
    "status": "success",
    "data": [
        {
            "id": 1,
            "name": "Earned Wage Access",
            "code": "EWA",
            "description": "...",
            "readMore": "...",
            "status": "ACTIVE",
            "tag": "...",
            "fixedPaymentOption": false
        },
        ...
    ]
}
Use code to find the benefit you need (for example EWA or RENT) and pass its id as benefitId.

2 · Enroll a benefit

POST /api/public/v1/companies/:companyId/benefits BENEFIT_WRITE
Request body
{ "benefitId": 1 }
Response 201
{
    "status": "success",
    "message": "Company benefit created successfully",
    "data": {
        "id": 3,
        "companyId": 42,
        "benefitId": 1,
        "status": "ACTIVE",
        "paymentOption": "PAYROLL_DEDUCTION",
        "createdAt": "2026-04-24T10:00:00.000Z",
        "updatedAt": "2026-04-24T10:00:00.000Z"
    }
}
data.id is the company benefit id. Use it (not the catalog benefitId) when you update the enrollment.
StatusMessageFix
404Benefit not foundUse an id from GET /benefits
404Company not foundUse a company created with your developer account
409Resource already existsThe benefit is already enrolled. Update it instead.

3 · Update or deactivate a benefit

PATCH /api/public/v1/companies/:companyId/benefits/:id BENEFIT_WRITE

:id is the company benefit id. Send at least one of status (ACTIVE or INACTIVE) and paymentOption (PAYROLL_DEDUCTION or EMPLOYEE_SELF_PAYMENT). Some benefits have a fixed payment option and return 400 if you try to change it. Setting a WAITLISTED benefit to INACTIVE removes the enrollment. Suspended benefits can only be managed by Work Benefit (403). To see current enrollments, call GET /api/public/v1/companies/:companyId/benefits (BENEFIT_READ).

6
Configure EWA settings
Developer
Define how EWA requests are handled for this company — auto-approval rules, withdrawal limits, and time-window restrictions. Default settings are created automatically with all rules disabled (fully manual approval). Configure before employees start submitting requests. Read the current settings with GET /api/public/v1/companies/:companyId/ewa/settings (EWA_READ).
PATCH /api/public/v1/companies/:companyId/ewa/settings EWA_WRITE

All fields are optional — send only what you want to change. By default, all rules are disabled and every EWA request requires manual approval.

Example A — Auto-approve all requests

{ "enabledAutoApprovalForAllEmployees": true }

Example B — Auto-approve up to 50% of accrued earnings

{
    "enabledAutoApprovalForAllEmployees": true,
    "hasPercentageOfAccruedWage": true,
    "percentageOfAccruedWage": 50
}

Example C — Auto-approve weekdays 8 am – 5 pm only

{
    "enabledAutoApprovalForAllEmployees": true,
    "enabledBetweenTimePeriods": true,
    "smartAutoApprovalStartTime": "08:00",
    "smartAutoApprovalEndTime": "17:00",
    "smartAutoApprovalDays": "WEEKDAYS"
}

Example D — Limit the number of requests per month

{
    "enabledAutoApprovalForAllEmployees": true,
    "hasMadeLessThanMaxRequestsPerMonth": true
}

The monthly request limit itself is set per employee (see Per-employee settings below).

All settings fields

FieldTypeDescription
enabledAutoApprovalForAllEmployeesbooleanMaster switch. When true, requests are auto-approved subject to the rules below. When false, only employees whose own approvalMode is AUTOMATIC can be auto-approved; everyone else needs manual approval.
hasPercentageOfAccruedWagebooleanEnable a cap on the percentage of accrued earnings that can be withdrawn
percentageOfAccruedWagenumber 0–100Used when hasPercentageOfAccruedWage is true. A request must be at or below this percentage of earned wages.
hasPermanentOrConfirmedEmployeeStatusbooleanOnly auto-approve employees whose company link is approved
hasRequestedLessThanMaxAmountPerCyclebooleanEnable a maximum total withdrawal amount per pay cycle
maxAmountPerCyclenumberUsed when hasRequestedLessThanMaxAmountPerCycle is true
hasMadeLessThanMaxRequestsPerMonthbooleanOnly auto-approve while the employee is under their maxRequestsPerMonth limit
hasPendingPaymentFromPriorCyclesbooleanAccepted and stored, but not applied to auto-approval at the moment
enabledBetweenTimePeriodsbooleanRestrict auto-approval to a specific time window
smartAutoApprovalStartTimestring HH:MMStart of the auto-approval window, 24-hour format. Set it when enabledBetweenTimePeriods is true.
smartAutoApprovalEndTimestring HH:MMEnd of the auto-approval window
smartAutoApprovalDaysstringWEEKDAYS, WEEKENDS, or EVERYDAY

Per-employee settings

PATCH /api/public/v1/companies/:companyId/ewa/employees/:linkId EWA_WRITE

Get each employee's linkId from GET /api/public/v1/companies/:companyId/ewa/employees (EWA_READ). Send at least one of: enrolled (boolean), maxWithdrawalPercentage (0–50), approvalMode (MANUAL or AUTOMATIC), maxRequestsPerMonth (1–50).

Multiple rules can be combined. A request is auto-approved only when it passes all enabled rules. For example, enabling both hasPercentageOfAccruedWage (50%) and enabledBetweenTimePeriods means requests are auto-approved only if they are within the time window AND within the wage cap.

What's next

The company is now live and fully configured. The next step is onboarding employees — continue to the Employee Onboarding Guide for the full flow covering registration, phone verification, selfie upload, company linking, and work email verification.

Employee Onboarding
Register an employee, verify their phone number, upload a selfie, link them to a company, and verify their work email. Follow the steps in order. If you include companyId and workEmail during registration, the company link is created in the same call and you can skip the separate link step.
The employee has to take part. Verification codes are sent by SMS to the employee's phone and by email to their work inbox. You cannot read these codes. Build a step in your app where the employee enters the code they received, then send it to the matching verify endpoint.
Steps in this guide
01Register employee accountPOST /employees
02Verify phone number (code from employee)POST /employees/:id/verify-phone
03Upload selfiePOST /employees/:employeeId/selfie
04Link employee to companyPOST /employees/:id/companies
05Verify work email (code from employee)POST /employees/:id/verify-work-email
06Await company approval if neededGET /employees/:id

Employee link status lifecycle

Every new company link starts as PENDING. After work email verification, the link is approved immediately if the email matches the company domain. Otherwise it stays pending until a company admin reviews it.

PENDING
→ verified company-domain email
APPROVED
  or  
→ needs company review
PENDING
  then  
→ admin approves or rejects
APPROVED
REJECTED
1
Register the employee account
Developer
Create the employee account in Work Benefit. Registration sends a 4-digit code by SMS to the employee's phone. If you also include companyId and workEmail, Work Benefit creates a pending company link and emails a separate 4-digit code to the work email.
POST /api/public/v1/employees EMPLOYEE_WRITE
Request body
{
    "firstName": "Ama",
    "lastName": "Mensah",
    "phoneNumber": "+233201234567",
    "companyId": 42,
    "workEmail": "ama@acmecorp.com"
}
FieldTypeNotes
firstNamestringrequired1–100 characters
lastNamestringrequired1–100 characters
phoneNumberstringrequiredInternational format, e.g. +233201234567. Receives the SMS code and must be unique.
companyIdintegeroptionalA company created with your developer account. If present, workEmail is also required.
workEmailstringoptionalIf present, companyId is also required. Must match the company domain when the domain is enforced.
Do not send gender or dateOfBirth here; the request is rejected with 400. Set them later with PATCH /api/public/v1/employees/:employeeId/profile.
Response 201
{
    "status": "success",
    "message": "Employee registered successfully",
    "data": {
        "id": 108,
        "firstName": "Ama",
        "lastName": "Mensah",
        "phoneNumber": "+233201234567",
        "gender": null,
        "dateOfBirth": null,
        "createdAt": "2026-04-24T10:15:00.000Z",
        "companyLinks": [
            {
                "id": 55,
                "workEmail": "ama@acmecorp.com",
                "emailVerified": false,
                "status": "PENDING",
                "createdAt": "2026-04-24T10:15:00.000Z",
                "company": { "id": 42, "name": "Acme Corp", "code": "A1B2C3D4", "status": "ACCEPTED" }
            }
        ]
    }
}
Save data.id. You will use it as the employee id in every later employee call.
StatusMessageFix
400Work email must use the company domain: acmecorp.comUse a work email on the company domain
404Company not foundUse a company created with your developer account
409An account with this phone number already existsThe phone number is already registered
2
Verify the employee's phone number
Employee  ·  Developer
Ask the employee for the 4-digit code they received by SMS and submit it. The code is valid for 10 minutes and allows 3 attempts. If it expires, request a new one.
POST /api/public/v1/employees/:id/verify-phone EMPLOYEE_WRITE
Request body
{ "code": "1234" }
Response 200
{
    "status": "success",
    "message": "Phone number verified successfully"
}
Need a new code? Call POST /api/public/v1/employees/:id/resend-phone-otp (no body) to send another SMS code.
StatusMessageFix
409Invalid OTP codeAsk the employee to check the code and try again
409OTP code has expiredResend the code
409Phone number is already verifiedContinue to the next step
3
Upload the employee's selfie
Employee  ·  Developer
A selfie is required for identity checks. It can only be uploaded after the phone number is verified. The photo must show one clear face.
POST /api/public/v1/employees/:employeeId/selfie EMPLOYEE_WRITE

Send the image as multipart/form-data in a field named selfie.

Request
curl -X POST https://api-v2.workbenefits.app/api/public/v1/employees/108/selfie \
    -H "X-Api-Key: wb_test_..." \
    -F "selfie=@ama.jpg"
Response 200
{
    "status": "success",
    "message": "Selfie uploaded successfully",
    "data": {
        "selfie": "https://.../employees/108/selfie/....jpg",
        "previousSelfie": null
    }
}
StatusMessageFix
400Selfie image is requiredSend the file in the selfie field
400Verify the employee's phone number before uploading a selfieComplete Step 2 first
400Face check failed (no face, several faces or poor quality)Ask the employee for a clear photo of their face only
4
Link the employee to a company
Developer
Create or re-initiate a company link for an existing employee. Skip this step if you already supplied companyId and workEmail during registration. Linking creates a PENDING link and emails a 4-digit code to the work email. An employee can have only one active company link at a time.
POST /api/public/v1/employees/:id/companies EMPLOYEE_WRITE
Request body
{
    "companyId": 42,
    "workEmail": "ama@acmecorp.com"
}
Response 201
{
    "status": "success",
    "data": {
        "id": 55,
        "companyId": 42,
        "workEmail": "ama@acmecorp.com",
        "status": "PENDING",
        "otpSent": true,
        "createdAt": "2026-04-24T10:15:00.000Z"
    }
}
StatusMessageFix
400A work email with the domain @acmecorp.com is required to link to this companyUse the company's domain when email-domain enforcement is enabled
404Company not foundUse a valid company linked to your developer account
409Your current company link must be revoked before linking to a new companyOnly one active company relationship is allowed at a time
409Your link to this company was rejected and cannot be re-attemptedRejected links are terminal for that employee-company pair
409This work email is already in use at this companyUse the employee's own work email
5
Verify the employee's work email
Employee  ·  Developer
Ask the employee for the 4-digit code sent to their work email and submit it with the companyId. After successful verification, the link becomes APPROVED immediately when the verified email matches the company domain. Otherwise, it remains PENDING until a company admin reviews it on the Work Benefit platform.
POST /api/public/v1/employees/:id/verify-work-email EMPLOYEE_WRITE
Request body
{
    "companyId": 42,
    "code": "4821"
}
Response 200
{
    "status": "success",
    "data": {
        "id": 55,
        "companyId": 42,
        "workEmail": "ama@acmecorp.com",
        "status": "APPROVED",
        "createdAt": "2026-04-24T10:15:00.000Z"
    }
}
Need a new code? Call POST /api/public/v1/employees/:id/resend-work-email-otp with { "companyId": 42 } to send another work email OTP.
The work email code is valid for 10 minutes with 3 attempts. If it expires, the pending link is deleted and you must link the company again (Step 4).
StatusMessageFix
409Invalid OTP codeAsk the employee to check the code and try again
409OTP has expired. Please re-initiate the link.Link the company again (Step 4)
409Work email is already verifiedContinue to Step 6
404Company link not foundCheck companyId, or link the company first
6
Await company approval if the link is still pending
Company Admin  ·  Work Benefit platform
No public API call is required for this review step. If the verified work email does not qualify for auto-approval, the company admin approves or rejects the employee from the Work Benefit company interface. Poll the employee record to monitor the current company-link status.
GET /api/public/v1/employees/:id EMPLOYEE_READ
Response 200
{
    "status": "success",
    "data": {
        "id": 108,
        "companyLinks": [
            {
                "id": 55,
                "workEmail": "ama.personal@gmail.com",
                "emailVerified": true,
                "status": "PENDING",
                "company": { "id": 42, "name": "Acme Corp" }
            }
        ]
    }
}
StatusWhat it meansWhat to do
PENDINGAwaiting company-admin reviewWait for the company admin to approve or reject in the Work Benefit platform
APPROVEDThe employee is successfully linked to the companyContinue with income verification and benefit usage flows
REJECTEDThe company declined the employee linkThis employee cannot re-attempt linking to the same company
Income Verification
Income verification has two parts that work together. First, the employee sets their salary on their profile. Then they submit supporting documents for admin review. In the public API, all of these routes live under the employee resource: /api/public/v1/employees/:employeeId/income.
Steps in this guide
01Set or update salaryPUT /employees/:employeeId/income
02Check verification statusGET /employees/:employeeId/income/verification
03Submit verification documentsPOST /employees/:employeeId/income/verification
04Update pending review after feedbackPATCH /employees/:employeeId/income/verification

Verification lifecycle

The employee can self-report salary before verification. Once an income verification is approved, that salary becomes locked. If an admin needs more evidence, the verification stays PENDING and the employee updates the existing request instead of creating a new one.

PENDING
→ admin approves
APPROVED
  or  
→ admin requests more evidence
PENDING
1
Set or update the employee's salary
Developer
The salary value is separate from the verification request. Use this endpoint to set the employee's self-reported income before verification. Once a verification has been approved, the salary is locked and this endpoint returns a conflict.

1 · Check current salary

GET /api/public/v1/employees/:employeeId/income EMPLOYEE_READ
Response 200
{
    "status": "success",
    "data": {
        "amount": "5000.00",
        "isVerified": false
    }
}

2 · Set or update salary

PUT /api/public/v1/employees/:employeeId/income EMPLOYEE_WRITE
Request body
{ "salary": 5000 }
Response 200
{
    "status": "success",
    "data": {
        "amount": "5000.00",
        "isVerified": false,
        "previousAmount": 4500
    }
}
Salary must be at least 800 and no more than 1,000,000.
StatusMessageFix
409Cannot update a verified salaryOnce verification is approved, treat salary as read-only
2
Check the employee's verification status
Developer
Always read the current verification state before deciding whether to submit a new request or update an existing pending one. The response changes depending on whether the employee has an approved verification, a pending verification, or both.
GET /api/public/v1/employees/:employeeId/income/verification EMPLOYEE_READ

Case A — first-time or still pending

{
    "status": "success",
    "data": {
        "id": 12,
        "status": "PENDING",
        "incomeAmount": "5000.00",
        "documents": [],
        "rfes": [
            { "id": 3, "adminComment": "Please provide a clearer bank statement" }
        ],
        "documentVerificationFee": "UNPAID"
    }
}

Case B — approved, no new review pending

{
    "status": "success",
    "data": {
        "id": 7,
        "status": "APPROVED",
        "incomeAmount": "4500.00",
        "verifiedAmount": "4500.00",
        "salaryReview": null
    }
}

Case C — approved, but a salary review is pending

{
    "status": "success",
    "data": {
        "id": 7,
        "status": "APPROVED",
        "incomeAmount": "4500.00",
        "salaryReview": {
            "id": 12,
            "status": "PENDING",
            "incomeAmount": "5500.00"
        }
    }
}
Amounts are returned as decimal strings. documentVerificationFee is PAID, WAIVED or UNPAID; it is waived automatically when the employee's work email matched the company domain. Approval and rejection are done by Work Benefit, not through the API.
There can only be one pending verification at a time. If a pending request already exists, update it with PATCH instead of submitting another POST.
3
Submit an income verification request
Developer
Submit the employee's income amount together with supporting documents. The employee can upload bank statements, mobile money statements, and additional files such as payslips or offer letters. This creates a new PENDING verification for admin review.
POST /api/public/v1/employees/:employeeId/income/verification EMPLOYEE_WRITE
Request body
{
    "monthlyIncome": 5000,
    "bank": [
        {
            "name": "January Statement",
            "bankName": "GCB Bank",
            "password": "abc123",
            "file": "data:application/pdf;base64,..."
        }
    ],
    "momo": [
        {
            "name": "MTN Jan",
            "file": "data:application/pdf;base64,..."
        }
    ],
    "otherFiles": [
        {
            "type": "PAY_SLIP",
            "file": "data:application/pdf;base64,..."
        }
    ]
}
FieldTypeNotes
monthlyIncomenumberrequiredMust be 0 or greater
bankarrayoptionalBank statement documents
momoarrayoptionalMobile money statement documents
otherFiles[].typestringPAY_SLIP, OFFER_LETTER, BUSINESS_REGISTRATION, or OTHER
otherFiles[].filestringBase64-encoded file content. No name field for other files.
Response 201
{
    "status": "success",
    "data": {
        "id": 42,
        "status": "PENDING",
        "incomeAmount": "5000.00",
        "documents": [
            { "id": 156, "documentType": "BANK_STATEMENT" },
            { "id": 157, "documentType": "PAY_SLIP" }
        ]
    }
}
StatusMessageFix
409A pending income verification already existsLoad the existing pending request and update it with PATCH instead
4
Update the pending verification after feedback
Developer
If the request is still pending, you can update the income amount and replace document groups. When you send a new bank, momo, or otherFiles array, the service deactivates the currently active documents for those document types and stores the new uploads instead.
PATCH /api/public/v1/employees/:employeeId/income/verification EMPLOYEE_WRITE

All fields are optional. Send only the parts that need to change.

Request body
{
    "monthlyIncome": 5500,
    "bank": [
        {
            "name": "Updated Statement",
            "bankName": "GCB Bank",
            "file": "data:application/pdf;base64,..."
        }
    ]
}
Response 200
{
    "status": "success",
    "data": {
        "id": 42,
        "status": "PENDING",
        "incomeAmount": "5500.00",
        "documents": [
            { "id": 201, "documentType": "BANK_STATEMENT", "isActive": true }
        ]
    }
}
If there is no pending income verification, this endpoint returns 404 with No pending income verification found. In that case, start a new request with POST instead.
EWA Request
Walk an employee through requesting part of their earned wages and withdrawing the approved funds to a bank account or mobile money wallet. The request and the withdrawal are separate. The request is approved first. The employee then sets a PIN (once) and confirms the withdrawal with that PIN on a page hosted by Work Benefit. The PIN is never sent through the API.
Steps in this guide
01Check available earned-wage amountGET /employees/:id/ewa
02Submit EWA requestPOST /employees/:id/ewa/requests
03Await approval if not auto-approvedGET /employees/:id/ewa/requests
04Set up PIN on hosted page (first time only)POST /employees/:id/pin/initialize
05Check transfer fee breakdownGET /employees/:id/payments/ewa/transfer/details
06Initialize withdrawal sessionPOST /employees/:id/payments/ewa/transfer/initialize
07Employee completes withdrawal on hosted pageWork Benefit hosted page

Prerequisites

Before any EWA request can be submitted, the following must all be true. The API returns a descriptive error if any condition is not met.

ConditionHow to satisfy it
Employee has an APPROVED link to a company with status ACCEPTEDComplete the Employee Onboarding flow first
The company has the EWA benefit enrolled and ACTIVE, and the employee is enrolled in itComplete Step 5 of the Company Registration flow. Approved employees are enrolled automatically.
The employee has a verified salary on fileComplete the Income Verification flow
The employee account is activeContact Work Benefit if the account was deactivated
No other EWA request is PENDINGWait for the existing request to be approved or rejected
The employee has a PIN (for withdrawals only)Step 4 of this guide

EWA request status lifecycle

Requests start as PENDING. Depending on the company's EWA auto-approval settings, a request may be approved immediately on submission or it may wait for manual approval by a company admin (or by you, through the company EWA endpoints).

PENDING
→ auto-approved or manual approval
APPROVED
  or  
→ declined
REJECTED
1
Check the employee's available earned-wage amount
Developer
Fetch the employee's current EWA position before submitting a request. This tells you how much has been earned so far this pay cycle, how much is still available to request, the service charge rate, and the cut-off date. Show this to the employee so they can choose a request amount.
GET /api/public/v1/employees/:employeeId/ewa EWA_READ
Response 200
{
    "status": "success",
    "data": {
        "earnedWage": 1820.00,
        "availableToUse": 1820.00,
        "maxAmountAllowed": 2100.00,
        "availableToWithdraw": 0.00,
        "serviceChargePercentage": 0.049,
        "maximumWithdrawalPercentage": 70,
        "totalRequestedAmount": 0.00,
        "totalRequests": 0,
        "cutoffDate": "2026-04-28T00:00:00.000Z",
        "repaymentDate": "2026-04-30T00:00:00.000Z",
        "linkId": 55,
        "message": "You can withdraw all of your estimated earned salary"
    }
}
FieldDescription
earnedWageWages accrued so far this pay cycle based on days worked
availableToUseAmount still available to request this cycle. The request amount must not be higher than this.
maxAmountAllowedUpper limit for this cycle, based on the employee's maximumWithdrawalPercentage and the company's salary exposure cap
availableToWithdrawApproved funds not yet transferred out — ready to withdraw now
serviceChargePercentageService charge rate applied to each request (0.049 = 4.9%)
cutoffDateDeadline for submitting EWA requests this pay cycle
repaymentDateDate the advance is deducted from the employee's salary
If the employee is not linked to a company yet, the amounts are 0, linkId is null and message explains why. Returns 400 if the employee has no salary on file.
2
Submit the EWA request
Developer
Submit the amount the employee wants to access. The platform checks auto-approval rules configured for the company (see Configure EWA settings). If all rules pass, the request is approved immediately and autoApproved is true. Otherwise it stays PENDING for manual review.
POST /api/public/v1/employees/:employeeId/ewa/requests EWA_WRITE
Request body
{ "amount": 500 }
Response 201
{
    "status": "success",
    "message": "Earned wage request submitted successfully",
    "data": {
        "id": 77,
        "reference": "EWA-20260427-A3BX9Z",
        "companyLinkId": 55,
        "amount": "500",
        "serviceCharge": "24.5",
        "status": "APPROVED",
        "requestDate": "2026-04-27T11:20:00.000Z",
        "cutOffDate": "2026-04-28T00:00:00.000Z",
        "repaymentDate": "2026-04-30T00:00:00.000Z",
        "approvedById": null,
        "createdAt": "2026-04-27T11:20:00.000Z",
        "updatedAt": "2026-04-27T11:20:00.000Z",
        "autoApproved": true
    }
}
StatusMessageFix
400You have a pending requestWait for the existing pending request to be approved or rejected before submitting a new one
400Amount requested exceeds available amount. Available: {n}Use a value ≤ availableToUse from Step 1
400EWA requests are no longer accepted for this pay cycle. The cut-off date has passed.Wait for the next pay cycle
404You are not associated with an active company. Please contact your administrator.The employee needs an approved link to an ACCEPTED company
403Earned Wage Access is not currently available at your company.Enroll the EWA benefit for the company and make sure it is ACTIVE
403You are not enrolled for Earned Wage Access. Please contact your administrator for assistance.Enroll the employee with PATCH /companies/:companyId/ewa/employees/:linkId and { "enrolled": true }
403Your salary details have not been verified yet. Please contact your administrator.Complete the income verification flow first
403Sandbox API keys cannot be used in a production environment.Use a wb_live_ key for production
Amounts in this response are decimal strings. If autoApproved is true, skip Step 3. The funds are ready to withdraw.
3
Await approval if not auto-approved
Company Admin  ·  Developer
If the request was not auto-approved, it waits for manual approval. A company admin can approve it on the Work Benefit platform, or you can approve or reject it on the company's behalf with PATCH /api/public/v1/companies/:companyId/ewa/requests/:requestId/approve or /reject (EWA_WRITE). Poll the requests list to see when the status changes to APPROVED or REJECTED.
GET /api/public/v1/employees/:employeeId/ewa/requests EWA_READ

Filter by status to narrow results. Supports pagination via page and pageSize (max 100).

Response 200
{
    "status": "success",
    "data": {
        "data": [
            {
                "id": 77,
                "reference": "EWA-20260427-A3BX9Z",
                "status": "APPROVED",
                "amount": 500,
                "serviceCharge": 24.5,
                "requestDate": "2026-04-27T11:20:00.000Z",
                "cutOffDate": "2026-04-28T00:00:00.000Z",
                "repaymentDate": "2026-04-30T00:00:00.000Z",
                "companyName": "Acme Corp"
            }
        ],
        "total": 1,
        "page": 1,
        "pageSize": 20,
        "totalPages": 1
    }
}
StatusWhat it meansWhat to do
PENDINGAwaiting manual reviewPoll again later
APPROVEDFunds are ready to withdrawContinue to Step 4
REJECTEDRequest was declinedInform the employee and let them submit a new request in the next cycle
4
Set up the employee's PIN (first time only)
Developer  ·  Employee
Every withdrawal is confirmed with the employee's 4-digit PIN. The PIN is set on a page hosted by Work Benefit, so you never handle it. Check the PIN status first and only run the setup if the employee has no PIN yet.

1 · Check whether the employee has a PIN

GET /api/public/v1/employees/:employeeId/pin/status EMPLOYEE_READ
Response 200
{
    "status": "success",
    "message": "PIN status fetched successfully",
    "data": { "hasPin": false }
}

If hasPin is true, skip to Step 5.

2 · Create a PIN setup session

POST /api/public/v1/employees/:employeeId/pin/initialize EMPLOYEE_WRITE
Request body
{ "redirectUrl": "https://yourapp.com/pin/done" }

redirectUrl is optional. It is where the employee is sent after setting the PIN.

Response 201
{
    "status": "success",
    "message": "PIN setup session created",
    "data": {
        "url": "https://api-v2.workbenefits.app/hosted/pin/3f9c...",
        "expiresAt": "2026-04-27T12:05:00.000Z"
    }
}

3 · Employee sets the PIN on the hosted page

No API call required from your side. Send the employee to url. They choose a 4-digit PIN and are then sent to your redirectUrl. The URL can be used once and expires after 30 minutes.
5
Check the transfer fee breakdown
Developer
Before the employee confirms the withdrawal, show them the fee and the amount they will receive. Bank transfers have a fixed fee of GHS 7 that is taken from the amount. Mobile money transfers have no fee.
GET /api/public/v1/employees/:employeeId/payments/ewa/transfer/details PAYMENT_READ

Pass accountType (BANK or MOBILE_MONEY) and amount as query parameters.

Request
GET /api/public/v1/employees/108/payments/ewa/transfer/details?accountType=BANK&amount=500
Response 200
{
    "status": "success",
    "data": {
        "fee": 7,
        "requestedAmount": 500,
        "receivedAmount": 493,
        "minimumAmount": 10
    }
}
This endpoint only calculates; nothing is deducted. The minimum withdrawal is GHS 10.
6
Initialize the withdrawal session
Developer
Create a hosted session for the withdrawal. Work Benefit returns a URL. Redirect the employee there or open it in a WebView. The employee enters their account details and PIN on that page, and the transfer starts from there.
POST /api/public/v1/employees/:employeeId/payments/ewa/transfer/initialize PAYMENT_WRITE
Request body
{
    "amount": 500,
    "accountType": "BANK",
    "redirectUrl": "https://yourapp.com/ewa/complete"
}
FieldTypeNotes
amountnumberrequiredAmount to withdraw, at least 1, up to 2 decimal places. It must also be at least GHS 10 and no more than availableToWithdraw when the employee submits.
accountTypestringrequiredBANK or MOBILE_MONEY
redirectUrlstring (URL)optionalWhere to send the employee after a successful withdrawal
Response 201
{
    "status": "success",
    "data": {
        "url": "https://api-v2.workbenefits.app/hosted/withdrawal/eyJhbGciO...",
        "expiresAt": "2026-04-27T12:05:00.000Z"
    }
}
The URL can be used once and expires after 30 minutes. If it expires, call this endpoint again to get a new one.
StatusMessageFix
403Your account has been deactivated. Please contact your administrator for assistance.The employee account is inactive
403Sandbox API keys cannot be used in a production environment.Use a wb_live_ key for production
404Employee not foundUse an employee registered with your developer account
7
Employee completes withdrawal on the hosted page
Employee  ·  Work Benefit hosted page
Send the employee to the URL from Step 6. For BANK, they pick their bank and enter the account number. For MOBILE_MONEY, they pick the network (MTN, Telecel or AirtelTigo) and enter the phone number. They then enter their 4-digit PIN and submit. Work Benefit starts the transfer.
No API call required from your side. On success the page sends the employee to redirectUrl, if you set one. If something is wrong (wrong PIN, no PIN set, amount above the available balance, below GHS 10, or above the GHS 2,500 daily limit), the page shows the error to the employee. Check the result in the employee's transaction history.

Check withdrawal outcome

GET /api/public/v1/employees/:employeeId/payments/history PAYMENT_READ

Supports page, pageSize (max 100) and type query parameters. type is one of requests, withdrawals, rent or shop. Use type=withdrawals to see only withdrawals. The result is grouped into separate lists, not the standard list envelope.

Response 200
{
    "status": "success",
    "message": "Transaction history retrieved",
    "data": {
        "requests": [ ... ],
        "withdrawals": [
            {
                "id": 203,
                "reference": "...",
                "status": "COMPLETED",
                "transferType": "BANK",
                "amount": "500",
                "transferFee": "7",
                "createdAt": "2026-04-27T11:45:00.000Z"
            }
        ],
        "external": [ ... ],
        "total": 1,
        "page": 1,
        "pageSize": 20
    }
}
Withdrawal status values: PROCESSING (transfer started, waiting for the payment provider), COMPLETED (funds delivered), FAILED (transfer failed).
Rent Benefit
Help an employee apply for rent financing through Work Benefit. The platform funds the move-in costs and the employee repays in monthly installments. The flow has five distinct stages — each one must be completed before the next can begin. Payments are handled via Paystack checkout links returned by the API.
Steps in this guide
01Check eligibility & preview costsGET /rent/limit · GET /rent/calculate
02Submit rent applicationPOST /rent/applications
03Pay physical inspection feePOST /rent/applications/:id/initialize-physical-inspection-payment
04Await Work Benefit reviewGET /rent/applications/:id → stage
05Sign tenancy agreementPATCH /rent/applications/:id/sign-tenancy-agreement
06Pay initial (move-in) depositPOST /rent/applications/:id/initialize-initial-deposit-payment
07Make monthly rent paymentsPOST /rent/applications/:id/pay

Application stage lifecycle

Each application moves through five stages in order. Poll GET /rent/applications/:id and read the stage field to know what to do next.

CREATED_APPLICATION
→pay inspection
PAID_PHYSICAL_INSPECTION
→review, then sign
AGREED_TO_PAYMENT_PLAN
→pay deposit
PAID_INITIAL_DEPOSIT
→monthly payments
COMPLETED_RENT_PAYMENT

Prerequisites

All of the following must be in place before a rent application can be submitted.

ConditionHow to satisfy it
Employee has a salary on fileSet via PUT /employees/:id/income — does not need to be verified first
Employee has an emergency contactAdd via POST /employees/:id/emergency-contact
Employee is linked to a company with Rent benefit ACTIVEComplete Employee Onboarding and enroll the Rent benefit via Company Registration Step 5
Identity verification documents (Ghana Card / passport) and income verification documents are passed to Renmo when you create the application. Submitting them in advance via /identity-verification and /income/verification ensures Renmo has the evidence it needs to approve the application faster.
1
Check eligibility & preview costs
Developer
Before creating an application, show the employee how much they can borrow and what their monthly repayments would look like. The rent limit is based on the employee's salary: up to 30% of monthly salary, over a maximum of 12 months. Use the calculator to preview different amount and duration combinations.

1 · Get the employee's rent limit

GET /api/public/v1/employees/:employeeId/rent/limit BENEFIT_READ
Response 200
{
    "status": "success",
    "data": {
        "maxMonthlyPayment": 2028,
        "maxTotalPayback": 18720,
        "maxPaybackPeriod": 12,
        "premuimRate": 0.025
    }
}
The rate field is spelled premuimRate in the response. Returns 404 if the employee has no salary on file. maxMonthlyPayment and maxTotalPayback are the ceiling. The employee can choose a lower amount or shorter duration. Use the calculator below to show them the cost for any combination.

2 · Preview costs for a specific amount & duration

GET /api/public/v1/employees/:employeeId/rent/calculate BENEFIT_READ

Pass amount (the rent cost to finance) and paybackDuration (months to repay) as query parameters. premiumRate is optional; the default is 2.5% per month.

Request
GET /api/public/v1/employees/108/rent/calculate?amount=5000&paybackDuration=6
Response 200
{
    "status": "success",
    "data": {
        "totalPayback": 5750,
        "monthlyRent": 958
    }
}

3 · Check physical inspection fees

GET /api/public/v1/employees/:employeeId/rent/physical-inspection-fees BENEFIT_READ
Response 200
{
    "status": "success",
    "data": {
        "standardFee": 130,
        "expressFee": 250
    }
}
The physical inspection fee is non-refundable and paid directly by the employee before the application can progress. The express service schedules the inspection sooner.

4 · Check qualification (optional)

GET /api/public/v1/employees/:employeeId/rent/qualify BENEFIT_READ

Pass amount and paybackDuration as query parameters. The check uses the employee's income and employment details.

Response 200
{
    "status": "success",
    "message": "Rent qualification checked successfully",
    "data": { "results": "P" }
}

results is "P" when the employee qualifies, or "F" with a message when they do not.

2
Submit the rent application
Developer
Create the application with the employee's rent budget, desired duration, and landlord details. Work Benefit collects the employee's existing salary, identity documents, and income verification documents from the system automatically — you do not re-upload them here.
POST /api/public/v1/employees/:employeeId/rent/applications BENEFIT_WRITE
Request body
{
    "monthlyRentBudget": 1200,
    "durationOfRent": 12,
    "rentPaybackPeriod": 6,
    "fullNameOfLandlord": "Kwame Asante",
    "phoneNumberOfLandlord": "+233201234567",
    "propertyGpsAddress": "GS-0001-0001"
}
FieldTypeNotes
monthlyRentBudgetnumberrequiredThe employee's monthly rent cost in GHS
durationOfRentintegerrequiredHow many months the tenancy lasts (minimum 1)
rentPaybackPeriodintegerrequiredHow many months to repay the advance (minimum 1). Keep it within maxPaybackPeriod from the limit.
fullNameOfLandlordstringoptionalLandlord's full name
phoneNumberOfLandlordstringoptionalLandlord's contact number
propertyGpsAddressstringoptionalGhana Post GPS address of the property
Response 201 — application data from Renmo
{
    "status": "success",
    "data": {
        "id": 8801,
        "application_date": "2026-04-27",
        "monthly_rent_value": "1200",
        "payback_duration": 6,
        ...
    }
}
Save data.id as the applicationId. You will need it for every subsequent step in this flow.
StatusMessageFix
404No emergency contact foundAdd an emergency contact via POST /employees/:id/emergency-contact first
404No salary information foundSet salary via PUT /employees/:id/income first
400You are not eligible for this benefit. A minimum monthly salary of GHS 1,500 is required.The employee's salary is below the minimum
3
Pay the physical inspection fee
Developer  ·  Employee
Work Benefit sends an agent to physically inspect the property before committing to fund the rent. The employee pays a non-refundable inspection fee via Paystack. Initialize the payment to get a checkout link, then redirect the employee there to complete the payment. Optionally book an express slot for a faster inspection.
POST /api/public/v1/employees/:employeeId/rent/applications/:applicationId/initialize-physical-inspection-payment BENEFIT_WRITE
Request body
{
    "expressServiceForPhysicalInspection": false,
    "dateAndTimeForVerification": "2026-05-02"
}
FieldTypeNotes
expressServiceForPhysicalInspectionbooleanoptionalWhen true, charges GHS 250 (express). When false or omitted, charges GHS 130 (standard).
dateAndTimeForVerificationstringoptionalPreferred inspection date, format YYYY-MM-DD
Response 201 — Paystack checkout
{
    "status": "success",
    "data": {
        "authorization_url": "https://checkout.paystack.com/...",
        "access_code": "...",
        "reference": "..."
    }
}

Redirect the employee to authorization_url. Once they complete the payment, the application stage advances to PAID_PHYSICAL_INSPECTION.

4
Await Work Benefit review
Work Benefit Team
After the inspection fee is paid, a Work Benefit agent visits the property and reviews the application. If approved, the team sets up the payment plan and move-in deposit. There is no API call to trigger this. Poll the application until paymentSchedule and moveInDepositBreakdown are filled in.
GET /api/public/v1/employees/:employeeId/rent/applications/:applicationId BENEFIT_READ
Response 200
{
    "status": "success",
    "data": {
        "applicationId": 8801,
        "stage": "PAID_PHYSICAL_INSPECTION",
        "moveInDepositBreakdown": {
            "monthlyRent": 1200,
            "refundableRentSecurity": 1200,
            "serviceFee": 300,
            "extraPayment": 0,
            "proRatedFirstMonth": 0,
            "totalMoveInDeposit": 2700
        },
        "paymentSchedule": [ ... ]
    }
}

Once the payment plan is in place, the tenancy agreement is ready to review and sign. Continue to Step 5.

5
Sign the tenancy agreement
Developer  ·  Employee
Generate a link for the employee to read the tenancy agreement, then record their signature via the API. You can also email the agreement to the employee or their landlord before signing.

1 · Generate the tenancy agreement link

GET /api/public/v1/employees/:employeeId/rent/applications/:applicationId/generate-tenancy-agreement-link BENEFIT_READ
Response 200
{
    "status": "success",
    "data": {
        "link": "https://api-v2.workbenefits.app/api/v2/employee/rent/tenancy-agreement/eyJ..."
    }
}

2 · Optionally email the agreement

POST /api/public/v1/employees/:employeeId/rent/applications/:applicationId/send-tenancy-agreement-email BENEFIT_WRITE
Request body
{ "email": "ama@acmecorp.com" }

3 · Record the employee's signature

PATCH /api/public/v1/employees/:employeeId/rent/applications/:applicationId/sign-tenancy-agreement BENEFIT_WRITE

No request body required. Calling this endpoint records the employee's acceptance of the tenancy agreement terms.

Response 200
{
    "status": "success",
    "data": { "tenant_has_signed": "yes", ... }
}
After signing, the application stage becomes AGREED_TO_PAYMENT_PLAN and the initial deposit payment can be initialized.
6
Pay the initial (move-in) deposit
Developer  ·  Employee
The initial deposit covers the first month's rent, a refundable rent security, and a service fee. Read the totalMoveInDeposit breakdown from Step 4, then initialize a Paystack payment for that total. Once paid, Work Benefit disburses funds to the landlord and the stage advances to PAID_INITIAL_DEPOSIT.
POST /api/public/v1/employees/:employeeId/rent/applications/:applicationId/initialize-initial-deposit-payment BENEFIT_WRITE

No request body required. The amount is taken from the application's initial deposit. Returns 400 if the deposit is not available yet.

Response 201 — Paystack checkout
{
    "status": "success",
    "data": {
        "authorization_url": "https://checkout.paystack.com/...",
        "access_code": "...",
        "reference": "..."
    }
}

Redirect the employee to authorization_url. After payment, the stage advances to PAID_INITIAL_DEPOSIT and the employee can move in.

7
Make monthly rent payments
Developer  ·  Employee
Each month, the employee repays a portion of the advance. Fetch the payment plan to see all upcoming and past installments, then initialize a Paystack payment for the amount due. Once all installments are paid, the stage advances to COMPLETED_RENT_PAYMENT.

1 · Get the payment plan

GET /api/public/v1/employees/:employeeId/rent/applications/:applicationId/payment-plan BENEFIT_READ
Response 200
{
    "status": "success",
    "data": [
        {
            "applicationId": 8801,
            "rentMonth": "May",
            "rentYear": 2026,
            "amount": 958,
            "amountPaid": 0,
            "totalAmountDue": 958,
            "fines": 0,
            "isLate": false,
            "paymentStatus": "Unpaid",
            "expectedPaymentDate": "2026-05-30"
        },
        ...
    ]
}

2 · Initialize a monthly payment

POST /api/public/v1/employees/:employeeId/rent/applications/:applicationId/pay BENEFIT_WRITE
Request body
{ "amount": 958 }
Response 201 — Paystack checkout
{
    "status": "success",
    "data": {
        "authorization_url": "https://checkout.paystack.com/...",
        "reference": "..."
    }
}

3 · View payment history

GET /api/public/v1/employees/:employeeId/rent/applications/:applicationId/payments BENEFIT_READ
Response 200
{
    "status": "success",
    "data": [
        {
            "id": 451,
            "paymentPurpose": "PHYSICAL_VERIFICATION_FEE",
            "amountPaid": 130,
            "transactionReference": "ps_ref_abc123",
            "datePaymentWasMade": "2026-04-27",
            "paymentMonth": null,
            "paymentTitle": "Physical Inspection Fee"
        },
        ...
    ]
}
paymentPurpose values: PHYSICAL_VERIFICATION_FEE, INITIAL_DEPOSIT, RENT_PAYMENT. Use this history to reconcile what the employee has paid and what remains outstanding.