The Platform API

Sources & Events

Manage your videos, ask questions, and build applications with the Platform API. Explore the operations below for request details, responses, and examples.

Sources & Events

Register where your media comes from (a camera, a lane, a system), send in external events such as transactions or sensor triggers, link them to videos, and browse videos by source, time and your own metadata.

GET/api/v1/projects/:projectId/sourcesList sources

List sources

Sources come back in creation order, each with video_count and event_count.

POST/api/v1/projects/:projectId/sourcesRegister a source

Register a source

Safe to call on every start-up: an existing source with the same external_key is returned unchanged (200, source_created: false); a new one answers 201. kind is one of api (default), upload, url_import, rtsp, connector, edge_bridge, external_pipeline or event_feed. Set config.timezone so day and hour groups follow the source's local time. Requires write access.

{
    "external_key": "plaza-6-lane-4",
    "kind": "event_feed",
    "label": "Plaza 6, lane 4",
    "config": { "timezone": "America/New_York" }
  }
// Response (201)
  {
    "id": "src_SOURCE_ID",
    "external_key": "plaza-6-lane-4",
    "kind": "event_feed",
    "label": "Plaza 6, lane 4",
    "config": { "timezone": "America/New_York" },
    "status": "active",
    "created_at": "2026-10-01T09:00:00Z",
    "source_created": true
  }
PATCH/api/v1/projects/:projectId/sources/:sourceIdUpdate a source

Update a source

Updates kind, label, config or status; other fields are ignored. config is replaced as a whole. Requires write access.

POST/api/v1/projects/:projectId/eventsSend events

Send events

Creates or updates up to 1,000 events for one registered source, named by source_id or source_key. Each event needs external_id and occurred_at; re-sending the same external_id updates it instead of duplicating it. Requires write access.

curl -X POST https://platform.endlessai.com/api/v1/projects/PROJECT_ID/events \
    -H "X-Api-Key: YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "source_key": "plaza-6-lane-4",
      "events": [
        {
          "external_id": "txn-88213",
          "kind": "toll_transaction",
          "occurred_at": "2026-10-01T14:03:12Z",
          "occurred_until": "2026-10-01T14:03:20Z",
          "meta": { "vehicle_class": "2" }
        }
      ]
    }'
GET/api/v1/projects/:projectId/eventsList events

List events

Oldest first. Filter with source_id and status; page forward by passing the created_at of the last event as since. limit defaults to 100 (max 500).

POST/api/v1/projects/:projectId/events/:eventId/mediaLink a video or media item to an event

Link a video or media item to an event

Give exactly one of video_id or media_id, plus an optional role (default primary). Returns 204. Requires write access.

{ "video_id": "VIDEO_ID", "role": "primary" }
GET/api/v1/projects/:projectId/videos/:videoId/eventsList a video's events

List a video's events

Send the request to https://platform.endlessai.com/api/v1/projects/:projectId/videos/:videoId/events using your project key or user token.

GET/api/v1/projects/:projectId/media/:mediaId/eventsList a media item's events

List a media item's events

Send the request to https://platform.endlessai.com/api/v1/projects/:projectId/media/:mediaId/events using your project key or user token.

GET/api/v1/projects/:projectId/display-schemaGet the display schema

Get the display schema

The fields a browsing view shows for each video, and which meta.* fields can be used as filters. Returns { "fields": [] } until one is set.

PUT/api/v1/projects/:projectId/display-schemaSet the display schema

Set the display schema

Replaces the schema (at most 32 fields). type is enum, id, timestamp, number or string; set facet: true on a meta.* field to allow filtering by it. Requires write access.

{
    "fields": [
      { "key": "meta.vehicle_class", "label": "Vehicle class", "type": "enum", "facet": true },
      { "key": "external_id", "label": "Transaction", "type": "id" }
    ],
    "group_by_default": ["source", "day"]
  }
GET/api/v1/projects/:projectId/videos/browseBrowse videos

Browse videos

Newest capture first. Filter with source_id, from, to, tag and repeatable facet=meta.KEY:value (declared facets only). Page with limit (default 50, max 200) and offset; the response carries total and has_more.

GET/api/v1/projects/:projectId/videos/groupsCount videos by day or hour

Count videos by day or hour

Counts per source per bucket (day or hour), with ready and errors. Takes the same filters as browse.

// Response
  {
    "groups": [
      {
        "source_id": "src_SOURCE_ID",
        "source_label": "Plaza 6, lane 4",
        "source_kind": "event_feed",
        "bucket_start": "2026-10-01T00:00:00",
        "count": 214,
        "ready": 210,
        "errors": 1
      }
    ],
    "display_schema": { "fields": [] }
  }