Skip to main content

Payroll & Statutory Compliance (PAY)

Module code PAY · Release R1 (MVP); Form 16, 24Q data, journal export and VDA in R2 · Packaging Core · Requirements FR-PAY-*, BR-PAY-*, RULE-PAY-* in the PRD and BRD · Related Attendance, Leave, Claims, Exit, Core HR

:::caution Statutory figures are examples Every rate, ceiling, slab and threshold on this page is an example to verify against the current notification before use. In the product they are reference data in a versioned statutory pack published by the platform (see Platform), never constants in code. :::

1. Overview and scope​

PAY turns compensation, attendance and one-time inputs into correct, reproducible net pay and the statutory outputs that go with it. It is the main reason SMEs buy the product and the riskiest module in the MVP. The design rests on four ideas:

  1. Salary structures (templates of components and formulas) are separate from each employee's compensation record (effective-dated CTC + structure version).
  2. A pure, deterministic calculation library (packages/payroll-engine) runs gross-to-net from a frozen input snapshot. It has golden tests.
  3. A full pay-run lifecycle with validation, review, approval, lock, payment and reversal. Locked runs are immutable.
  4. Statutory reference data (EPF, ESI, PT, LWF, TDS slabs, minimum wages) is versioned by effective date and state.

In scope (R1)

  • Components, structures, compensation assignment and revision (with arrears), CTC breakup.
  • Pay schedules and periods, including an attendance cut-off different from the pay month.
  • Regular and off-cycle runs, salary hold, inputs (arrears, variable pay, bonus, other deductions, OT, extra days, reimbursements, LOP reversal).
  • Statutory: EPF (incl. EPS, EDLI, admin charges, VPF), ESI, PT by state, LWF by state, gratuity provision, TDS with regime choice, declarations and proofs.
  • Advance salary and loans with a recovery ledger.
  • Payslips (PDF, optional password, publish and notify), payroll register, statutory files (EPFO ECR, ESIC contribution upload, PT and LWF challan data), bank transfer files.

R2: F&F run type (with Exit), Form 16 Part B, 24Q return data, accounting journal export (Tally XML, Zoho Books), VDA, statutory bonus calculator.

Out of scope: direct portal filing or automation (legal and fragility risk), payment execution through bank APIs (later H2H), non-India payroll.

2. Learned from the reference product​

Source: Spryple analysis, §7, §16, §19 and §21.5.

  • [Observed] Pay groups are chosen by CTC range; each group lists earnings and deductions as a percentage of CTC or Basic, or flat, and components can be renamed for the payslip (SP-PAY-05, SP-PAY-10, SP-PAY-11; A/M: Payroll).
  • [Observed] A pay schedule can use the same or different payroll and leave windows (e.g. 25th to 24th) and a salary basis of actual days in month or organisation working days (SP-PAY-04).
  • [Observed] Statutory configuration covers EPF with EDLI and admin charges, ESI with state-wise numbers and an "include employer contribution in CTC" toggle, PT slabs by state; outputs are reports (EPF download, ESI PDF/Excel, PT by state), plus a bank PDF and a bank-wise summary (SP-PAY-02, SP-PAY-03, SP-PAY-13, SP-PAY-15).
  • [Observed] The documented TDS flow is a manually typed amount per employee (SP-PAY-16), although the employee menu shows "IT Declaration" and "Form 16" items [Observed-SS §21.5].
  • [Observed] Advance salary needs at least one payslip, is capped at gross, is approved by the Finance Manager and recovered over instalments with cooling-off months (SP-PAY-07; E: Payroll).
  • [Inferred] No run lifecycle (lock, approve, rollback), variance checks or off-cycle runs are documented, and gratuity appears as an employee deduction on a sample payslip (§7.3, §21.5).

3. Our design decisions​

