--- title: Enrollment Lifecycle | Vitable Docs description: How enrollments move through statuses and which webhook events fire at each transition. --- An enrollment represents an employee’s participation in a benefit plan. Each enrollment moves through a defined set of statuses as the employee selects, confirms, or declines coverage. Vitable fires webhook events at every transition, giving your system a real-time view of where each enrollment stands. Enrollment statuses apply across all benefit categories: `Medical`, `Dental`, `Vision`, and `Hospital`. ## Quick Reference This table maps every enrollment status transition to the webhook event that triggers it. | Current Status | Event Fired | New Status | What Happened | | -------------- | -------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------- | | *(none)* | `enrollment.started` | `pending` | An enrollment window opened for the employee | | `pending` | `enrollment.granted` | `pending` | The system or an admin created the enrollment opportunity — the employee still needs to make an election | | `pending` | `enrollment.elected` + `enrollment.accepted` | `enrolled` | The employee selected a plan and confirmed — both events fire together from the same action | | `pending` | `enrollment.waived` | `waived` | The employee declined the benefit | | `enrolled` | `enrollment.terminated` | `inactive` | Coverage has ended | There is no `terminated` enrollment status. When an enrollment is terminated, it transitions to `inactive`. The `enrollment.terminated` event signals the transition, but the resulting status on the enrollment resource is `inactive`. ## Mapping Statuses | Status | Meaning | Events | Transitions To | | ---------- | ------------------------------------------------ | ----------------------------------------------------------------- | -------------------- | | `pending` | Enrollment window opened, employee has not acted | `enrollment.started`, `enrollment.granted` | `enrolled`, `waived` | | `enrolled` | Employee selected a plan and confirmed | `enrollment.elected` + `enrollment.accepted` (both fire together) | `inactive` | | `waived` | Employee declined the benefit | `enrollment.waived` | — | | `inactive` | Coverage has ended | `enrollment.terminated` | — | The high-level flow is: ``` pending -> enrolled (elected + accepted fire together) pending -> waived enrolled -> inactive (via terminated) ``` ## Understanding the Events Vitable fires six enrollment webhook events. Each event follows the standard webhook payload format with `event_id`, `event_name`, `resource_type`, `resource_id`, and `created_at` fields. After receiving any enrollment event, fetch the current state by calling `GET /v1/enrollments/{id}` with the `resource_id` from the payload. ### Starting an Enrollment When an employee becomes eligible for benefits and an enrollment window opens, Vitable fires `enrollment.started`. The enrollment is created with a status of `pending`. This event fires during both open enrollment periods and qualifying life event (QLE) windows. At this point, the employee can view available plans and make a selection. ### Granting an Enrollment Opportunity The `enrollment.granted` event fires when the system or an admin creates an enrollment opportunity for the employee. The enrollment remains in `pending` status — the employee still needs to make an election. This is distinct from `enrollment.started`, which signals the enrollment window opened. ### Electing and Confirming Coverage When the employee selects a plan, signs, and confirms in the enrollment widget, Vitable fires both `enrollment.elected` and `enrollment.accepted` together. The enrollment moves from `pending` to `enrolled` in a single step — there is no intermediate state between selecting a plan and confirming coverage. Once enrolled, the employee has active coverage and the enrollment’s `coverage_start` field reflects when coverage began. After receiving `enrollment.accepted`, fetch the enrollment with `GET /v1/enrollments/{id}` to read the `employee_deduction_in_cents` and `employer_contribution_in_cents` fields. These values are set once coverage is confirmed. ### Waiving Coverage If the employee declines a benefit, Vitable fires `enrollment.waived` and the enrollment moves to the `waived` status. The `answered_at` field on the enrollment records when the employee made this decision. ### Terminating Coverage When active coverage ends, Vitable fires `enrollment.terminated` and the enrollment transitions to `inactive`. The enrollment’s `terminated_at` and `coverage_end` fields reflect when coverage was terminated and when it officially ends. Coverage can end for several reasons: the employee leaves the company, the employer terminates the plan, or an administrative change removes the employee’s eligibility. Regardless of the cause, the resulting status is always `inactive`. ## Enrolling Through Open Enrollment During an open enrollment period, all eligible employees receive enrollment windows for each available benefit category. The typical flow looks like this: ``` sequenceDiagram participant V as Vitable API participant P as Partner V--)P: enrollment.started (status: pending) Note over P: Employee views plans V--)P: enrollment.elected + enrollment.accepted (status: enrolled) Note over V: Deductions generated on monthly schedule ``` **Diagram summary:** 1. Vitable fires `enrollment.started` when the enrollment window opens. The enrollment is `pending`. 2. The employee selects a plan. Vitable fires both `enrollment.elected` and `enrollment.accepted` together. The enrollment moves to `enrolled`. 3. Payroll deductions are generated later on a monthly schedule — `employee.deduction_created` fires on the 1st of each month, not immediately after enrollment. An employee may also choose to waive coverage, in which case step 2 would be replaced by an `enrollment.waived` event and the enrollment would move to `waived`. Employees receive separate enrollments for each benefit category they are eligible for. A single employee may trigger `enrollment.started` for `Medical`, `Dental`, `Vision`, and `Hospital` independently. ## Enrolling Through a Qualifying Life Event A qualifying life event (QLE) — such as marriage, the birth of a child, or loss of other coverage — opens a special enrollment window outside the regular open enrollment period. The webhook event flow is the same: ``` sequenceDiagram participant V as Vitable API participant P as Partner Note over V,P: Qualifying life event occurs V--)P: enrollment.started (status: pending) V--)P: enrollment.elected + enrollment.accepted (status: enrolled) Note over V: Deductions generated on monthly schedule ``` **Diagram summary:** 1. A qualifying life event triggers a new enrollment window. Vitable fires `enrollment.started`. 2. The employee selects a plan. Vitable fires both `enrollment.elected` and `enrollment.accepted` together. The enrollment moves to `enrolled`. 3. Payroll deductions are generated later on a monthly schedule — not immediately after enrollment. The QLE flow produces the same sequence of events as open enrollment. The difference is the trigger — a qualifying life event rather than a scheduled enrollment period. ## Terminating an Enrollment When an enrolled employee’s coverage ends, the enrollment transitions from `enrolled` to `inactive`: | From | To | Event | Notes | | ---------- | ---------- | ----------------------- | -------------------------------------------------------------------- | | `enrolled` | `inactive` | `enrollment.terminated` | There is no `terminated` status — the enrollment moves to `inactive` | - The enrollment’s status field reads `inactive`, not `terminated`. When you receive `enrollment.terminated`, check the `coverage_end` field on the enrollment to determine the last day of active coverage. This is important for payroll integration — you may need to adjust deductions for partial coverage periods. ## Troubleshooting **Receiving events out of order.** Webhook events are delivered at least once and may arrive out of order. Always fetch the current enrollment state with `GET /v1/enrollments/{id}` after receiving an event rather than relying on event order to track status. If you receive `enrollment.accepted` before `enrollment.elected`, the enrollment’s `status` field from the API reflects the true current state. **Duplicate events.** Your endpoint may receive the same event more than once. Use the `event_id` field for idempotency — if you have already processed an event, skip it. **Missing events.** If your endpoint was unreachable, use `GET /v1/webhook-events` to query for events you may have missed. You can filter by `resource_type` and time range to find enrollment events. For delivery details on a specific event, call `GET /v1/webhook-events/{event_id}/deliveries`. **Enrollment stuck in pending.** An enrollment remains `pending` until the employee makes a selection and it is processed, or the enrollment window closes. If an enrollment has been `pending` longer than expected, the employee may not have completed their enrollment yet. ## Related Webhooks | Event | Description | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `enrollment.started` | An enrollment window has opened for an employee. [Learn more](/webhooks/events/index.md) | | `enrollment.elected` | An employee has selected a benefit plan. [Learn more](/webhooks/events/index.md) | | `enrollment.accepted` | An enrollment has been confirmed and coverage is active. [Learn more](/webhooks/events/index.md) | | `enrollment.granted` | An enrollment has been granted through auto-enrollment or admin action. [Learn more](/webhooks/events/index.md) | | `enrollment.waived` | An employee has declined a benefit. [Learn more](/webhooks/events/index.md) | | `enrollment.terminated` | Coverage has ended and the enrollment is now `inactive`. [Learn more](/webhooks/events/index.md) | | `employee.deduction_created` | Payroll deductions generated on monthly schedule for employees with active enrollments. [Learn more](/embedded_benefits/concepts/payroll-deductions/index.md) | ## Related API Endpoints - [`GET /v1/employees/{id}/enrollments`](/api/resources/employees/methods/list_enrollments/index.md) — List all enrollments for an employee - [`GET /v1/enrollments/{id}`](/api/resources/enrollments/methods/retrieve/index.md) — Retrieve a single enrollment by ID - [`GET /v1/webhook-events`](/api/resources/webhook_events/methods/list/index.md) — Query webhook events to recover missed deliveries - [`GET /v1/webhook-events/{event_id}/deliveries`](/api/resources/webhook_events/methods/list_deliveries/index.md) — Inspect delivery attempts for a specific event