Dokumentasi / CCTV Beta
CCTV API
Host https://cctv.larika.id. Kirim API key di header Authorization: Bearer atau x-api-key. Deskripsi endpoint masih berbahasa Inggris.
Node
GET/v1/nodesNodes
The nodes (locations) of the key’s client (the management key: every node) — business hours, camera count, whether it is online.
curl -X GET 'https://cctv.larika.id/v1/nodes' \
-H 'Authorization: Bearer $LARIKA_KEY'200
[
{
"id": "cmuwaprz30002kniwevk87652",
"name": "Kantor Depati",
"timezone": "Asia/Jakarta",
"openTime": "08:00",
"closeTime": "17:00",
"clientId": "cmuwcl0001",
"cameras": 2,
"online": true
}
]Cameras
GET/v1/camerasList cameras
Cameras of the key’s nodes (nodeId = one node) with status (ONLINE · OFFLINE · UNKNOWN = node offline), recording, detection settings, zones and lines.
curl -X GET 'https://cctv.larika.id/v1/cameras' \
-H 'Authorization: Bearer $LARIKA_KEY'200
[
{
"id": "cmuwarje90004kniwsvv67q1u",
"nodeId": "cmuwaprz30002kniwevk87652",
"name": "Ruang tamu",
"status": "ONLINE",
"error": null,
"ptz": true,
"hasSub": true,
"manufacturer": "macro-video-soft",
"model": "IPCamera",
"recordMode": "MOTION",
"retentionDays": 7,
"motionEnabled": true,
"motionThreshold": 0.003,
"detect": {
"tamper": true,
"frozen": true,
"lights": true,
"sound": false,
"soundDb": -10,
"cameraEvents": true,
"person": true,
"vehicle": false,
"aiScore": 0.45,
"face": false,
"faceScore": 0.7,
"soundAi": false,
"soundAiScore": 0.35
},
"zones": [
{
"id": "pintu",
"name": "Pintu masuk",
"points": [
[
0.25,
0
],
[
0.85,
0
],
[
0.85,
0.55
],
[
0.25,
0.55
]
],
"schedule": "always",
"dwellSec": 120
}
],
"lines": [
{
"id": "pintu",
"name": "Pintu",
"points": [
[
0.45,
0.15
],
[
0.95,
0.15
]
]
}
],
"lastMotionAt": "2026-10-06T07:04:20.000Z",
"createdAt": "2026-10-06T07:04:20.000Z"
}
]GET/v1/cameras/{camera}One camera
One camera, same shape as in the list.
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Camera id (List cameras). |
curl -X GET 'https://cctv.larika.id/v1/cameras/{camera}' \
-H 'Authorization: Bearer $LARIKA_KEY'200
{
"id": "cmuwarje90004kniwsvv67q1u",
"nodeId": "cmuwaprz30002kniwevk87652",
"name": "Ruang tamu",
"status": "ONLINE",
"error": null,
"ptz": true,
"hasSub": true,
"manufacturer": "macro-video-soft",
"model": "IPCamera",
"recordMode": "MOTION",
"retentionDays": 7,
"motionEnabled": true,
"motionThreshold": 0.003,
"detect": {
"tamper": true,
"frozen": true,
"lights": true,
"sound": false,
"soundDb": -10,
"cameraEvents": true,
"person": true,
"vehicle": false,
"aiScore": 0.45,
"face": false,
"faceScore": 0.7,
"soundAi": false,
"soundAiScore": 0.35
},
"zones": [
{
"id": "pintu",
"name": "Pintu masuk",
"points": [
[
0.25,
0
],
[
0.85,
0
],
[
0.85,
0.55
],
[
0.25,
0.55
]
],
"schedule": "always",
"dwellSec": 120
}
],
"lines": [
{
"id": "pintu",
"name": "Pintu",
"points": [
[
0.45,
0.15
],
[
0.95,
0.15
]
]
}
],
"lastMotionAt": "2026-10-06T07:04:20.000Z",
"createdAt": "2026-10-06T07:04:20.000Z"
}PATCH/v1/cameras/{camera}Settings & detectors
Name, recording (OFF · MOTION = minutes around events, with pre-roll · CONTINUOUS), retention, motion sensitivity, and the detectors. detect is merged: send only the switches you change. The node applies it at once.
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Camera id (List cameras). |
| recordMode | body | string | OFF · MOTION · CONTINUOUS |
| retentionDays | body | number | Recordings older than this are deleted on the node. |
| motionThreshold | body | number | Fraction of pixels changed that counts as motion (default 0.003). |
| detect | body | object | tamper frozen lights sound soundDb cameraEvents · AI: person vehicle aiScore face faceScore soundAi soundAiScore |
curl -X PATCH 'https://cctv.larika.id/v1/cameras/{camera}' \
-H 'Authorization: Bearer $LARIKA_KEY' \
-H 'content-type: application/json' \
-d '{"detect":{"person":true,"vehicle":false}}'200
{
"id": "cmuwarje90004kniwsvv67q1u",
"nodeId": "cmuwaprz30002kniwevk87652",
"name": "Ruang tamu",
"status": "ONLINE",
"error": null,
"ptz": true,
"hasSub": true,
"manufacturer": "macro-video-soft",
"model": "IPCamera",
"recordMode": "MOTION",
"retentionDays": 7,
"motionEnabled": true,
"motionThreshold": 0.003,
"detect": {
"tamper": true,
"frozen": true,
"lights": true,
"sound": false,
"soundDb": -10,
"cameraEvents": true,
"person": true,
"vehicle": false,
"aiScore": 0.45,
"face": false,
"faceScore": 0.7,
"soundAi": false,
"soundAiScore": 0.35
},
"zones": [
{
"id": "pintu",
"name": "Pintu masuk",
"points": [
[
0.25,
0
],
[
0.85,
0
],
[
0.85,
0.55
],
[
0.25,
0.55
]
],
"schedule": "always",
"dwellSec": 120
}
],
"lines": [
{
"id": "pintu",
"name": "Pintu",
"points": [
[
0.45,
0.15
],
[
0.95,
0.15
]
]
}
],
"lastMotionAt": "2026-10-06T07:04:20.000Z",
"createdAt": "2026-10-06T07:04:20.000Z"
}PUT/v1/cameras/{camera}/zonesZones
All zones of the camera (replaced). Points in 0–1 of the picture (0,0 top left). Zone events: zone.motion.* (pixels), zone.enter / zone.exit / dwell.long (AI). schedule: always · open (business hours only) · closed (outside them).
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Camera id (List cameras). |
curl -X PUT 'https://cctv.larika.id/v1/cameras/{camera}/zones' \
-H 'Authorization: Bearer $LARIKA_KEY' \
-H 'content-type: application/json' \
-d '[{"id":"kasir","name":"Kasir","points":[[0,0.5],[0.4,0.5],[0.4,1],[0,1]],"schedule":"always","dwellSec":120}]'200
[
{
"id": "pintu",
"name": "Pintu masuk",
"points": [
[
0.25,
0
],
[
0.85,
0
],
[
0.85,
0.55
],
[
0.25,
0.55
]
],
"schedule": "always",
"dwellSec": 120
}
]PUT/v1/cameras/{camera}/linesCounting lines
All lines of the camera (replaced): line.crossed with direction in / out. in = crossing in the direction of point 1 → 2 turned 90° clockwise on the picture (a line drawn left → right: top → bottom is in).
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Camera id (List cameras). |
curl -X PUT 'https://cctv.larika.id/v1/cameras/{camera}/lines' \
-H 'Authorization: Bearer $LARIKA_KEY' \
-H 'content-type: application/json' \
-d '[{"id":"pintu","name":"Pintu","points":[[0.45,0.15],[0.95,0.15]]}]'200
[
{
"id": "pintu",
"name": "Pintu",
"points": [
[
0.45,
0.15
],
[
0.95,
0.15
]
]
}
]Watch
GET/v1/cameras/{camera}/snapshotSnapshot
The current frame (JPEG), from the node. Logged in the access log.
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Camera id (List cameras). |
curl -X GET 'https://cctv.larika.id/v1/cameras/{camera}/snapshot' \
-H 'Authorization: Bearer $LARIKA_KEY'200 image/jpegPOST/v1/cameras/{camera}/liveLive view ticket
A one-time WebSocket URL (60 s to open, one viewer) for go2rtc’s player at /player/video-rtc.js, MSE mode. Get a new one for every (re)connect. Logged in the access log.
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Camera id (List cameras). |
| quality | body | string | main (default) or sub (smaller, for grids). |
curl -X POST 'https://cctv.larika.id/v1/cameras/{camera}/live' \
-H 'Authorization: Bearer $LARIKA_KEY' \
-H 'content-type: application/json' \
-d '{"quality":"sub"}'200
{
"url": "wss://cctv.example.com/live/9xQ…",
"expiresAt": "2026-10-06T07:04:20.000Z"
}POST/v1/cameras/{camera}/ptzPTZ move / stop
Move at a speed (−1…1) until you send all 0 (stop). Hold-to-move: a move on press, a stop on release. 409 camera_has_no_ptz on fixed cameras.
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Camera id (List cameras). |
curl -X POST 'https://cctv.larika.id/v1/cameras/{camera}/ptz' \
-H 'Authorization: Bearer $LARIKA_KEY' \
-H 'content-type: application/json' \
-d '{"x":0,"y":0,"zoom":0}'200
{
"ok": true
}Footage
GET/v1/cameras/{camera}/recordingsRecordings
Files on the node, newest first (1-minute MP4s). recording: true = the minute being written. from / to: epoch ms or ISO.
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Camera id (List cameras). |
| from | query | ms | ISO | Start of the range. |
| to | query | ms | ISO | End of the range. |
curl -X GET 'https://cctv.larika.id/v1/cameras/{camera}/recordings' \
-H 'Authorization: Bearer $LARIKA_KEY'200
[
{
"file": "20261006-140400.mp4",
"startedAt": 1791271440000,
"endedAt": 1791271500000,
"bytes": 1843200,
"recording": false
}
]GET/v1/cameras/{camera}/recordings/{file}Download a recording
One file, streamed from the node (fragmented MP4: a minute still being written plays as far as it got). Logged in the access log.
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Camera id (List cameras). |
| file * | path | string | file from Recordings. |
curl -X GET 'https://cctv.larika.id/v1/cameras/{camera}/recordings/{file}' \
-H 'Authorization: Bearer $LARIKA_KEY'200 video/mp4GET/v1/eventsEvents
Stored events, newest first. before = the at of the last one seen (cursor). clipPath = its video. Kept EVENT_RETENTION_DAYS (90).
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| type | query | string | e.g. person.detected |
| cameraId | query | string | |
| before | query | ms | ISO | |
| limit | query | number | 1–200, default 50 |
curl -X GET 'https://cctv.larika.id/v1/events' \
-H 'Authorization: Bearer $LARIKA_KEY'200
[
{
"id": "cmuxe0001",
"nodeId": "cmuwaprz30002kniwevk87652",
"cameraId": "cmuwarje90004kniwsvv67q1u",
"type": "person.detected",
"at": "2026-10-06T07:04:20.000Z",
"clipPath": "/v1/events/cmuxe0001/clip",
"data": {
"state": "start",
"count": 1,
"boxes": [
[
0.12,
0.3,
0.25,
0.71
]
],
"confidence": 0.81,
"source": "node",
"cameraName": "Ruang tamu",
"snapshotId": "WC5Xq4jc7c1y",
"snapshotUrl": "https://cctv.example.com/s/WC5Xq4jc7c1y.jpg"
}
}
]GET/v1/events/{event}/clipEvent clip
The video of one event (MP4), cut from the recordings: pre s before it until post s after it — or after its end (motion.end, state: end) for things that last; ≤ 10 min. Needs the camera recording (MOTION keeps the minutes around events). Logged in the access log.
| Parameter | Di | Tipe | Keterangan |
|---|---|---|---|
| id * | path | string | Event id (eventId in webhooks). |
| pre | query | seconds | default 10 |
| post | query | seconds | default 15 |
curl -X GET 'https://cctv.larika.id/v1/events/{event}/clip' \
-H 'Authorization: Bearer $LARIKA_KEY'200 video/mp4Webhook
Satu webhook per klien untuk semua node. Setiap permintaan membawa x-larika-event dan x-larika-webhook-key; cocokkan yang kedua dengan kunci webhook Anda.
| Kejadian | Keterangan |
|---|---|
| motion.start / motion.end · motion.afterhours | Pixel motion (whole picture); after-hours copy outside business hours. |
| zone.motion.start / end | Pixel motion inside a zone. |
| person.detected · vehicle.detected · face.detected | AI: `state` start (count, boxes) / end. |
| zone.enter · zone.exit · dwell.long · line.crossed | AI: zones and counting lines. |
| sound.loud · sound.classified | Loudness / what the sound is (glass, alarm, scream, …). |
| camera.tamper · lights.on / off | Covered, blurred, moved; room lights. |
| intrusion · input.alarm · camera.motion · camera.event | The camera’s own (ONVIF) events. |
| camera.online / offline · stream.frozen · stream.degraded · recording.gap | Camera / video health. |
| camera.ip.changed · camera.clock.drift · camera.weak_password · camera.ptz.moved | Camera health and security. |
| node.online / offline · node.clock.drift · disk.low · device.new | The node and its LAN. |
{
"id": "cmuwh0001",
"event": "person.detected",
"nodeId": "cmuwaprz30002kniwevk87652",
"ts": 1791271460000,
"data": {
"state": "start",
"count": 1,
"boxes": [
[
0.12,
0.3,
0.25,
0.71
]
],
"confidence": 0.81,
"source": "node",
"cameraName": "Ruang tamu",
"snapshotId": "WC5Xq4jc7c1y",
"snapshotUrl": "https://cctv.example.com/s/WC5Xq4jc7c1y.jpg",
"cameraId": "cmuwarje90004kniwsvv67q1u",
"at": "2026-10-06T07:04:20.000Z",
"eventId": "cmuxe0001",
"clipPath": "/v1/events/cmuxe0001/clip",
"afterHours": true
}
}