IDDecision [Proposed]Reason
D-PAY-01Salary structure = versioned template of components with formulas. Compensation = per employee, effective-dated (valid_from/valid_to), pointing to a structure version and an annual CTC. Structure suggestions by grade, not by CTC band.A promotion at the same CTC can change structure; increments do not need a new "group".
D-PAY-02Components have typed flags: taxable, PF wage, ESI wage, PT wage, gratuity wage, prorate, part of CTC, payslip label, rounding, statutory code. Formulas use a safe expression language with references to other components and inputs, evaluated in dependency order.One engine handles all tenants; no per-tenant code.
D-PAY-03A pay run freezes a snapshot per employee: compensation version, payable days, inputs, statutory rate versions, declaration version, and the hash of all of it. Outputs are stored as pay_run_line rows.Exact reproduction of what was paid; legal defensibility.
D-PAY-04Run lifecycle: Draft → Calculated → InReview → Approved → Locked → Paid, with Reversed via a reversal run. Preparer and approver must differ when segregation of duties is on.Controls the reference product lacks.
D-PAY-05Back-dated compensation changes and post-lock attendance changes become arrears or LOP-reversal inputs in the next run, recomputed from the old periods' snapshots.Locked runs stay untouched; employees still get the correct money.
D-PAY-06A TDS engine projects annual tax under the chosen regime, using declarations (estimates) before the proof window and verified proofs after. The manual TDS override remains as an audited input.Parity with the hidden IT Declaration menu, and correctness.
D-PAY-07Statutory outputs are portal-ready files generated by versioned adapters, not reports.The portal formats are what finance actually needs.
D-PAY-08Gratuity is an employer provision (accrual), not a payslip deduction; payment happens in F&F.Correct treatment; avoids the reference product's confusing display.
D-PAY-09Money is stored as integer paise (bigint … _paise, see Data model) and computed with a decimal library, never floating point. Rounding is per component and per statutory rule.Paise-level errors are visible to every employee.
D-PAY-10Only one non-reversed regular run per employee per period. Off-cycle runs are separate run types.Prevents double pay.

4. Domain model​

Notes:

  • pay_component.kind: EARNING, DEDUCTION, EMPLOYER_CONTRIBUTION, REIMBURSEMENT, INFORMATION. calc_type: FIXED, PERCENT, FORMULA, INPUT, STATUTORY.
  • pay_run.run_type: REGULAR, OFF_CYCLE, ARREARS, BONUS, FNF (R2), REVERSAL. pay_run_employee.status: INCLUDED, HELD, EXCLUDED, ERROR.
  • pay_input.input_type: ARREAR, VARIABLE_PAY, BONUS, OTHER_DEDUCTION, REIMBURSEMENT, OT_HOURS, EXTRA_DAYS, LOP_REVERSAL, LOP_ADDITIONAL, TDS_OVERRIDE, LEAVE_ENCASHMENT, NOTICE_PAY, NOTICE_RECOVERY.
  • pay_statutory_rate rows are copied from the platform's statutory pack (tenant_id set when a tenant overrides or accepts a pack). PAN and Aadhaar stay in Core HR with envelope encryption; PAY reads them only for file generation.
  • pay_bank_file.debit_account_id points to a legal-entity bank account in ORG. Employee bank accounts are in EMP.

5. State machines​

5.1 Pay run​

Rules: Locked requires the attendance period to be locked (Attendance). Payslips are published to employees at Locked or at a scheduled publish time. A reversal creates a REVERSAL run whose lines negate the original; a corrected OFF_CYCLE run follows.

5.2 Tax declaration​

5.3 Advance salary or loan​

6. Key business logic​

6.1 CTC breakup at assignment​

function breakup(annual_ctc, structureVersion, statutoryContext):
monthly_ctc = annual_ctc / 12
values = {}
for line in topologicalOrder(structureVersion.lines, by formula references):
if line.is_balancing: continue
values[line.component] = evaluate(line.formula, values, monthly_ctc)
# e.g. BASIC = 0.50 * CTC ; HRA = 0.40 * BASIC ; CONV = 1600
employer = employerContributions(values, statutoryContext) # EPF ER, ESI ER if part_of_ctc
balancing = monthly_ctc - sum(earnings in values) - sum(employer where part_of_ctc)
if balancing < 0: error "structure exceeds CTC"
values[balancing component] = balancing # e.g. SPECIAL_ALLOWANCE
# Labour Codes wage check (optional, configurable): if excluded allowances exceed
# 50% of total remuneration, the excess counts as wages for PF and gratuity. Verify.
return values

6.2 Payable days​

function payableDays(employee, period, schedule):
att_window = [period.att_window_start, period.att_window_end] # e.g. 26th to 25th
basis_days = case schedule.salary_basis:
CALENDAR -> days in period
FIXED -> schedule.fixed_basis_days # e.g. 30 or 26
WORKING_DAYS -> days in period minus week-offs and holidays for the employee

employed = days in period within [joining_date, last_working_date]
if salary_basis == WORKING_DAYS: employed = working days within that range

