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