Benefits live in the Business Made app, on HR › Benefits (/hr/benefits). The screen has two tabs: Plans and Enrollments. All of it uses business-made/benefits/… (appengine business-made/services/benefits.service.ts, controller benefits.controller.ts). Call those routes with call_appengine.
Business Made records the plans and who is covered. It never talks to the insurer or the 401(k) provider, so enrolling someone here doesn't sign them up with the carrier. Nothing in benefits sends an email. Starting coverage only takes the employee's share from pay, through Payroll.
The Business Made docs cover the same screen for people: Benefits, compensation, performance and learning. Payroll deductions are explained in People, periods and schedules. For the API, see the Business Made module reference.
Ask first
- The plan: its name, the kind of plan, the provider (name, plan number, group number), and the plan year.
- Coverage levels and cost: which levels are offered (employee only, employee + spouse, employee + children, family). For each level, ask what the employee pays and what the employer pays, and whether that is per pay, per month or per year. Never guess a premium.
- Who can join: full-time, part-time or contract staff. Only this is checked (see What it can't do yet).
- For each enrollment: the person, the plan, the coverage level, and when cover starts. Is this a new hire, open enrollment, a life event or a reinstatement?
- Dependents and beneficiaries: their names, relationship and date of birth. For a beneficiary, ask the percentage and whether they are primary or contingent.
Steps
1. HR › Benefits: the plan
POST business-made/benefits/plans takes the plan's fields at the top level. On screen, click New plan.
| Field | On screen | Values |
|---|---|---|
title | Name | Required (PLAN_TITLE_REQUIRED). |
type | Kind of plan | Required (PLAN_TYPE_REQUIRED): health, dental, vision, life, disability-short-term, disability-long-term, retirement-401k, hsa, fsa, commuter, wellness, employee-assistance, tuition-reimbursement, other. |
category | (automatic) | Set from the type: medical, insurance, retirement, financial or lifestyle. |
provider | Provider | { name, planId, groupNumber, contactPhone, contactEmail, website }. |
coverage.tiers[] | Coverage levels and cost | { tier, employeeCost, employerCost, frequency }. See the notes after this table. |
eligibility | Who can join | { employmentTypes: ["full-time","part-time","contract"], waitingPeriodDays, minHoursPerWeek }. |
planYear, startDate, endDate, description | Plan year, Dates, What it covers | Optional. endDate must not come before startDate (PLAN_DATES_INVALID). |
Notes on coverage.tiers[]:
tieris one ofemployee-only,employee-spouse,employee-children,family.frequencyisper-pay-period,monthly(the default) orannually.- Each tier can appear only once (
PLAN_TIER_DUPLICATE), and costs can't be negative.
A new plan is always a draft. To edit one, call POST business-made/benefits/plans/update with { sk, data: { … } }. What you send is merged into the plan, and the whole plan is checked again.
Hands on: the plan's sk.
Check: the plan shows under Benefit plans as Draft, with its Coverage levels and cost.
2. HR › Benefits: switch the plan on
POST business-made/benefits/plans/<sk>/activate. On screen, click Activate.
A plan with no coverage level can't be switched on (PLAN_TIERS_REQUIRED, tested). People can only be enrolled in an active plan (PLAN_NOT_ACTIVE).
Deactivate (…/deactivate) stops new enrollments. People already enrolled stay covered, and their deductions go on.
DELETE business-made/benefits/plans/<sk> works only while nobody has enrolled. Otherwise it gives PLAN_IN_USE, and you should deactivate the plan instead.
Set enrollment window (POST …/plans/<sk>/open-enrollment with { startDate, endDate }) saves the open-enrollment dates on the plan. Nothing checks them (see below).
Hands on: the plan, active. Check: the plan reads Active and appears under The plans you can enroll in.
3. HR › Benefits: make sure Payroll can take the share
Before you start anyone's coverage, check that there is a deduction type for the plan. Use GET business-made/payroll/deduction-types. Starting coverage looks for an active deduction type in the plan's category:
| Plan type | Deduction category it needs | Seeded type |
|---|---|---|
health, dental, vision | health, dental, vision | Health Premium, Dental Premium, Vision Premium |
hsa | hsa | HSA |
retirement-401k | retirement | 401(k): a percentage of pay, 5% by default |
fsa | fsa | none |
| any other type | benefit | none |
POST business-made/payroll/deduction-types/seed adds the seeded set. You can also add a type on Payroll › Deduction types (/payroll/setup/deductions).
If no type matches, coverage still starts, but nothing is taken from pay. The enrollment reads "Not taken from pay — no benefit deduction set up in Payroll" (tested on a life plan).
- Monthly and yearly shares are taken every pay. A $100-a-month share on a bi-weekly schedule took $100 from each pay, which is about $216 a month (tested on two previews). The deduction is saved with
frequency: monthly, but the payroll engine reads only its amount. Until that is fixed, tell the person. If the share is per pay, set the level's frequency toper-pay-period. - A 401(k) plan takes the deduction type's percentage, not the plan's dollar share. With the seeded 401(k) type, that is 5% of pay (read from code). Confirm the person's contribution rate on the deduction in Payroll › People & pay.
Hands on: the deduction type that will be used, or "none". Check: the type is listed on Payroll › Deduction types, and it is active.
4. HR › Benefits: enroll the person
POST business-made/benefits/enrollments with:
{ "employeeId": "<bm_employee sk or EMP-… code>",
"benefitPlanId": "<plan sk>",
"coverageTier": "employee-only",
"coverageStartDate": "2026-11-01",
"enrollmentType": "new-hire" }enrollmentType is new-hire (the default), open-enrollment, qualifying-life-event or reinstatement. On screen, click Enroll someone, then Enroll, or Enroll and start coverage to do steps 4 and 5 together.
The enrollment is always created pending. It copies the level's costs: employeeContribution, employerContribution, totalCost and contributionFrequency.
The enrollment is refused in these cases (all tested except NOT_ELIGIBLE and EMPLOYEE_INACTIVE):
| Code | Cause |
|---|---|
TIER_NOT_OFFERED | The plan has no such level. |
ALREADY_ENROLLED | The person already has a pending or active enrollment in this plan. |
NOT_ELIGIBLE | Their employment type isn't on the plan. |
EMPLOYEE_INACTIVE | The person no longer works here. |
Search the person's enrollments first: GET business-made/benefits/enrollments/employee/<employeeId>.
Hands on: the enrollment's sk.
Check: the Enrollments tab lists them as Pending.
5. HR › Benefits: start coverage
POST business-made/benefits/enrollments/<sk>/activate. On screen, click Start coverage. This works on a pending enrollment only (NOT_PENDING).
It sets the enrollment to active, and sets the start date to today if none was given. If the person has an employee share, it creates a bm_employee_deduction for that amount, which is taken from every pay from the start date (see the warning in step 3). The result is on payrollDeduction, as one of these:
active, with the deduction, for example "$100.00 monthly taken from pay as Health Premium";not-linked;none(no employee share).
Stop if the person didn't ask for coverage to start. Starting it changes their next paycheck. Show the plan, the level, the start date and the amount taken, and wait for a yes.
Hands on: the payrollDeduction state.
Check: the enrollment reads Active, or Waiting to start ·
6. HR › Benefits: dependents and beneficiaries
| Add | Route | Needs |
|---|---|---|
| Dependent | POST business-made/benefits/enrollments/<sk>/dependents | firstName, lastName, relationship (spouse, domestic-partner, child, other), dateOfBirth YYYY-MM-DD (DEPENDENT_DOB_REQUIRED, tested) |
| Beneficiary | POST business-made/benefits/enrollments/<sk>/beneficiaries | firstName, lastName, relationship, percentage 1–100, type primary or contingent. The primaries can't add up to more than 100%, and neither can the contingents (BENEFICIARY_OVER_100, tested). |
On screen, use Add dependent and Add beneficiary. Remove one with DELETE …/dependents/<id> or …/beneficiaries/<id>.
A dependent can be added to an employee-only level (tested). Check that the level covers them. If it doesn't, the person needs a different level: end this enrollment and enroll them at the right level.
Hands on: the dependents and beneficiaries saved. Check: the enrollment's Dependents and Beneficiaries lists show them.
7. HR › Benefits: waive, end or remove
| Need | Route | Works on |
|---|---|---|
| The person declines cover | POST …/enrollments/<sk>/waive with { reason }. On screen: Waive → Waive cover. | pending |
| Coverage stops (they left, or changed plan) | POST …/enrollments/<sk>/terminate with { reason }. On screen: End coverage, or Cancel coverage before it has started. | active (NOT_ACTIVE otherwise) |
| Take back an enrollment made by mistake | DELETE business-made/benefits/enrollments/<sk>. On screen: Remove. | pending, waived or declined. Other statuses give KEEP_HISTORY (tested). |
Ending coverage sets the end date to today and cancels the payroll deduction (tested). If cover hadn't started yet, the end date is the start date, and the enrollment reads "Cancelled before anything was taken from pay" (tested).
Stop before ending someone's coverage. Show who, which plan and the end date, and wait for a yes. Tell the person to let the insurer know, because Business Made doesn't.
Hands on: the enrollment's new status. Check: the enrollment reads Ended or Waived, and its Payroll line no longer shows a deduction.
When it's done
Tell the person what was saved:
- each plan, with its levels and costs;
- who is enrolled, at which level and from when;
- what will come out of each person's pay, and the frequency warning from step 3.
Open /hr/benefits.
GET business-made/benefits/metrics and the screen's tiles (Active plans, People covered, Employer cost) answer "how many are covered" and "what do benefits cost us". GET business-made/benefits/view/enrollments?… lists the enrollments with their labels.
What it can't do yet
- The deduction frequency is ignored. Monthly and yearly shares are taken every pay (step 3).
- A 401(k) enrollment's dollar share isn't used. The deduction type's percentage is.
- The enrollment window is information only. Enrollments are accepted outside it.
waitingPeriodDays,minHoursPerWeek,eligibleDepartmentsandeligibleLocationsaren't checked. OnlyemploymentTypesis.- The coverage level doesn't limit dependents. You can add any dependent to any level.
- No carrier connection. Nothing is sent to an insurer or 401(k) provider.
- No self-enrollment. People can't enroll or waive from their staff portal; HR does it for them.
- No benefit emails. Nothing tells the person when coverage starts or ends.
- Changing a level's cost doesn't change existing enrollments. The costs were copied onto each enrollment when it was made.
Don'ts
- Don't start or end coverage without a yes. It changes the person's pay.
- Don't guess premiums, employer shares or contribution rates.
- Don't tell the person a monthly share will be taken once a month. It won't be, until the frequency bug is fixed.
- Don't delete a plan or an enrollment that people have used. Deactivate the plan or end the coverage, so the history stays.
- Don't say the person is enrolled with the insurer. Business Made only records it.