unpaid = 0
for day in ATT days in att_window (locked):
unpaid += (1 - day.paid_fraction) # absences, unpaid halves, penalised week-offs
for days the employee was employed
unpaid += LVE LOP days in att_window not already counted as unpaid in ATT

# Days after the attendance cut-off (att_window_end + 1 .. period_end) are deemed
# present now and trued up next period through LOP_ADDITIONAL inputs.

# Proportional week-off deduction (reference payroll rule, configurable):
if policy.prop_weekoff.enabled and unpaid > policy.prop_weekoff.lop_threshold:
weekoffs = week-offs in att_window
unpaid += weekoffs * unpaid / working_days_in_att_window

unpaid += sum(LOP_ADDITIONAL inputs) - sum(LOP_REVERSAL inputs as days, when the policy
settles reversals as days rather than as amounts)
payable = clamp(employed - unpaid, 0, basis_days)
return basis_days, payable, unpaid

Example: CALENDAR basis, 30-day month, employee joined on the 11th, 2 LOP days in the window. employed = 20, unpaid = 2, payable = 18, proration factor 18 / 30 = 0.6.

6.3 Gross-to-net order of operations​

The engine is a pure function calculate(snapshot) -> lines. The order matters because later steps use earlier results.

function calculate(s): # s = frozen input snapshot
# 1. Earnings from the compensation record
for c in s.compensation.lines where kind == EARNING:
amt = c.monthly_amount
if c.prorate: amt = amt * s.payable_days / s.basis_days
line(c, amt)
# 2. Mid-period compensation change: split the period by valid_from and prorate
# each slice by its own payable days (handled by the snapshot builder).
# 3. Variable earnings from inputs
line(OT, s.ot_hours * hourlyRate(s) * s.ot_multiplier)
line(EXTRA_DAYS, s.extra_days * dailyRate(s))
line(ARREAR, VARIABLE_PAY, BONUS, LEAVE_ENCASHMENT, NOTICE_PAY inputs)
gross = sum(earning lines)

# 4. Reimbursements (non-taxable, outside gross for statutory bases)
line(REIMBURSEMENT inputs)

# 5. Statutory wage bases
pf_wage = sum(lines with pf_wage) ; pf_base = min(pf_wage, ceiling) if capped else pf_wage
esi_wage = sum(lines with esi_wage)
pt_wage = sum(lines with pt_wage)

# 6. Employee statutory deductions
line(EPF_EE, round(pf_base * epf_ee_rate)) # example 12%
line(VPF, round(pf_wage * vpf_percent))
if esiCovered(s, esi_wage): line(ESI_EE, roundUp(esi_wage * esi_ee_rate)) # example 0.75%
line(PT, ptSlab(s.pt_state, pt_wage, s.period)) # state slab, month rules
line(LWF_EE, lwf(s.lwf_state, s.period)) # only in due months

# 7. Income tax (TDS) - see 6.5
line(TDS, s.tds_override if present else tds(s, lines))

# 8. Other deductions in priority order
for d in [LOAN_RECOVERY, ADVANCE_RECOVERY, OTHER_DEDUCTION inputs, NOTICE_RECOVERY]:
line(d)

# 9. Net and the net-pay floor
net = gross + reimbursements - sum(deduction lines)
if net < policy.min_net_pay:
reduce non-statutory deductions in reverse priority until net >= floor
carry the unrecovered amounts to the next period ; add warning NET_FLOOR_APPLIED
# 10. Employer contributions (cost, not deducted)
line(EPF_ER_EPF, EPS, EDLI, EPF_ADMIN) # see 6.4
if esiCovered: line(ESI_ER, roundUp(esi_wage * esi_er_rate)) # example 3.25%
line(LWF_ER) ; line(GRATUITY_PROVISION, gratuity_wage * provision_rate)
# 11. Rounding: per component rule, then net rounded per policy (e.g. nearest rupee)
# 12. YTD: ytd_amount on each line = previous locked YTD + amount
return lines, gross, net, employer_cost

6.4 EPF split (example rates, verify)​

pf_base = min(pf_wage, 15000) when pf_wage_capped else pf_wage # ceiling example
epf_ee = 12% of pf_base
eps = 8.33% of min(pf_base, 15000) # pension, capped; zero if not EPS-eligible
epf_er = 12% of pf_base - eps # employer share to the EPF account
edli = 0.5% of min(pf_base, 15000)
admin = 0.5% of pf_base (subject to a minimum per establishment)

