Sources & Events
Manage your videos, ask questions, and build applications with the Platform API. Explore the operations below for request details, responses, and examples.
No matching documentation. Try a different search.
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" }
}
]
}'import requests
resp = requests.post(
f"{BASE}/projects/{PROJECT_ID}/events",
headers={"X-Api-Key": API_KEY},
json={
"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"},
}
],
},
)
print(resp.json()) # {"events": [...], "created": 1, "updated": 0}const res = await fetch(`${BASE}/projects/${projectId}/events`, {
method: "POST",
headers: { "X-Api-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
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" },
},
],
}),
});
console.log(await res.json()); // { events: [...], created: 1, updated: 0 }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": [] }
}