---
title: "Submit a roster file for import"
method: POST
path: "/roster"
tags: ["Roster"]
---

# Submit a roster file for import

`POST /roster`

Uploads a roster file and opens a new import job for it.

**When to use:** call this to start a fresh import cycle; to replace the file on a job that already exists, use `PUT /roster/{rosterId}/reupload` instead.

**Preconditions:** send a multipart request carrying the `file` part and the `tenant-id` header. `jobType` must be one of `PRACTITIONER`, `FACILITY`, or `GROUP` (`GROUP` additionally requires the tenant's `supportGroupRosters` configuration), and a roster template matching that job type must already exist for the tenant - if it does not, the request fails with 404. Accepted file formats are CSV, XLSX, TSV, JSON, and TXT; when the file is an `.xlsx`, `sheetName` names the worksheet to read. `templateId` is always required and references the saved column layout; `mappings` may additionally supply column mappings inline as a JSON string.

**What you get back:** 201 with the newly created roster job at its starting status. Take the job's id from the response and poll `GET /roster/{id}` to follow progress.

**Side effects:** the uploaded file is stored and a validation run is queued - except for `.txt` uploads, which are held at `PRE_PROCESSING` for pre-processing first, so read the returned status rather than assuming validation has begun. A notification email is sent for interactively uploaded rosters (automated feeds notify through their own configured recipients, and tenants may be excluded from these emails by configuration). Not idempotent - every call creates another roster job, so re-submitting the same file produces a duplicate import. The job's `data` object carries its status, file details, and record counters; see `GET /roster/{id}` for the full list of keys it can hold.

## Headers

- `tenant-id` string, required

## Response `201`

The roster job that was created, at its initial status. Poll it by id to follow validation progress.

- RosterResponse
  - `id` string
  - `tenantId` string
  - `templateId` string
  - `createdAt` string, date
  - `createdBy` string
  - `updatedAt` string, date
  - `updatedBy` string
  - `data` RosterJobData — Payload of a roster import job. Keys appear as processing progresses - none is guaranteed present at every stage, and new keys may be introduced by newer features, so treat unknown keys as valid. Key names mirror RosterDataRecord.
    - `status` 'PENDING' | 'IN_PROGRESS' | 'VALIDATION_IN_PROGRESS' | 'VALIDATION_FAILED' | 'PRE_PROCESSING' | 'VALIDATED' | 'COMPLETED' | 'APPROVED' | 'PARTIAL_IMPORTED' | 'REVALIDATE' | 'FAILED' | 'CANCELLED' — Current stage of the job. See the roster read operations' descriptions for the meaning of each value.
    - `jobType` 'PRACTITIONER' | 'FACILITY' | 'GROUP' — Kind of entity the file contains. GROUP uploads require the tenant's supportGroupRosters configuration.
    - `templateId` string — Identifier of the roster template describing the file's column layout.
    - `description` string — Free-text note supplied when the roster was uploaded.
    - `filename` string — Name of the file currently attached to the job.
    - `filePath` string — Storage path of that file, relative to the bucket.
    - `bucketName` string — Storage bucket holding the file.
    - `sheetName` string — Worksheet read from the file, for .xlsx uploads only.
    - `initialFilename` string — The file first uploaded, retained after a reupload replaces it.
    - `initialFilePath` string — Storage path of the first uploaded file, retained after a reupload.
    - `uploadCount` integer — How many times a file has been uploaded to this job; 1 until the first reupload.
    - `orderedFileColumns` string — The file's column headers in the order they appear in the header row, joined by the ",/" separator.
    - `totalRecords` integer — Rows found in the file.
    - `validRecords` integer — Rows that passed validation.
    - `invalidRecords` integer — Rows that failed validation.
    - `completedRecords` integer — Rows that were ingested successfully.
    - `failedRecords` integer — Rows that could not be ingested.
    - `rosterFileUrl` string — Time-limited link to the uploaded roster file. Present on the single-job read only.
    - `templateUrl` string — Time-limited link to the template the roster was mapped with. Present on the single-job read only.
    - `mappingsFileUrl` string — Time-limited link to the resolved column mappings. Present on the single-job read only.
    - `failureReasons` string — Validation or ingestion errors explaining why rows did not succeed.
    - `importedAt` string — When ingestion finished, as an ISO-8601 timestamp in UTC.
    - `roster-export-path` string — Storage path of the failed-records export, present once one has been generated.
    - `roster-export-url` string — Time-limited link to the failed-records export, present once one has been generated.
  - `invalidCount` integer
  - `validCount` integer
  - `completedCount` integer
  - `failedCount` integer

## Other responses

- `400` — Returned when the submitted file or metadata is rejected - an unsupported file format, an `.xlsx` without a readable `sheetName`, a missing `templateId`, an invalid `jobType`, or malformed `mappings` JSON. Correct the submission and retry.
- `401` — Returned when the request carries no valid authentication.
- `403` — Returned when the caller lacks the CREATE_ROSTER permission for this tenant.
- `404` — Returned when no roster template matching the supplied `templateId` and `jobType` exists for the tenant. Create or configure the template first.
- `500` — Returned when the roster job could not be created. No job is left behind; retry the upload, and contact support if the failure persists.

---

[API](https://skmtc.net/certifyos/apis/certify-api-layer.md) · [All operations](https://skmtc.net/certifyos/apis/certify-api-layer/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/certifyos/certify-api-layer/revisions/563848e0ecc0/schema)
