Coram Ai reunification API

Bulk-import your school reunification roster — schools, students (with guardians), teachers, staff, and class sections — programmatically, the API equivalent of the in-app CSV upload. Each import is a full roster replace; poll the status endpoint for completion and partial-success counts.

OpenAPI Specification

coram-ai-reunification-api-openapi.yml Raw ↑
openapi: 3.0.0
info:
  title: Coram alerts reunification API
  description: "# Introduction\n\nThe Coram API enables seamless programmatic access to your security camera infrastructure, allowing you to build custom integrations and automate workflows.\n\n# Supported Actions\n\nManage and interact with your Coram platform resources:\n\n| Resource | Description |\n| -------- | ----------- |\n| **Camera Groups** | Organize and configure camera collections |\n| **Cameras** | Access camera settings, streams, and metadata |\n| **Locations** | Manage physical site configurations |\n| **NVRs** | Control and monitor network video recorders |\n| **Access Control** | List access control doors and trigger remote unlocks |\n\n# Required Headers\n\nEvery API request must include the following headers:\n\n| Header | Required | Description |\n| ------ | -------- | ----------- |\n| `X-Auth-Token` | Yes | Your API key (from the API key List Page) |\n\n**Example:**\n```\ncurl -X GET \"https://api.coram.ai/developer-api/v1/cameras\" \\\n  -H \"X-Auth-Token: xxxxxxxxxxxxxxx\"\n```\n\n# Response Format\n\nAll API responses follow a consistent structure based on the operation result.\n\n## Success Response\n\n**List Operations** (e.g., `GET /v1/cameras`)\n\nReturns a `results` array with pagination support:\n\n```json\n{\n  \"results\": [\n    { \"id\": \"cam_001\", \"name\": \"Front Door\", ... },\n    { \"id\": \"cam_002\", \"name\": \"Lobby\", ... }\n  ],\n  \"has_more\": true\n}\n```\n\nWhen `has_more` is `true`, additional results are available. Use pagination parameters to fetch more.\n\n**Bulk/Batch Operations** (e.g., `POST /v1/cameras`, `PATCH /v1/cameras`)\n\nReturns a top-level `status` field indicating whether the API request was processed, and a `results` array where each item reflects the outcome of an individual operation:\n\n```json\n{\n  \"status\": \"success\",\n  \"results\": [\n    { \"status\": \"success\", \"data\": { \"id\": \"cam_001\", \"mac_address\": \"...\" } },\n    { \"status\": \"error\", \"error\": { \"code\": \"VALIDATION_ERROR\", \"message\": \"...\" } }\n  ]\n}\n```\n\n| Field | Description |\n| ----- | ----------- |\n| `status` (top-level) | `\"success\"` if the request was processed |\n| `results[].status` | `\"success\"` or `\"error\"` for each operation |\n| `results[].data` | Present when `status` is `\"success\"` |\n| `results[].error` | Present when `status` is `\"error\"` |\n\n## Error Response\n\nWhen an error occurs, the response includes `status` set to `\"error\"` along with detailed error information:\n\n```json\n{\n  \"status\": \"error\",\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Invalid MAC address format\",\n    \"field\": \"mac_address\"\n  }\n}\n```\n\n**Error Fields:**\n\n| Field | Type | Description |\n| ----- | ---- | ----------- |\n| `code` | string | Machine-readable error code |\n| `message` | string | Human-readable error description |\n| `field` | string | The field that caused the error (if applicable) |\n\n\n# HTTP Status Codes\n\nThe API uses standard HTTP status codes to indicate the success or failure of requests:\n\n## Success Codes\n\n| Code | Description |\n| ---- | ----------- |\n| `200 OK` | Request succeeded |\n| `201 Created` | Resource successfully created |\n| `204 No Content` | Request succeeded with no response body |\n\n## Client Error Codes\n\n| Code | Description |\n| ---- | ----------- |\n| `400 Bad Request` | Invalid request syntax or parameters |\n| `401 Unauthorized` | Missing or invalid API key |\n| `403 Forbidden` | Valid API key but insufficient permissions |\n| `404 Not Found` | Requested resource does not exist |\n| `422 Unprocessable Entity` | Validation error in request body |\n| `429 Too Many Requests` | Rate limit exceeded |\n\n## Server Error Codes\n\n| Code | Description |\n| ---- | ----------- |\n| `500 Internal Server Error` | Unexpected server error (rare) |\n| `502 Bad Gateway` | Upstream service unavailable |\n| `503 Service Unavailable` | Service temporarily unavailable |\n\n\n# Error Handling Best Practices\n\n1. **Always check the `status` field** — Verify if the response indicates `\"success\"` or `\"error\"`\n\n2. **Handle specific error codes** — Use the `error.code` field to handle different error types programmatically\n\n3. **Implement retry logic** — For `429` and `5xx` errors, implement exponential backoff\n\n4. **Log error details** — Capture `error.code`, `error.message`, and `error.field` for debugging\n\n**Example error handling:**\n```python\nresponse = api.create_camera(data)\n\nif response[\"status\"] == \"error\":\n    error = response[\"error\"]\n    if error[\"code\"] == \"VALIDATION_ERROR\":\n        print(f\"Invalid {error['field']}: {error['message']}\")\n    elif error[\"code\"] == \"DUPLICATE_RESOURCE\":\n        print(\"Camera already exists\")\n    else:\n        print(f\"Error: {error['message']}\")\n```\n\n---\n\n# Getting Started\n\n1. **Generate an API key** from your Coram app settings\n2. **Explore the endpoints** in the navigation to begin integrating\n\nNeed help? Contact [support@coram.ai](mailto:support@coram.ai)\n"
  version: 1.0.0
