Coram Developer API

Programmatic access to Coram physical-security infrastructure — cameras, camera groups, NVRs, locations, access-control doors and events, firearm-detection alerts, and school reunification roster imports. API-key auth via the X-Auth-Token header.

OpenAPI Specification

coram-ai-openapi-original.json Raw ↑
{"openapi":"3.0.0","info":{"title":"Coram 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"}],"paths":{"/v1/alerts/{alert_event_id}/clip":{"get":{"tags":["alerts"],"summary":"Get alert video clip","description":"Returns a public download URL for the MP4 video clip associated with an alert event. The URL does not require authentication and expires after 24 hours. Use the alert_event_id from webhook payloads to retrieve the corresponding video clip.","operationId":"get_alert_clip","parameters":[{"required":true,"schema":{"type":"integer","title":"Alert Event Id"},"name":"alert_event_id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertClipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-public-api":true,"security":[{"X-Auth-Token":[]}]}},"/v1/alerts/{alert_event_id}/feedback":{"get":{"tags":["alerts"],"summary":"Get firearm alert feedback","description":"Returns the current feedback (0.0-1.0 rating, reason, comment) for a firearm alert event. Feedback is organization-wide (last write wins); the feedback fields are null when no feedback has been submitted yet. Returns 404 if the alert event does not exist and 400 if it is not a firearm alert.","operationId":"get_firearm_alert_feedback","parameters":[{"required":true,"schema":{"type":"integer","title":"Alert Event Id"},"name":"alert_event_id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirearmAlertFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-public-api":true,"security":[{"X-Auth-Token":[]}]},"put":{"tags":["alerts"],"summary":"Submit firearm alert feedback","description":"Submits feedback for a firearm alert event. rating is a 0.0-1.0 score (today 0.0 = incorrect / unhelpful, 1.0 = correct). Feedback is organization-wide and idempotent (last write wins): submitting again replaces the previous feedback. feedback_reason and feedback_comment are optional. Returns 404 if the alert event does not exist and 400 if it is not a firearm alert.","operationId":"submit_firearm_alert_feedback","parameters":[{"required":true,"schema":{"type":"integer","title":"Alert Event Id"},"name":"alert_event_id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirearmAlertFeedbackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirearmAlertFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-public-api":true,"security":[{"X-Auth-Token":[]}]}},"/v1/alerts/{alert_event_id}/details":{"get":{"tags":["alerts"],"summary":"Get alert details (including AI description)","description":"Returns metadata about an alert event, including the rich natural-language description generated by Coram's vision language model. The description is produced asynchronously after the alert webhook fires, so consumers polling immediately may see `ai.status: pending` for a few seconds. Once `ai.status` is `ready`, the `ai.short_description` and `ai.long_description` fields are populated. If it is `failed`, no description will arrive.\n\nWhen a video clip is being produced for the alert, a `clip` object is included: `clip.status` is `ready` (with a download `mp4_url` valid for 24 hours and an `expires_at`), `pending` (still uploading — poll again shortly), or `failed`. Alerts with no clip pipeline omit the `clip` field entirely.","operationId":"get_alert_details","parameters":[{"required":true,"schema":{"type":"integer","title":"Alert Event Id"},"name":"alert_event_id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertDetailsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-public-api":true,"security":[{"X-Auth-Token":[]}]}},"/v1/alerts/firearm":{"post":{"tags":["alerts"],"summary":"Create firearm alert","description":"Creates a firearm-detection alert configuration. Recipients are specified by email (matched case-insensitively) and resolved to platform users in your organization; each recipient's notify_by_sms/notify_by_push only deliver when the user has a phone number / registered device. The schedule is interpreted in each camera's local time. The alert is attributed to the API key (creator_name = the key's identity). Note: a null name is omitted from the response rather than serialized as null.","operationId":"create_firearm_alert","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFirearmAlertRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirearmAlertResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Firearm alert not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Camera assignment conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true,"parameters":[]}},"/v1/alerts/firearm/{firearm_alert_id}":{"delete":{"tags":["alerts"],"summary":"Delete firearm alert","description":"Deletes a firearm-detection alert configuration and any alerts it has produced. Returns 204 on success and 404 if the firearm alert does not exist in your organization.","operationId":"delete_firearm_alert","parameters":[{"required":true,"schema":{"type":"integer","title":"Firearm Alert Id"},"name":"firearm_alert_id","in":"path"}],"responses":{"204":{"description":"Successful Response"},"403":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Firearm alert not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true},"patch":{"tags":["alerts"],"summary":"Update firearm alert","description":"Partially updates a firearm-detection alert configuration; only the provided fields are changed. Providing recipients replaces the full recipient list. The schedule is interpreted in each camera's local time. Note: a null name is omitted from the response rather than serialized as null.","operationId":"update_firearm_alert","parameters":[{"required":true,"schema":{"type":"integer","title":"Firearm Alert Id"},"name":"firearm_alert_id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFirearmAlertRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirearmAlertResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Firearm alert not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Camera assignment conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true}},"/v1/cameras":{"get":{"tags":["cameras"],"summary":"List cameras","description":"List all cameras accessible to the user.\n\nRetrieve cameras accessible to the user with their current status and\nconfiguration details. Results can be filtered by NVR, location,\nMAC address, IP address, or camera group ID.","operationId":"list_cameras","parameters":[{"required":false,"schema":{"items":{"type":"string"},"type":"array","title":"nvr"},"name":"nvrs","in":"query"},{"required":false,"schema":{"items":{"type":"integer"},"type":"array","title":"location_id"},"name":"location_ids","in":"query"},{"required":false,"schema":{"type":"boolean","title":"exclude_disabled","default":false},"name":"exclude_disabled","in":"query"},{"required":false,"schema":{"items":{"type":"string"},"type":"array","title":"mac_address"},"name":"mac_addresses","in":"query"},{"required":false,"schema":{"items":{"type":"string"},"type":"array","title":"ip"},"name":"ips","in":"query"},{"required":false,"schema":{"items":{"type":"integer"},"type":"array","title":"group_id"},"name":"group_ids","in":"query"},{"required":false,"schema":{"type":"integer","minimum":1.0,"title":"page","default":1},"name":"page","in":"query"},{"required":false,"schema":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"limit","default":100},"name":"limit","in":"query"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CameraList"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-public-api-version":"v1","x-public-api":true,"security":[{"X-Auth-Token":[]}]},"post":{"tags":["cameras"],"summary":"Register cameras","description":"Register multiple cameras in single location.\n\nRegister one or more cameras at a location by assigning them to available NVRs.\nCameras are automatically load-balanced across NVRs based on available capacity.","operationId":"register_cameras","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterCameraRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterCameraResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true,"parameters":[]},"delete":{"tags":["cameras"],"summary":"Delete cameras","description":"Delete one or more cameras.\n\nRemove one or more cameras from the system.","operationId":"delete_cameras","requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DeleteCameraPayload"},"type":"array","title":"DeleteCameraPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteCameraResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true,"parameters":[]},"patch":{"tags":["cameras"],"summary":"Update cameras","description":"Update one or more cameras.\n\nUpdate configuration, credentials, feature flags, and group assignments\nfor one or more cameras. Each camera is updated independently - if one\ncamera's update fails, others can still succeed. Within each camera's\nupdate, all changes are atomic.\n\n**Group Assignment:**\n- Provide `group_ids` to assign the camera to specific groups\n- An empty list `[]` resets the camera to the default group\n- Omit `group_ids` (null) to leave group assignments unchanged","operationId":"update_cameras","requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UpdateCameraPayload"},"type":"array","title":"UpdateCameraPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCameraResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true,"parameters":[]}},"/v1/cameras/move":{"post":{"tags":["cameras"],"summary":"Move cameras","description":"Move one or more cameras to different NVRs.\n\nTransfer one or more cameras from one NVR to another.","operationId":"move_cameras","requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/MoveCameraPayload"},"type":"array","title":"MoveCameraPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveCameraResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true,"parameters":[]}},"/v1/camera-groups":{"get":{"tags":["camera-groups"],"summary":"List camera groups","description":"List camera groups.\n\nRetrieve a paginated list of camera groups.\nGroups are used to organize cameras for easier management and access control.","operationId":"list_camera_groups","parameters":[{"required":false,"schema":{"type":"integer","minimum":1.0,"title":"Page","default":1},"name":"page","in":"query"},{"required":false,"schema":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Limit","default":100},"name":"limit","in":"query"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CameraGroupList"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-public-api-version":"v1","x-public-api":true,"security":[{"X-Auth-Token":[]}]},"post":{"tags":["camera-groups"],"summary":"Create camera group","description":"Create a new camera group.\n\nCreate a new camera group with the specified name. Camera groups are used\nto organize cameras for easier management and access control.","operationId":"create_camera_group","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCameraGroupPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCameraGroupResponse"}}}},"409":{"description":"Camera group name already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"400":{"description":"Camera group creation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true,"parameters":[]}},"/v1/nvrs":{"get":{"tags":["nvrs"],"summary":"List NVRs","description":"List all NVRs accessible to the user.\n\nRetrieve NVRs accessible to the user with their current status and\nconfiguration details. Results can be filtered by NVR UUID or location.","operationId":"list_nvrs","parameters":[{"required":false,"schema":{"type":"string","title":"nvr"},"name":"nvr","in":"query"},{"required":false,"schema":{"type":"integer","title":"location_id"},"name":"location_id","in":"query"},{"required":false,"schema":{"type":"integer","minimum":1.0,"title":"page","default":1},"name":"page","in":"query"},{"required":false,"schema":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"limit","default":100},"name":"limit","in":"query"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NVRList"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-public-api-version":"v1","x-public-api":true,"security":[{"X-Auth-Token":[]}]},"post":{"tags":["nvrs"],"summary":"Register NVRs","description":"Register multiple NVRs in bulk.\n\nRegister one or more NVRs by assigning them to locations.","operationId":"register_nvrs","requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RegisterNVRPayload"},"type":"array","title":"RegisterNVRPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterNVRResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true,"parameters":[]}},"/v1/nvrs/ping":{"post":{"tags":["nvrs"],"summary":"Ping NVR","description":"Run ping test on NVR.\n\nSend a ping request to the specified NVR to test network connectivity.","operationId":"ping_nvr","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PingRequestBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/PingSuccessResponse"},{"$ref":"#/components/schemas/PingFailureResponse"}],"title":"Response Ping Nvr"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-public-api-version":"v1","x-public-api":true,"security":[{"X-Auth-Token":[]}],"parameters":[]}},"/v1/nvrs/speedtest":{"post":{"tags":["nvrs"],"summary":"Speed test NVR","description":"Run speed test on NVR.\n\nInitiate a speed test on the specified NVR to measure network performance.","operationId":"speedtest_nvr","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeedTestRequestPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SpeedTestSuccessResponse"},{"$ref":"#/components/schemas/SpeedTestFailureResponse"}],"title":"Response Speedtest Nvr"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-public-api-version":"v1","x-public-api":true,"security":[{"X-Auth-Token":[]}],"parameters":[]}},"/v1/locations":{"get":{"tags":["locations"],"summary":"List locations","description":"List all locations accessible to the user.\n\nRetrieve locations accessible to the user for the organization.\nReturns locations across all product lines the user has access to.","operationId":"list_locations","parameters":[{"required":false,"schema":{"type":"integer","minimum":1.0,"title":"page","default":1},"name":"page","in":"query"},{"required":false,"schema":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"limit","default":100},"name":"limit","in":"query"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationList"}}}},"403":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"x-public-api-version":"v1","x-public-api":true,"security":[{"X-Auth-Token":[]}]},"post":{"tags":["locations"],"summary":"Create locations","description":"Create multiple locations in bulk.\n\nCreate one or more locations for the organization.","operationId":"create_locations","requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CreateLocationPayload"},"type":"array","title":"CreateLocationPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLocationResponse"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"X-Auth-Token":[]}],"x-public-api-version":"v1","x-public-api":true,"parameters":[]}},"/v1/access-control/events/{event_id}/clip":{"get":{"tags":["events"],"summary":"Get event clip URL","description":"Returns a public download URL for the MP4 video clip captured by the event's door's primary camera around the event timestamp. The URL does not require authentication and expires after 24 hours. Use the event_id from the events list endpoint or a webhook payload to fetch the clip.","operationId":"GetEventClip","parameters":[{"required":true,"schema":{"type":"integer","title":"Event Id"},"name":"event_id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventClipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-public-api-version":"v1","x-public-api":true,"security":[{"X-Auth-Token":[]}]}},"/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\nstatus. Admins see every door in the tenant; lower roles see only\ndoors they have permission to unlock. Use `page` and `limit` to\npaginate; `has_more` on the response signals more pages remain.\n","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/events":{"get":{"operationId":"list_access_control_events","summary":"List access control events","description":"Returns events emitted by access control devices: card scans\n(`card_reader_event`), grant/deny decisions (`access_control_event`),\nREX presses (`rex_event`), door-held-open alarms (`door_held_open_event`),\nbattery alerts, and so on. Use the filters to narrow down by door,\ncardholder, event type, or time window. Pagination uses the same\n`page` + `limit` + `has_more` shape as every other developer-API list\nendpoint.\n\nThe per-event `details` object carries event-type-specific data\n(card_id, access_decision_reason, etc.). Treat it defensively: new\nkeys may appear in future event types.\n","tags":["events"],"security":[{"X-Auth-Token":[]}],"parameters":[{"in":"query","name":"start_time","required":false,"schema":{"type":"string","format":"date-time"},"description":"RFC3339 inclusive lower bound on `timestamp`."},{"in":"query","name":"end_time","required":false,"schema":{"type":"string","format":"date-time"},"description":"RFC3339 inclusive upper bound on `timestamp`."},{"in":"query","name":"door_ids","required":false,"schema":{"type":"array","items":{"type":"integer","format":"int64"}},"style":"form","explode":true,"description":"Repeat to filter to events emitted at specific doors."},{"in":"query","name":"cardholder_ids","required":false,"schema":{"type":"array","items":{"type":"integer","format":"int64"}},"style":"form","explode":true,"description":"Repeat to filter to events for specific cardholders."},{"in":"query","name":"event_types","required":false,"schema":{"type":"array","items":{"type":"string","enum":["card_reader_event","access_control_event","remote_access_control_event","door_relay_event","door_forced_open_event","door_held_open_event","rex_event","door_position_event","fire_alarm_event","battery_status_event","device_lid_status_event","relay_testing_button_event","door_control_state_event"]}},"style":"form","explode":true,"description":"Repeat to filter to a subset of event types."},{"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 events per page (capped at 100)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"}}}},"400":{"description":"Invalid filter values","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","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.\nRequires `write:*` scope and an `admin` creator role. The same\npermission checks the in-app unlock flow runs are applied; an API key\nwhose creator no longer has access to the door is rejected.\n","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"}},"/v1/reunification/imports":{"post":

# --- truncated at 32 KB (96 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/coram-ai/refs/heads/main/openapi/coram-ai-openapi-original.json