--- title: Census Sync | Vitable Docs description: How to submit employee data in bulk via census sync, monitor async processing, and handle validation errors. --- Census sync replaces an employer’s entire employee roster. When you submit a new sync, the system matches existing employees using `reference_id` and other available information. Employees included in the payload are created or updated; employees omitted are deactivated. This is not an append operation. You send a single `POST /v1/employers/{id}/census-sync` request containing up to 5,000 employees, and Vitable processes them asynchronously. The response is an acceptance receipt — not the final result. You track outcomes through webhooks and the `GET /v1/employers/{id}/employees` endpoint. This guide covers the request format, the async processing model, how to verify results, and what to do when things go wrong. ## Quick Reference ### Per-Employee Fields | Field | Type | Required | Rules | | ------------------------ | ------ | -------- | ------------------------------------------------------------------------------------ | | `first_name` | string | Yes | | | `last_name` | string | Yes | | | `date_of_birth` | string | Yes | ISO 8601 date | | `email` | string | Yes | Valid email address | | `phone` | string | Yes | 10-digit US phone number | | `reference_id` | string | No | Must be unique within the payload | | `address.address_line_1` | string | No | | | `address.address_line_2` | string | No | | | `address.city` | string | No | | | `address.state` | string | No | Valid US state or territory code | | `address.zipcode` | string | No | | | `start_date` | string | No | ISO 8601 date | | `employee_class` | string | No | `Full Time`, `Part Time`, `Temporary`, `Intern`, `Seasonal`, `Individual Contractor` | | `compensation_type` | string | No | `Salary` or `Hourly` | ### Validation Rules | Rule | Limit | | ----------------------------- | -------------------------------------- | | Maximum employees per request | 5,000 | | `reference_id` uniqueness | Must be unique within a single payload | | `phone` format | 10-digit US number | | `state` format | Valid US state or territory code | ## Understanding the Async Flow Census sync is fully asynchronous. When you submit a request, Vitable validates the payload structure and immediately returns an acceptance receipt. The actual employee creation and eligibility evaluation happen in the background. ``` sequenceDiagram participant P as Partner participant V as Vitable API P->>V: POST /v1/employers/{id}/census-sync V-->>P: 202 { data: { accepted_at, employer_id } } Note over V: Async processing begins V--)P: webhook: employee.eligibility_granted (per employee) P->>V: GET /v1/employers/{id}/employees V-->>P: 200 Paginated employee list ``` **Flow summary:** 1. You submit a `POST /v1/employers/{id}/census-sync` request with an array of employees. 2. Vitable responds with `202` and an acceptance receipt containing `accepted_at` and `employer_id`. 3. Vitable processes each employee asynchronously — creating records and evaluating eligibility. 4. For each employee that becomes benefits-eligible, Vitable delivers an `employee.eligibility_granted` webhook. 5. You verify the final state by calling `GET /v1/employers/{id}/employees`. The `202` response confirms Vitable accepted your request for processing. It does not mean employees have been created yet. Always use webhooks or the list employees endpoint to confirm outcomes. There is **no sync-completion webhook**. There is no single event that signals the entire census sync is done. Listen for individual `employee.eligibility_granted` events to track processing progress. ## Submitting a Census Sync Send a `POST /v1/employers/{id}/census-sync` request with an `employees` array: ``` { "employees": [ { "reference_id": "EMP-001", "first_name": "Jane", "last_name": "Doe", "date_of_birth": "1990-05-15", "email": "jane.doe@example.com", "phone": "5551234567", "address": { "address_line_1": "123 Main St", "address_line_2": "Apt 4B", "city": "Austin", "state": "TX", "zipcode": "78701" }, "start_date": "2026-01-15", "employee_class": "Full Time", "compensation_type": "Salary" }, { "reference_id": "EMP-002", "first_name": "John", "last_name": "Smith", "date_of_birth": "1985-11-22", "email": "john.smith@example.com", "phone": "5559876543", "address": { "address_line_1": "456 Oak Ave", "city": "Dallas", "state": "TX", "zipcode": "75201" }, "start_date": "2026-02-01", "employee_class": "Part Time", "compensation_type": "Hourly" } ] } ``` A successful response looks like this: ``` { "data": { "accepted_at": "2026-03-25T14:30:00+00:00", "employer_id": "empr_550e8400-e29b-41d4-a716-446655440000" } } ``` The `accepted_at` timestamp marks when Vitable acknowledged the request. Store this alongside the `employer_id` so you can correlate it with incoming webhooks. ## Employee Matching and Processing The system matches employees using `reference_id` and other available information to preserve existing records and enrollment history across syncs. Matched employees are updated with the new data; unmatched entries create new employee records. Previously deactivated employees are reactivated if they reappear in a subsequent sync. **Required fields per employee:** `first_name`, `last_name`, `date_of_birth`, `email`, `phone` **Optional fields:** `reference_id`, `address`, `start_date`, `employee_class`, `compensation_type` ## Monitoring Progress After submitting a census sync, you have two ways to track what happened: webhooks and the list employees endpoint. ### Receiving Webhooks As Vitable processes each employee, it evaluates eligibility based on the employer’s [eligibility policy](/embedded_benefits/concepts/eligibility-policies/index.md). When an employee becomes benefits-eligible, you receive an `employee.eligibility_granted` webhook: ``` { "event_id": "wevt_550e8400-e29b-41d4-a716-446655440000", "organization_id": "org_xyz789", "event_name": "employee.eligibility_granted", "resource_type": "employee", "resource_id": "empl_7c9e6679-7425-40de-944b-e07fc1f90ae7", "created_at": "2026-03-25T14:31:00+00:00" } ``` Use the `resource_id` to fetch the employee’s full record via `GET /v1/employees/{id}`. The `employee.eligibility_granted` event signals that an employee is now eligible for benefits and their enrollment window is open. For the full employee status model and related events, see the [Employee Lifecycle](/embedded_benefits/concepts/employee-lifecycle/index.md) guide. ### Verifying via the List Employees Endpoint You can also poll `GET /v1/employers/{id}/employees` to see which employees have been created. This endpoint returns a paginated list: Terminal window ``` GET /v1/employers/{id}/employees?page=1&limit=20 ``` | Parameter | Default | Description | | --------- | ------- | ---------------------------- | | `page` | 1 | Page number | | `limit` | 20 | Number of employees per page | The response includes pagination metadata: ``` { "data": [...], "pagination": { "page": 1, "limit": 20, "total": 150, "total_pages": 8 } } ``` Compare `pagination.total` against the number of employees you submitted to confirm all records were created. ## Walking Through the Happy Path This diagram shows the complete sequence for a successful census sync, from submission through employee verification. ``` sequenceDiagram participant P as Partner participant V as Vitable API P->>V: POST /v1/employers/{id}/census-sync (N employees) V-->>P: 202 { data: { accepted_at, employer_id } } Note over V: Processing employee 1 of N V--)P: webhook: employee.eligibility_granted (employee 1) Note over V: Processing employee 2 of N V--)P: webhook: employee.eligibility_granted (employee 2) Note over V: ... (remaining employees) P->>V: GET /v1/employers/{id}/employees?page=1&limit=20 V-->>P: 200 Paginated employee list (total: N) ``` **Step-by-step:** 1. You send a census sync with N employees. 2. Vitable returns the acceptance receipt immediately. 3. For each processed employee, Vitable delivers an `employee.eligibility_granted` webhook (timing depends on the employer’s eligibility policy and any waiting period). 4. You call the list employees endpoint to confirm all N records exist. ## Handling Errors and Partial Failures Not every census sync succeeds cleanly. The following flowchart shows how validation and processing errors surface. ``` sequenceDiagram participant P as Partner participant V as Vitable API P->>V: POST /v1/employers/{id}/census-sync alt Payload invalid V-->>P: 400 Validation Error (no processing) else Payload valid V-->>P: 202 Accepted Note over V: Per-employee async processing V--)P: webhook: employee.eligibility_granted (valid employees) end ``` **Summary:** - If the payload structure is invalid (e.g., missing required fields, exceeding 5,000 employees), you receive a `400` error immediately — no processing occurs. - If the payload is accepted, individual employees may still fail during async processing (e.g., a duplicate `reference_id` matching an existing employee). Successfully processed employees are created; failed ones are skipped. - You will only receive `employee.eligibility_granted` webhooks for employees that were successfully created and evaluated as eligible. ## Troubleshooting ### Exceeding the batch limit Each census sync request accepts a maximum of 5,000 employees. If your payload exceeds this limit, the entire request is rejected with a `400` error. Split larger datasets into multiple requests. ### Duplicate `reference_id` values within a payload Every `reference_id` in a single request must be unique. If you include the same `reference_id` twice, the request fails validation. Deduplicate your data before submitting. ### Invalid phone numbers The `phone` field must be a 10-digit US phone number (digits only, no dashes or spaces). Values like `555-123-4567` or `+15551234567` are rejected. Send `5551234567` instead. ### Invalid state codes The `state` field must be a valid US state or territory code (e.g., `TX`, `CA`, `PR`). Full state names like `Texas` are not accepted. ### Partial failures When some employees in a batch are valid and others are not, Vitable processes the valid ones and skips the invalid ones. You will not receive an explicit error notification for skipped employees. To detect partial failures, compare the `pagination.total` from `GET /v1/employers/{id}/employees` against the number of employees you submitted. If the count is lower than expected, review your source data for the issues described above and resubmit the corrected records. There is no webhook for failed employee records in a census sync. The only way to detect partial failures is to compare the expected employee count against the actual count returned by the list employees endpoint. ## Related Webhooks | Event | Description | | ------------------------------ | -------------------------------------------------------------------------------------------------------- | | `employee.eligibility_granted` | An employee has been evaluated as eligible for benefits. [View event details](/webhooks/events/index.md) | The `employee.eligibility_granted` event is the primary signal that census sync processing completed for a given employee. For the full set of employee-related events — including `employee.eligibility_terminated` and `employee.deactivated` — see the [Employee Lifecycle](/embedded_benefits/concepts/employee-lifecycle/index.md) guide. ## Related API Endpoints - [`POST /v1/employers/{id}/census-sync`](/api/resources/employers/methods/submit_census_sync/index.md) — Submit employee data for async processing - [`GET /v1/employers/{id}/employees`](/api/resources/employers/methods/list_employees/index.md) — List employees for an employer (paginated) - [`GET /v1/employees/{id}`](/api/resources/employees/methods/retrieve/index.md) — Retrieve a single employee record - [`GET /v1/webhook-events`](/api/resources/webhook_events/methods/list/index.md) — Query webhook events for troubleshooting - [`GET /v1/webhook-events/{event_id}/deliveries`](/api/resources/webhook_events/methods/list_deliveries/index.md) — Check delivery status for a specific event