servers:
- url: https://developer.coram.ai
  description: Production
security:
- X-Auth-Token: []
tags:
- name: reunification
  description: Bulk-import your school reunification roster — schools, students (with guardians), teachers, staff, and class sections — programmatically, the API equivalent of the in-app CSV upload. Each import is a full roster replace; poll the status endpoint for completion and partial-success counts.
paths:
  /v1/reunification/imports:
    post:
      operationId: create_reunification_import
      summary: Import reunification roster data
      description: 'Bulk-imports reunification roster data — schools, students (with their

        guardians), teachers, and staff — as JSON. The programmatic equivalent

        of the in-app CSV upload.


        **This is a full roster replace, not a partial update.** Each call

        replaces the organization''s entire reunification roster: every person

        and school you send is inserted or updated, and **anything you omit is

        removed**. To keep someone, include them in every import; to remove

        someone, send a roster without them. Always send the complete roster —

        sending only `students`, for example, would delete all schools,

        teachers, and sections. To guard against that, the `schools`,

        `students`, `teachers`, and `sections` keys are **required to be

        present** (they may be empty arrays); `staff` is optional. This mirrors

        the in-app CSV upload, which requires every file.


        List each student, teacher, and staff member **once**. To place a person

        in multiple schools, put every school id in `school_ids` on that single

        object — a repeated `student_id`/`teacher_id`/`staff_id` is rejected

        (`400`), not merged.


        Cross-references are resolved against the ids in this same payload, so

        list an entity before you reference it. A `school_id`, `student_id`, or

        `teacher_id` that doesn''t resolve to something in the payload is

        silently skipped — the surrounding record is still imported, just

        without that link — so a mistyped id quietly drops a school membership

        or section rather than failing the import.


        Processing runs asynchronously and respects the same Auth0 rate limits

        as the in-app flow; only one import per organization runs at a time —

        a request made while one is already running returns `409`. The `202`

        response is the initial in-progress status — poll

        `GET /reunification/imports/status` for completion and partial-success

        counts.


        `school_location_mappings` maps each `school_id` in the payload to a

        Coram location ID. Requires `write:*` scope and an `admin` creator role;

        the feature must be enabled for the organization.

        '
      tags:
      - reunification
      security:
      - X-Auth-Token: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReunificationImportInput'
      responses:
        '202':
          description: Import accepted and processing started
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReunificationImportStatus'
        '400':
          description: Empty payload, missing school_location_mappings, or invalid data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
        '403':
          description: API key lacks the required scope/role, or the feature is not enabled for the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
        '409':
          description: An import is already running for this organization; poll the existing one instead
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
        '500':
          description: Unexpected server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
      x-public-api: true
      x-public-api-version: v1
      parameters: []
  /v1/reunification/imports/status:
    get:
      operationId: get_reunification_import_status
      summary: Get the current reunification import status
      description: 'Returns the status of the organization''s most recent reunification

        import, including per-entity synced/total counts and the

        `succeeded_users` / `failed_users` provisioning figures. Poll this

        after starting an import. Requires `read:*` scope and a `regular`

        creator role.


        Note: `succeeded_users` and `failed_users` are eventually consistent.

        Guest/staff accounts are provisioned in the background after the roster

        is imported, so these counts keep climbing for a few seconds after

        `status` becomes `completed`. Treat `completed` as "roster imported" and

        keep polling the user counts until they stop changing.


        `completed` means the roster was imported and account provisioning was

        dispatched — it does **not** by itself mean every account succeeded.

        Check `failed_users`: a non-zero value on a `completed` import is a

        partial success (e.g. seat limits or Auth0 rejections for some users).

        '
      tags:
      - reunification
      security:
      - X-Auth-Token: []
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReunificationImportStatus'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
        '403':
          description: API key lacks the required scope/role, or the feature is not enabled for the organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
        '404':
          description: No import found for this organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
        '500':
          description: Unexpected server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
      x-public-api: true
      x-public-api-version: v1
      parameters: []