6.5 TDS projection​

function tds(s, lines):
months_left = remaining months in FY including this one
recurring = taxable monthly earnings from compensation (unprorated)
projected = YTD taxable (incl. previous employer per declaration)
+ taxable lines this month
+ recurring * (months_left - 1)
if regime == OLD:
projected -= exemptions (HRA per rent declared or proved, LTA, etc.)
projected -= deductions (chapter VI-A items capped per item, PT paid, standard deduction)
else: # NEW regime
projected -= standard deduction for the year # example, verify
tax = slabTax(regime, FY, projected) - rebate + surcharge
tax = tax * (1 + cess_rate) # example 4%
remaining = max(0, tax - TDS deducted YTD - previous employer TDS)
# One-time items (bonus, arrears): tax on the increment is deducted in full this month
# when policy.tds.one_time_full_deduction is true.
return round(remaining / months_left)

Declarations are used until proof_window_start; afterwards only verified proofs count. Regime is the employee's declared choice; the default is the statutory default regime. Section references follow the Income-tax Act in force for the year (the 2025 Act applies from FY 2026-27; verify mappings).

6.6 ESI coverage (example rule, verify)​

esiCovered(s, esi_wage):
if employee was covered at the start of the current contribution period
(April to September or October to March): covered for the whole period
else covered when esi_wage <= ceiling (example 21000; higher for persons with disability)

6.7 Arrears from back-dated changes​

on pay.compensation.changed with valid_from in a locked period:
for each locked period P from valid_from to last locked period:
old = locked pay_run_employee for P
new = calculate(old.input_snapshot with compensation replaced by the new version)
diff = new.lines - old.lines per component (earnings and statutory)
create pay_input ARREAR per earning component and statutory differences
for the next open period, source = ARREARS, referencing P

Post-lock attendance or leave changes arrive as att.adjustment.created or lve.request.cancelled and become LOP_REVERSAL (money owed to the employee) or LOP_ADDITIONAL inputs.

6.8 Pre-approval validation​

Blocking (must fix or exclude the employee): missing bank account for bank-transfer mode; negative net after the floor; PF member without UAN at file generation; compensation changed after calculation; attendance period not locked; employee with zero payable days but non-zero earnings.

Warnings (must be acknowledged): net pay variance above variance_threshold_percent vs last period; new joiners and leavers; ESI coverage changes; gross below the configured state minimum wage for the employee's skill category (minimum-wage table from the statutory pack); TDS override present; held salaries.

6.9 Statutory and bank file generation​

Generators are adapters with a version: EPFO_ECR (text format with UAN rows), ESIC_MC (monthly contribution upload), PT_<STATE> (challan or return data), LWF_<STATE>, TDS_24Q (R2), FORM16_PARTB (R2). Bank adapters: GENERIC_NEFT_CSV plus bank-specific formats. Every file is generated from locked run lines only, stored in object storage with Object Lock, and listed with its adapter version so a format change never alters a past file.

7. Configuration​

