Coram Ai doors API

List access control doors and trigger momentary remote unlocks. The same per-door permission checks the in-app unlock flow runs apply here — a key whose creator cannot unlock a door in the UI cannot unlock it via the API either.

OpenAPI Specification

coram-ai-doors-api-openapi.yml Raw ↑
openapi: 3.0.0
info:
  title: Coram alerts doors 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: doors
  description: List access control doors and trigger momentary remote unlocks. The same per-door permission checks the in-app unlock flow runs apply here — a key whose creator cannot unlock a door in the UI cannot unlock it via the API either.
paths:
  /v1/access-control/doors:
    get:
      operationId: list_access_control_doors
      summary: List access control doors
      description: 'Returns the doors the API key creator can see, with their online

        status. Admins see every door in the tenant; lower roles see only

        doors they have permission to unlock. Use `page` and `limit` to

        paginate; `has_more` on the response signals more pages remain.

        '
      tags:
      - doors
      security:
      - X-Auth-Token: []
      parameters:
      - in: query
        name: page
        required: false
        schema:
          type: integer
          minimum: 1
          default: 1
        description: 1-indexed page number.
      - in: query
        name: limit
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 100
        description: Maximum doors per page (capped at 100).
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DoorList'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
        '403':
          description: API key lacks the required scope or role
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
      x-public-api: true
      x-public-api-version: v1
  /v1/access-control/doors/{door_id}/unlock:
    post:
      operationId: unlock_access_control_door
      summary: Unlock an access control door
      description: 'Sends a momentary-unlock command to the door''s controller board.

        Requires `write:*` scope and an `admin` creator role. The same

        permission checks the in-app unlock flow runs are applied; an API key

        whose creator no longer has access to the door is rejected.

        '
      tags:
      - doors
      security:
      - X-Auth-Token: []
      parameters:
      - in: path
        name: door_id
        required: true
        schema:
          type: integer
          format: int64
        description: ID of the door to unlock
      responses:
        '200':
          description: Unlock command issued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnlockResponse'
        '400':
          description: Invalid door_id
          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 or role, or the creator cannot unlock this door
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthErrorResponse'
      x-public-api: true
      x-public-api-version: v1
components:
  schemas:
    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.

            '
    UnlockResponse:
      type: object
      required:
      - status
      properties:
        status:
          type: string
          enum:
          - success
          description: Always `success` on a 200; non-200 responses use AuthErrorResponse.
      x-public-api-group: doors
    DoorList:
      type: object
      required:
      - results
      - has_more
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/Door'
        has_more:
          type: boolean
          description: 'Reserved for future pagination; always false today. The endpoint

            currently returns every door the caller can see.

            '
      x-public-api-group: doors
    Door:
      type: object
      required:
      - id
      - name
      - location_id
      - is_online
      properties:
        id:
          type: integer
          format: int64
          description: Stable internal identifier of the door.
        name:
          type: string
          description: Human-readable door name as set in the Coram app.
        location_id:
          type: integer
          format: int64
          description: ID of the location this door belongs to. Zero when the door has no assigned location.
        is_online:
          type: boolean
          description: 'True if the door''s controller board has reported a heartbeat

            within the last 15 seconds.

            '
      x-public-api-group: doors
  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