components:
  schemas:
    ReunificationSection:
      type: object
      required:
      - section_id
      description: A class/section linking students to teachers (drives student↔teacher relationships).
      properties:
        section_id:
          type: string
        school_id:
          type: string
        teacher_ids:
          type: array
          items:
            type: string
        student_ids:
          type: array
          items:
            type: string
    ReunificationSchool:
      type: object
      required:
      - school_id
      - name
      properties:
        school_id:
          type: string
          description: Your identifier for the school; referenced by students/teachers/staff and school_location_mappings.
        name:
          type: string
        sis_id:
          type: string
        address:
          type: string
        city:
          type: string
        state:
          type: string
        zip:
          type: string
    ReunificationTeacher:
      type: object
      required:
      - teacher_id
      - name
      - school_ids
      properties:
        teacher_id:
          type: string
        name:
          type: string
        sis_id:
          type: string
        email:
          type: string
        phone_number:
          type: string
        school_ids:
          type: array
          items:
            type: string
    ReunificationImportInput:
      type: object
      required:
      - schools
      - students
      - teachers
      - sections
      - school_location_mappings
      description: 'Full-replace roster payload. `schools`, `students`, `teachers`, and

        `sections` must be present (may be empty arrays) so an omitted key can''t

        silently delete that entity type; `staff` is optional. Phone numbers are

        normalized to E.164; a number that can''t be parsed for the region is

        dropped (stored as null), not rejected — so a malformed guardian

        contact number is silently lost rather than failing the import.

        '
      properties:
        schools:
          type: array
          items:
            $ref: '#/components/schemas/ReunificationSchool'
        students:
          type: array
          items:
            $ref: '#/components/schemas/ReunificationStudent'
        teachers:
          type: array
          items:
            $ref: '#/components/schemas/ReunificationTeacher'
        staff:
          type: array
          items:
            $ref: '#/components/schemas/ReunificationStaff'
        sections:
          type: array
          description: Class/section memberships that establish student↔teacher relationships.
          items:
            $ref: '#/components/schemas/ReunificationSection'
        school_location_mappings:
          type: object
          additionalProperties:
            type: integer
            format: int32
          description: Maps each school_id in the payload to a Coram location ID.
    ReunificationStaff:
      type: object
      required:
      - staff_id
      - name
      - school_ids
      properties:
        staff_id:
          type: string
        name:
          type: string
        sis_id:
          type: string
        email:
          type: string
        phone_number:
          type: string
        title:
          type: string
        department:
          type: string
        roles:
          type: array
          items:
            type: string
        school_ids:
          type: array
          items:
            type: string
    ReunificationStudent:
      type: object
      required:
      - student_id
      - name
      - school_ids
      properties:
        student_id:
          type: string
        name:
          type: string
        sis_id:
          type: string
        email:
          type: string
        phone_number:
          type: string
        grade:
          type: string
        school_ids:
          type: array
          description: School IDs the student belongs to. The first is the primary school; the rest are secondary.
          items:
            type: string
        guardians:
          type: array
          items:
            $ref: '#/components/schemas/ReunificationGuardian'
    AuthErrorResponse:
      type: object
      required:
      - detail
      properties:
        detail:
          type: string
          description: 'Human-readable error description. Matches the shape FastAPI uses

            for `HTTPException` responses, so existing code that parses the

            Python developer-api''s auth errors works unchanged.

            '
    ReunificationGuardian:
      type: object
      required:
      - name
      properties:
        name:
          type: string
        email:
          type: string
        phone:
          type: string
        relationship:
          type: string
    ReunificationImportStatus:
      type: object
      required:
      - id
      - status
      description: 'Progress and partial-success report for a reunification import. Counts

        are cumulative for the current/most-recent import.

        '
      properties:
        id:
          type: string
        status:
          type: string
          enum:
          - in_progress
          - completed
          - failed
        synced_schools:
          type: integer
          format: int32
          default: 0
        total_schools:
          type: integer
          format: int32
          default: 0
        synced_students:
          type: integer
          format: int32
          default: 0
        total_students:
          type: integer
          format: int32
          default: 0
        synced_teachers:
          type: integer
          format: int32
          default: 0
        total_teachers:
          type: integer
          format: int32
          default: 0
        synced_staff:
          type: integer
          format: int32
          default: 0
        total_staff:
          type: integer
          format: int32
          default: 0
        synced_guardians:
          type: integer
          format: int32
          default: 0
        total_guardians:
          type: integer
          format: int32
          default: 0
        succeeded_users:
          type: integer
          format: int32
          default: 0
        failed_users:
          type: integer
          format: int32
          default: 0
        error_message:
          type: string
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
  securitySchemes:
    X-Auth-Token:
      type: apiKey
      in: header
      name: X-Auth-Token
x-tagGroups:
- name: Surveillance
  tags:
  - cameras
  - camera-groups
  - locations
  - nvrs
  - alerts
- name: Access Control
  tags:
  - doors
  - events
- name: Emergency Management System
  tags:
  - reunification