SettingTypeDefaultDescription
schedule.frequencyenumMONTHLYOnly monthly in R1
schedule.pay_dayintegerlast dayDay salaries are credited
schedule.attendance_cutoff_dayintegernullNull means same window as the pay month; e.g. 25 gives a 26th to 25th window
schedule.salary_basisenumCALENDARCALENDAR, FIXED, WORKING_DAYS
schedule.fixed_basis_daysinteger30Used with FIXED
epf.enabledbooleantrueEPF applies
epf.wage_ceiling_appliesbooleantrueContributions on capped wages by default
epf.employer_in_ctcbooleantrueEmployer PF is part of CTC
epf.include_admin_in_ctcbooleanfalseAdmin and EDLI charges in CTC
esi.enabledbooleantrueESI applies
esi.employer_in_ctcbooleantrueReference toggle [Observed SP-PAY-03]
pt.enabledbooleantruePT by the employee's work state
lwf.enabledbooleantrueLWF by state
gratuity.provision_enabledbooleantrueMonthly provision posted as employer cost
gratuity.provision_ratenumber0.0481Example 15/26/12 of gratuity wage; verify actuarially
tds.default_regimeenumNEWStatutory default regime (verify)
tds.declaration_windowdate rangeAprilWhen employees declare
tds.proof_windowdate rangeJanuary to FebruaryWhen proofs are accepted
tds.one_time_full_deductionbooleantrueTax on one-time items deducted in the same month
ot.included_in_salarybooleantrueReference payroll rule [Observed SP-PAY-07]
ot.rate_basisenumGROSSBASIC_DA or GROSS for the hourly rate
extra_days.rate_basisenumGROSSDaily rate base for extra days
absent_is_lopbooleantrueReference payroll rule [Observed SP-PAY-07]
prop_weekoff.enabledbooleanfalseProportional week-off deduction
prop_weekoff.lop_thresholdnumber5LOP days before it applies
statutory_independent_of_lopbooleanfalseCompute PF and ESI on full wages despite LOP [Observed SP-PAY-07]
min_net_paynumber0Net floor before deductions are deferred
rounding.netenumNEAREST_RUPEENONE, NEAREST_RUPEE, UP, DOWN
variance_threshold_percentnumber20Warning above this change vs last period
sod.enabledbooleantruePreparer cannot approve; compensation editor cannot approve the run
payslip.passwordenumNONENONE, PAN_DOB, CUSTOM
payslip.publishenumON_LOCKON_LOCK or SCHEDULED
advance.min_payslipsinteger1[Observed SP-PAY-07]
advance.max_percent_of_grossnumber100Cap on advance amount
advance.max_instalmentsinteger6Recovery instalments
advance.cooling_off_monthsinteger3Gap between advances [Observed SP-PAY-07]
freeze_windowday range25 to 5Platform change-freeze window shown to payroll users

8. Screens by persona​

PersonaWebMobile
EmployeePayslips (FY and month), CTC and revision history, tax regime and declarations, proofs upload, tax projection, Form 16 (R2), advance request and recovery statusPayslips, declarations status, advance request
Finance adminComponents, structures with formula editor and preview, statutory registrations, statutory pack acceptance with change log, schedules, bank accounts, payslip template—
Payroll managerAssign and revise compensation with breakup preview, inputs grid (bulk upload), run console (calculate, warnings, variance, hold, approve, lock), payslip publish, statutory files, bank files, advance approvals, proof verificationRun status and approval (approver only)
Org ownerPayroll cost dashboard by department and month, run approval when configuredApproval
AuditorRead-only runs, files and audit trail—

9. Events​

EventEmitted whenMain consumers
pay.compensation.changedA compensation record is created or revisedPAY arrears calculator, NTF (revision letter), RPT
pay.input.createdA one-time input is added (any source)PAY run invalidation
pay.run.createdA run is createdRPT
pay.run.calculatedCalculation completesNTF (preparer), RPT
pay.run.submittedSent for reviewNTF (approver task)
pay.run.approvedApprovedNTF
pay.run.lockedLocked; snapshots finalATT (close period), CLM (mark payouts paid-in-run), EXT (F&F settled), NTF, accounting export
pay.payslip.publishedPayslips visible to employeesNTF (email, push)
pay.statutory_file.generatedA statutory file is readyNTF, RPT
pay.bank_file.generatedA bank file is readyNTF
pay.run.paidBank payment confirmedCLM, EXT, NTF
pay.run.reversedA reversal run is lockedATT, CLM, NTF
pay.declaration.submittedEmployee submits declarations or proofsNTF (payroll task)
pay.loan.approvedAdvance or loan approvedNTF

Consumed: att.period.locked, att.period.unlocked, att.adjustment.created, att.overtime.approved, lve.request.cancelled, lve.encashment.approved, lve.cycle.closed, clm.claim.approved (reimbursement input when payout mode is payroll), ext.fnf.initiated (create F&F run), emp.employee.joined, emp.job.changed (PT state, structure suggestion), plt.statutory_pack.published.

10. API surface​

Detailed in API endpoints.

ResourceMain operations
/v1/pay/componentsCRUD
/v1/pay/structures and /v1/pay/structures/:id/versionsCRUD, publish, POST /:id/preview with a CTC
/v1/pay/compensationsPOST assign or revise (with breakup), GET history per employee
/v1/pay/employee-statutoryGET, PUT UAN, ESIC, regime, VPF
/v1/pay/schedules and /v1/pay/periodsCRUD, GET periods
/v1/pay/inputsPOST single or bulk upload, GET, DELETE while the run is not locked
/v1/pay/runsPOST create, POST /:id/calculate, POST /:id/submit, POST /:id/approve, POST /:id/lock, POST /:id/reverse, GET /:id/employees, GET /:id/variance
/v1/pay/runs/:id/employees/:employeeId/holdPOST hold or release
/v1/pay/payslipsGET (SELF, ORG), GET /:id/pdf, POST /publish
/v1/pay/statutory-filesPOST generate for run and scheme, GET download
/v1/pay/bank-filesPOST generate, POST /:id/confirm, GET download
/v1/pay/tax-declarations and /v1/pay/tax-proofsPOST, PATCH, POST /:id/verify
/v1/pay/loansPOST request, GET, ledger

