---
title: "Import reunification roster data"
method: POST
path: "/v1/reunification/imports"
tags: ["reunification"]
---

# Import reunification roster data

`POST /v1/reunification/imports`

Imports a reunification roster as JSON. This is the API
equivalent of the in-app CSV upload and writes to the same roster.

**Every call replaces the whole roster.** Records you send are created
or updated, and anything you leave out is deleted. Only records that
came from this API or the in-app CSV upload are replaced.

Deleting someone revokes the Coram access an earlier import gave them.
Access granted by hand survives. Revocation is also skipped when a previous
import has not finished creating accounts yet, and when more than half
of a provisioned roster of 10 or more people would lose access at once,
which stops a truncated upload locking out a school.

Send every key on every call. The one you may omit is `staff`, and
omitting it deletes every staff member.

The import aborts and leaves the old roster intact if two schools map to
the same Coram location, if a location id does not exist, or if a
student or teacher repeats a school id in `school_ids` after the first
one. Repeating a `student_id`, `teacher_id` or `staff_id` returns `400`
before any work starts.

The order of the top level arrays does not matter.

The import runs in the background and is cancelled after one hour. The
`202` carries the initial status. Poll
`GET /v1/reunification/imports/status`. One import runs per organization
at a time and the in-app CSV upload counts as one, so a second request
returns `409`.

Requires `write:*` scope and an `admin` creator role, and the feature
must be enabled for your organization. The status endpoint needs
`read:*`, so a write-only key can start an import but never see whether
it worked. Requests are limited to an average of 5 per second with a
short burst allowance, and the body to 32 MiB.

## Request body

- ReunificationImportInput — `schools`, `students`, `teachers` and `sections` must be present, and may be empty arrays. Sending one as `null` is rejected the same way as leaving it out, because both would wipe everything that key covers. Only `staff` may be left out entirely. Phone numbers are normalized to E.164 against the deployment's own region, currently the United States. A number that cannot be parsed is stored as null rather than rejected, so include a country code. Unrecognized keys are dropped without error, so a misspelled field name fails silently.
  - `schools` ReunificationSchool[], required
    - `school_id` string, required — Your identifier for the school, referenced by students, teachers, staff and school_location_mappings. A blank value drops the school silently, and with it every reference to it. Do not send the same school_id twice.
    - `name` string, required — Not validated. A blank name is accepted and shows as a nameless school in Coram.
    - `sis_id` string — Stored and displayed only. Nothing matches or de-duplicates on it.
    - `address` string
    - `city` string
    - `state` string
    - `zip` string
  - `students` ReunificationStudent[], required
    - `student_id` string, required — Your identifier for the student. A blank value drops the student silently. Stored as the student's card number and visible in Coram, so do not put anything sensitive here.
    - `name` string, required — Not validated. A blank name is accepted and stored empty.
    - `sis_id` string — Stored and displayed only. Nothing matches or de-duplicates on it.
    - `email` string — The identity Coram uses for this person. They are still imported and still counted without a valid one, but they get no Coram account, appear in neither user counter, and lose any access an earlier import gave them. Two people sharing an email collapse into one Coram account.
    - `phone_number` string
    - `grade` string
    - `school_ids` string[], required — Schools the student belongs to, in order. The first one that resolves becomes the primary school, so a typo in the first position silently promotes the next. Repeating that first id is ignored, but repeating any other aborts the whole import. A student whose school_ids all fail to resolve is imported with no location access.
    - `guardians` ReunificationGuardian[]
      - `name` string, required — A guardian with a blank name is discarded along with their contact details.
      - `email` string
      - `phone` string
      - `relationship` string — Free text. Stored as "Other" when omitted or blank.
  - `teachers` ReunificationTeacher[], required
    - `teacher_id` string, required — A blank value drops the teacher silently.
    - `name` string, required
    - `sis_id` string
    - `email` string — Without a valid one the teacher is still imported but gets no Coram account and loses any access an earlier import gave them.
    - `phone_number` string
    - `school_ids` string[], required — The first one that resolves becomes the primary school. Repeating that first id is ignored, but repeating any other aborts the whole import.
  - `staff` ReunificationStaff[]
    - `staff_id` string, required — A blank value drops the staff member silently.
    - `name` string, required
    - `sis_id` string
    - `email` string — Without a valid one the staff member is still imported but gets no Coram account and loses any access an earlier import gave them.
    - `phone_number` string
    - `title` string — Stored and displayed only.
    - `department` string — Stored and displayed only.
    - `roles` string[] — Free text, not a fixed set. Displayed in Coram and used for nothing else.
    - `school_ids` string[], required — Unlike students and teachers, repeated ids here are removed rather than aborting the import. Staff have no primary school.
  - `sections` ReunificationSection[], required — An empty array removes every student to teacher pairing on the roster.
    - `section_id` string, required — Must not be blank or the section is dropped. Never stored, so nothing can look it up.
    - `school_id` string — Accepted and ignored. Sections take their schools from the people in them.
    - `teacher_ids` string[] — A teacher id that does not resolve drops that teacher's pairings in this section without error.
    - `student_ids` string[] — A student id that does not resolve drops that student's pairings in this section without error.
  - `school_location_mappings` object, required — Maps your school ids to Coram location IDs. Must be present and non-empty. Give every school an entry, because a school without one is imported with no location and everyone attached only to it loses access. Two schools may not point at the same location, and a location that does not exist in your organization aborts the import after it has already returned 202. The location id in the example is illustrative, so look up your real ones with `GET /v1/locations` before your first call.

## Response `202`

Import accepted and processing started

- ReunificationImportStatus — Progress report for your most recent import. Counters that are zero are left out of the response, so treat an absent field as zero. Every counter resets when a new import starts.
  - `id` string, required
  - `status` 'in_progress' | 'completed' | 'failed', required
  - `synced_schools` integer
  - `total_schools` integer
  - `synced_students` integer
  - `total_students` integer
  - `synced_teachers` integer
  - `total_teachers` integer
  - `synced_staff` integer
  - `total_staff` integer
  - `synced_guardians` integer
  - `total_guardians` integer — Counted after the merge described on the guardian object, so it is usually lower than the number of guardian entries you sent.
  - `succeeded_users` integer — Coram accounts created or updated. Rises in the background after status becomes completed. Guardians are never included, because they get no account.
  - `failed_users` integer — Accounts that could not be created, usually a seat limit or an Auth0 rejection. Zero does not mean everyone succeeded, since anyone without a valid email is absent from both figures.
  - `error_message` string
  - `started_at` string, date-time — Carries a real time only while the import is in progress. Once it reaches completed or failed the field is still returned but reset to 0001-01-01T00:00:00Z, so record the start time yourself if you need it.
  - `completed_at` string, date-time — Returned as 0001-01-01T00:00:00Z until the import finishes.

## Other responses

- `400` — Body is not a JSON object, omits or nulls any of schools/students/teachers/sections, has an empty school_location_mappings, repeats a student/teacher/staff id, or exceeds 32 MiB
- `401` — Missing or invalid API key
- `403` — API key lacks the required scope/role, or the feature is not enabled for the organization
- `409` — An import is already running for this organization, including one started by the in-app CSV upload. Poll the existing one instead. The lock clears after one hour at the latest
- `500` — Unexpected server error

---

[API](https://skmtc.dev/coram/apis/coram-api.md) · [All operations](https://skmtc.dev/coram/apis/coram-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/coram/coram-api/revisions/2842f226f93b/schema)
