---
title: "Save column mappings as a roster upload template"
method: POST
path: "/roster-upload/{templateId}/column-mapping/template"
tags: ["Roster upload"]
---

# Save column mappings as a roster upload template

`POST /roster-upload/{templateId}/column-mapping/template`

Creates a new roster upload template from a set of confirmed column mappings, or updates an existing one.

**When to use:** call this after reviewing the analysis returned by `POST /roster-upload/{templateId}/column-mapping`; this endpoint persists the mappings the user confirmed.

**Preconditions:** supply the `tenant-id` header and the `templateId` the mappings belong to. In the request body, set `updateExistingTemplate` to `true` to update the template identified by `templateId`, or omit it (or set it to `false`) to create a new one; `mappings` carries the confirmed field list and `jobType` selects the field catalogue. A null `mappings` list is treated as an empty list rather than rejected. `templateName` and `templateDescription` are not enforced by this endpoint on either create or update. The tenant must have the column mapping feature enabled and the caller must hold the UPDATE_ROSTER_UPLOAD permission - both failures return 403, distinguished by the `reason` field of the error body (`FEATURE_DISABLED` identifies the feature-flag case).

**Validation:** the field mappings are validated before anything is written. If validation fails the request returns 400 carrying the individual field errors and no template state is changed.

**What you get back:** the resulting template record, including `templateId`, `templateUrl`, `schemaUrl`, the persisted `fields`, and `status`. Both creation and update return 200.

**Side effects:** on success the confirmed mappings are also written to a tenant-scoped column mapping cache, which later mapping analyses use to suggest matches for unmapped headers. That cache write is fail-open - if it fails the template is still saved and the request still succeeds. The operation is not idempotent: submitting the same mappings twice with `updateExistingTemplate` set to `false` creates two templates.

## Path parameters

- `templateId` string, required

## Headers

- `tenant-id` string, required

## Request body

- SaveMappingsRequest — Confirmed column mappings to persist as a roster upload template.
  - `updateExistingTemplate` boolean — When true, updates the template named by the path parameter; when false or omitted, creates a new template. Creating twice with the same mappings produces two templates.
  - `jobType` 'PRACTITIONER' | 'FACILITY' | 'GROUP'
  - `mappings` Field[] — The confirmed column-to-field mappings to persist. Validated before anything is written; a null list is treated as empty rather than rejected.
    - `name` string — CSV column header this mapping applies to, exactly as it appears in the roster file.
    - `mappedKey` string — Key of the Certify system field this column maps to, from the field catalog returned by `GET /templates/fields`. The sentinel `[skipped]` marks a column the user explicitly chose not to map: skipped columns persist in the template/mappings CSVs but are excluded from the generated validation schema.
    - `mappedEntity` string — Entity that owns the mapped attribute (for example `practitioner` or `location`). When set, `mappedKey` is accepted as-is instead of being validated against the system-field catalog.
    - `entityGroup` string — Group label linking columns that belong to the same repeating entity instance (a multiple-value group), so related columns are ingested together.
    - `isRequired` boolean — Whether every roster row must supply a value for this column; drives the `required` constraint in the generated validation schema.
    - `isStaticField` boolean — Catalog metadata: whether the mapped system field is a static (fixed-value) field rather than one populated from roster data.
    - `isSingleSelect` boolean — Catalog metadata: whether the mapped system field accepts a single value from a fixed option list.
    - `validationRegex` string — Regular expression applied to this column's values during roster validation. Rows whose values do not match are rejected.
    - `description` string — Human-readable description of the mapped system field, from the field catalog.
    - `enum` string[] — Allowed values for enum-type system fields; roster values outside this list fail validation. Absent for free-form fields.
    - `separator` string — Literal separator character used to split a single cell value into multiple tokens for multi-value attributes. Blank or absent defaults to a comma at schema-generation time.
    - `singleSelect` boolean
    - `skipped` boolean
  - `templateName` string — Human-readable name for the template, shown when choosing a template for a future upload. Not enforced by this endpoint on create or update.
  - `templateDescription` string — Longer note describing what this template is for. Not enforced by this endpoint on create or update.

## Response `200`

The saved template, including its identifier, hosted URLs, and the persisted field mappings. Returned for both creation and update.

- SingleStepTemplateFlowResponse — Result of a single-step template create or update: the resolved column mappings, final status, and the GCS object paths of the template artifacts.
  - `fields` Field[] — The template's resolved column mappings. After a file upload these are the mappings extracted from the uploaded mappings CSV; after a metadata-only update they are the template's existing stored columns.
    - `name` string — CSV column header this mapping applies to, exactly as it appears in the roster file.
    - `mappedKey` string — Key of the Certify system field this column maps to, from the field catalog returned by `GET /templates/fields`. The sentinel `[skipped]` marks a column the user explicitly chose not to map: skipped columns persist in the template/mappings CSVs but are excluded from the generated validation schema.
    - `mappedEntity` string — Entity that owns the mapped attribute (for example `practitioner` or `location`). When set, `mappedKey` is accepted as-is instead of being validated against the system-field catalog.
    - `entityGroup` string — Group label linking columns that belong to the same repeating entity instance (a multiple-value group), so related columns are ingested together.
    - `isRequired` boolean — Whether every roster row must supply a value for this column; drives the `required` constraint in the generated validation schema.
    - `isStaticField` boolean — Catalog metadata: whether the mapped system field is a static (fixed-value) field rather than one populated from roster data.
    - `isSingleSelect` boolean — Catalog metadata: whether the mapped system field accepts a single value from a fixed option list.
    - `validationRegex` string — Regular expression applied to this column's values during roster validation. Rows whose values do not match are rejected.
    - `description` string — Human-readable description of the mapped system field, from the field catalog.
    - `enum` string[] — Allowed values for enum-type system fields; roster values outside this list fail validation. Absent for free-form fields.
    - `separator` string — Literal separator character used to split a single cell value into multiple tokens for multi-value attributes. Blank or absent defaults to a comma at schema-generation time.
    - `singleSelect` boolean
    - `skipped` boolean
  - `status` string — Final template status. `success` when the validation schema was generated and the template is ready for roster uploads; `failure` when schema generation failed with an I/O error. Metadata-only updates preserve and return the template's existing status.
  - `templateId` string — Server-generated ID of the created or updated template.
  - `templateUrl` string — GCS object path (not a signed URL) of the stored template CSV. Fetch a signed download URL via `GET /templates/{id}`, which returns it as `templateUrl` in the record's `data`.
  - `schemaUrl` string — GCS object path (not a signed URL) of the generated JSON validation schema. Fetch a signed download URL via `GET /templates/{id}`, which returns it as `schemaUrl` in the record's `data`.

## Other responses

- `400` — Returned when the submitted field mappings fail validation. The body lists the offending fields; no template or cache state is written. Correct the listed fields and resubmit.
- `401` — Returned when the request carries no valid authentication.
- `403` — Returned when the column mapping feature is disabled for the tenant (`reason` is `FEATURE_DISABLED`) or the caller lacks the UPDATE_ROSTER_UPLOAD permission. Ask an administrator to enable the feature or grant the permission.
- `500` — Returned when saving the template fails unexpectedly. Retry; if the failure persists, contact support.

---

[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)