11. Permissions​

Salary data is sensitive: list views mask amounts unless the viewer has pay.compensation.read at the right scope.

PermissionTypical scopesDefault roles
pay.payslip.readSELF, ORGemployee (SELF), payroll_manager, auditor
pay.compensation.readSELF, LOCATION, ORGemployee (SELF), payroll_manager, finance_admin, org_owner
pay.compensation.manageORG, LOCATIONpayroll_manager
pay.component.manageORGfinance_admin
pay.structure.manageORGfinance_admin
pay.statutory.manageORGfinance_admin
pay.input.manageORG, LOCATIONpayroll_manager
pay.run.prepareORG, LOCATIONpayroll_manager
pay.run.approveORGfinance_admin, org_owner
pay.run.lockORGpayroll_manager, finance_admin
pay.run.reverseORGfinance_admin (MFA step-up, audited)
pay.file.generateORGpayroll_manager
pay.declaration.submitSELFemployee
pay.declaration.verifyORGpayroll_manager
pay.loan.requestSELFemployee
pay.loan.approveORGpayroll_manager
pay.report.exportORGpayroll_manager, finance_admin, auditor

12. Reports​

ReportContentsReference
Payroll registerEvery employee and component for a run; totals[Observed] SP-PAY-13
Bank statement and bank-wise summaryNet pay by bank and debit account, with statutory totals[Observed] SP-PAY-15
EPF, ESI, PT, LWF registersContribution by employee for the period, plus the generated file[Observed] SP-PAY-13, files [Proposed]
Payable days reportBasis, employed, unpaid and payable days with the source breakdown[Observed] SP-PAY-13
Variance reportEmployee and component changes vs the previous period[Proposed]
Pay structure and compensation listEmployee, structure, CTC, effective date[Observed] §21.5
CTC and revision historyPer employee[Observed] E: Payroll
Tax projection and declarationsProjected tax, declared vs proved[Proposed]
Advance and loan registerPrincipal, recovered, balance, status[Observed] SP-PAY-13 (empty in the docs)
Payroll cost by department and monthGross, employer cost, trend[Observed] SA dashboard
Arrears registerArrear inputs with source period[Observed] SP-PAY-09
Form 16 and 24Q dataR2[Proposed]

13. Release and priority​

CapabilityReleaseMoSCoW
Components, structures, compensation, CTC breakup, revision with arrearsR1Must
Pay schedule with attendance cut-off; payable daysR1Must
Run lifecycle with validation, variance, hold, SoD, lock, reversalR1Must
EPF, ESI, PT, LWF, gratuity provisionR1Must
TDS engine with regime, declarations and proofsR1Must
ECR, ESIC, PT and LWF files; generic bank fileR1Must
Payslips with publish and notify; password optionR1Must
Advance salary and loans with recovery ledgerR1Should
Off-cycle and bonus runsR1Should
Bank-specific file formatsR1 (top 3 banks), R2 (more)Should
F&F run typeR2Must
Form 16 Part B, 24Q dataR2Must
Accounting journal export (Tally, Zoho Books)R2Should
VDA (CPI-linked DA)R2Could
Statutory bonus calculatorR2Could
Minimum-wage check by state and skillR1 (warning)Should

14. Open questions​

  1. Which banks do pilot customers use? This sets the R1 bank-format list.
  2. Should PF on arrears be computed on the arrear month's ceiling or the original month's? The default is the original month's rules.
  3. Do we support multiple legal entities (separate PF and ESI codes) in one tenant in R1? The data model allows it; UI scope is to be confirmed.
  4. Where are previous-employer income and TDS captured for mid-year joiners: declaration only, or also an import? The default is both.
  5. What is the R1 approach to the Labour Codes wage definition: warning only, or automatic adjustment of PF and gratuity wages? The default is a configurable warning plus an opt-in adjustment.
  6. Should the payroll engine move to a separate worker service before GA? The trigger in the architecture is a run longer than 10 minutes for the largest tenant.