Dokumentasi / SDK Pengenalan Wajah Dan Liveness / Referensi API
API ARSA Face SDK
API HTTP Face SDK v2. Error dikembalikan sebagai dokumen RFC 9457 problem+json dengan code yang stabil. Klien API melakukan autentikasi dengan API key (Authorization: Bearer <key> atau x-api-key). Deskripsi setiap endpoint di bawah masih dalam bahasa Inggris.
2.0.1
Recognition
Compare two faces (1:1), check a face against an enrolled person (1:1), find a face among everyone enrolled (1:N).
Find A Face Among Everyone Enrolled (1:N)
POST/v2/identify
Scope identify. Each face is identified (best match at or above the threshold for this gallery size and FPIR), unknown, or inconclusive (quality too poor to search).
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
attributes | string | Comma-separated subset of gender,expression, as for detection. |
fpir | number | Target false positive identification rate (default: tenant policy identify_fpir). |
imagewajib | string | Probe image; every face in it is searched. Multipart: an image file part (JPEG, PNG, BMP or WebP). JSON: the image as base64, optionally as a data: URL. |
k | integer | Candidates per face in investigation mode (1-50, default 5). |
mode | enum | investigation: return the top-k candidates instead of a decision. investigation |
quality | enum | Probe quality policy. default, surveillance |
Respons
| Status | Arti |
|---|---|
200 | One result per face |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
413 | Payload too large |
429 | Rate or licence limit reached |
503 | Busy, retry shortly |
Permintaan
curl -s -X POST "http://localhost:8080/v2/identify" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/identify",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/identify", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Respons 200
{
"face_count": 1,
"faces": [
{
"bbox": {
"height": 143,
"width": 102,
"x": 81,
"y": 50
},
"confidence": 0.9999,
"landmarks": [
[
104,
112
],
[
149,
112
],
[
124,
136
],
[
103,
155
],
[
152,
156
]
],
"passes": true,
"quality": {
"brightness": 150.2,
"eye_distance": 45,
"face_px": 143,
"pitch": 8.6,
"roll": 0,
"sharpness": 111.8,
"yaw": -5.1
},
"reasons": [],
"result": "identified",
"similarity": 0.7861,
"uid": "emp-1042"
}
],
"gallery_size": 250,
"model": "face-rec-b",
"threshold": {
"fpir": 0.01,
"method": "scaled",
"value": 0.3712
}
}Compare Two Faces (1:1)
POST/v2/verify
Scope verify. Compares the face of each image. When either image has no face the answer is 422 NO_FACE with the partial comparison in result; more than one face gives 422 MULTIPLE_FACES (see select); a face too poor to compare gives decision: inconclusive.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
far | number | Target false accept rate of the access profile (default 1e-4; the nearest stricter calibrated point is used). |
image1wajib | string | First image: one face (see select). Multipart: an image file part (JPEG, PNG, BMP or WebP). JSON: the image as base64, optionally as a data: URL. |
image2wajib | string | Second image: one face (see select). Multipart: an image file part (JPEG, PNG, BMP or WebP). JSON: the image as base64, optionally as a data: URL. |
model | string | Embedder id (GET /v2/models); default: the server's default embedder. |
profile | enum | Operating-point profile. access, access-relaxed |
select | enum | By default an image with more than one face is refused (422 MULTIPLE_FACES). With largest, the largest face is used when it is at least twice the size of the next (for example an ID document with a small second portrait). largest |
Respons
| Status | Arti |
|---|---|
200 | The comparison |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
413 | Payload too large |
422 | Refused (image quality or consistency) |
429 | Rate or licence limit reached |
503 | Busy, retry shortly |
Permintaan
curl -s -X POST "http://localhost:8080/v2/verify" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/verify",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/verify", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Respons 200
{
"decision": "match",
"faces": {
"image1": {
"bbox": {
"height": 143,
"width": 102,
"x": 81,
"y": 50
},
"confidence": 0.9999,
"landmarks": [
[
104,
112
],
[
149,
112
],
[
124,
136
],
[
103,
155
],
[
152,
156
]
],
"passes": true,
"quality": {
"brightness": 150.2,
"eye_distance": 45,
"face_px": 143,
"pitch": 8.6,
"roll": 0,
"sharpness": 111.8,
"yaw": -5.1
},
"reasons": []
},
"image2": {
"bbox": {
"height": 160,
"width": 118,
"x": 64,
"y": 41
},
"confidence": 0.9999,
"landmarks": [
[
104,
112
],
[
149,
112
],
[
124,
136
],
[
103,
155
],
[
152,
156
]
],
"passes": true,
"quality": {
"brightness": 132.7,
"eye_distance": 52,
"face_px": 160,
"pitch": 4.2,
"roll": -2,
"sharpness": 96.4,
"yaw": 3.3
},
"reasons": []
}
},
"match": true,
"model": "face-rec-b",
"operating_point": {
"far": 0.0001,
"profile": "access",
"threshold": 0.3479
},
"similarity": 0.8312,
"threshold": 0.3479
}Check A Face Against A Person (1:1)
POST/v2/verify/identity
Scope verify. 409 NEEDS_REENROLMENT when the identity has no template for the active embedder; 422 NO_FACE when the image has no face, 422 MULTIPLE_FACES when it has more than one (see select).
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
far | number | Target false accept rate (default: tenant policy verify_far). |
imagewajib | string | Probe image: one face (see select). Multipart: an image file part (JPEG, PNG, BMP or WebP). JSON: the image as base64, optionally as a data: URL. |
profile | enum | Default: tenant policy verify_profile. access, access-relaxed |
select | enum | By default an image with more than one face is refused (422 MULTIPLE_FACES). With largest, the largest face is used when it is at least twice the size of the next (for example an ID document with a small second portrait). largest |
uidwajib | string | The identity to verify against. |
Respons
| Status | Arti |
|---|---|
200 | The comparison against the identity's best template |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
409 | Conflict |
413 | Payload too large |
422 | Refused (image quality or consistency) |
429 | Rate or licence limit reached |
503 | Busy, retry shortly |
Permintaan
curl -s -X POST "http://localhost:8080/v2/verify/identity" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/verify/identity",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/verify/identity", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Respons 200
{
"decision": "match",
"face": {
"bbox": {
"height": 143,
"width": 102,
"x": 81,
"y": 50
},
"confidence": 0.9999,
"landmarks": [
[
104,
112
],
[
149,
112
],
[
124,
136
],
[
103,
155
],
[
152,
156
]
],
"passes": true,
"quality": {
"brightness": 150.2,
"eye_distance": 45,
"face_px": 143,
"pitch": 8.6,
"roll": 0,
"sharpness": 111.8,
"yaw": -5.1
},
"reasons": []
},
"match": true,
"model": "face-rec-b",
"operating_point": {
"far": 0.0001,
"profile": "access",
"threshold": 0.3479
},
"similarity": 0.7861,
"threshold": 0.3479,
"uid": "emp-1042"
}People And Enrolment
Enrol people, add photos, list and delete them; groups such as watchlists.
List Groups
GET/v2/groups
Scope enrol. Groups of people (a watchlist, VIPs, staff) with an alert level: alert and notice are carried by camera events (groups, alert) so screens and webhook receivers can react; none is a label only.
Respons
| Status | Arti |
|---|---|
200 | The tenant's groups with member counts |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/groups" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/groups",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/groups", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Create A Group
POST/v2/groups
Scope admin. Names are unique per tenant (1-64 characters). Audited (group.create).
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
level | enum | alert, notice, none |
namewajib | string |
Respons
| Status | Arti |
|---|---|
200 | The new group |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
409 | Conflict |
Permintaan
curl -s -X POST "http://localhost:8080/v2/groups" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/groups",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/groups", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Rename A Group Or Change Its Level
PATCH/v2/groups/{id}
Scope admin. Takes effect on the next camera event. Audited (group.update).
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Group id. |
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
level | enum | alert, notice, none |
name | string |
Respons
| Status | Arti |
|---|---|
200 | The group |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
409 | Conflict |
Permintaan
curl -s -X PATCH "http://localhost:8080/v2/groups/{id}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.patch(
"http://localhost:8080/v2/groups/{id}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/groups/{id}", {
method: "PATCH",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Delete A Group
DELETE/v2/groups/{id}
Scope admin. Audited (group.delete).
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Group id. |
Respons
| Status | Arti |
|---|---|
200 | Deleted; its members stay enrolled |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X DELETE "http://localhost:8080/v2/groups/{id}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.delete(
"http://localhost:8080/v2/groups/{id}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/groups/{id}", {
method: "DELETE",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());List People
GET/v2/identities
Scope enrol.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
prefix query | string | Only uids starting with this. |
after query | string | Uids after this one (next_after of the previous page). |
limit query | integer | Page size (1-1000). |
Respons
| Status | Arti |
|---|---|
200 | A page of identities, by uid |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/identities" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/identities",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/identities", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Enrol A Person
POST/v2/identities
Scope enrol. Every image must show one face passing the tenant's enrolment policy, and the images must show the same person; otherwise 422 with per-image images: [{image, reasons}] (or INCONSISTENT_ENROLMENT_IMAGES). 409 CONFLICT when the uid exists, 409 POSSIBLE_DUPLICATE (with existing_uid and similarity only for keys that also have the identify scope) when the face is already enrolled under another uid. 429 LICENSE_LIMIT at the licence's identity limit.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
display_name | string | Optional display name. |
force | boolean | string | true: skip the duplicate check (needs the admin scope). |
imagewajib | string | Enrolment image; send 1 to 5 images (e.g. image, image2 .. image5), the first 5 are used. Multipart: an image file part (JPEG, PNG, BMP or WebP). JSON: the image as base64, optionally as a data: URL. |
select | enum | Accept an image with several faces when the largest is at least twice the size of the next. largest |
uidwajib | string | Identity id: 1-128 characters of A-Z a-z 0-9 . _ : @ -. |
Respons
| Status | Arti |
|---|---|
201 | Enrolled |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
409 | Conflict |
413 | Payload too large |
422 | Refused (image quality or consistency) |
429 | Rate or licence limit reached |
503 | Busy, retry shortly |
Permintaan
curl -s -X POST "http://localhost:8080/v2/identities" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/identities",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/identities", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Get A Person
GET/v2/identities/{uid}
Scope enrol.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
uidwajib path | string | Identity id. |
Respons
| Status | Arti |
|---|---|
200 | The identity and its templates |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X GET "http://localhost:8080/v2/identities/{uid}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/identities/{uid}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/identities/{uid}", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Delete A Person
DELETE/v2/identities/{uid}
Scope enrol. From the dashboard this also needs an elevated session; 403 otherwise.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
uidwajib path | string | Identity id. |
Respons
| Status | Arti |
|---|---|
200 | Deleted with its templates |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X DELETE "http://localhost:8080/v2/identities/{uid}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.delete(
"http://localhost:8080/v2/identities/{uid}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/identities/{uid}", {
method: "DELETE",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Set A Person's Groups
PUT/v2/identities/{uid}/groups
Scope admin. Replaces the list (an empty list removes the person from every group). Audited (identity.groups, group names before and after).
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
uidwajib path | string | Identity id. |
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
groupswajib | string[] | Group ids. |
Respons
| Status | Arti |
|---|---|
200 | The person's groups |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X PUT "http://localhost:8080/v2/identities/{uid}/groups" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.put(
"http://localhost:8080/v2/identities/{uid}/groups",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/identities/{uid}/groups", {
method: "PUT",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Add Photos To A Person
POST/v2/identities/{uid}/templates
Scope enrol. Same image checks as enrolment; each new image must also match the identity's existing templates (422 INCONSISTENT_ENROLMENT_IMAGES otherwise).
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
uidwajib path | string | Identity id. |
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
imagewajib | string | Additional image; send 1 to 5 images, the first 5 are used. Multipart: an image file part (JPEG, PNG, BMP or WebP). JSON: the image as base64, optionally as a data: URL. |
select | enum | As for enrolment. largest |
Respons
| Status | Arti |
|---|---|
201 | Templates added |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
413 | Payload too large |
422 | Refused (image quality or consistency) |
429 | Rate or licence limit reached |
503 | Busy, retry shortly |
Permintaan
curl -s -X POST "http://localhost:8080/v2/identities/{uid}/templates" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/identities/{uid}/templates",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/identities/{uid}/templates", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Face Attributes
Find faces, judge their quality, and estimate gender and expression.
Detect Faces: Quality, Gender And Expression
POST/v2/detect
Scope verify. Attributes also need the analytics licence module.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
attributes | string | Comma-separated subset of gender,expression to compute. Default: the attributes enabled in the tenant policy. Asking for one that is not enabled is 403 FEATURE_DISABLED. |
imagewajib | string | The image (the first image field is used). Multipart: an image file part (JPEG, PNG, BMP or WebP). JSON: the image as base64, optionally as a data: URL. |
quality | enum | Quality policy the passes / reasons of each face are judged against. default, surveillance, enrol, strict_enrol, passport, cctv |
Respons
| Status | Arti |
|---|---|
200 | The faces found, largest first |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
413 | Payload too large |
429 | Rate or licence limit reached |
503 | Busy, retry shortly |
Permintaan
curl -s -X POST "http://localhost:8080/v2/detect" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/detect",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/detect", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Respons 200
{
"face_count": 1,
"faces": [
{
"bbox": {
"height": 143,
"width": 102,
"x": 81,
"y": 50
},
"confidence": 0.9999,
"landmarks": [
[
104,
112
],
[
149,
112
],
[
124,
136
],
[
103,
155
],
[
152,
156
]
],
"passes": true,
"quality": {
"brightness": 150.2,
"eye_distance": 45,
"face_px": 143,
"pitch": 8.6,
"roll": 0,
"sharpness": 111.8,
"yaw": -5.1
},
"reasons": []
}
]
}Liveness
Passive liveness from photos and active (guided) liveness sessions.
Passive Liveness (1-5 Photos)
POST/v2/liveness
Scope liveness, licence module passive_liveness. Fails closed: any problem with the frames gives inconclusive, never live. Covers printed photos and screen replays.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
imagewajib | string | A frame; send 1 to 5 frames of the same person (5 recommended), the first 5 are used. Multipart: an image file part (JPEG, PNG, BMP or WebP). JSON: the image as base64, optionally as a data: URL. |
profile | enum | Decision threshold profile. convenience, security |
Respons
| Status | Arti |
|---|---|
200 | The decision |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
413 | Payload too large |
429 | Rate or licence limit reached |
503 | Busy, retry shortly |
Permintaan
curl -s -X POST "http://localhost:8080/v2/liveness" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/liveness",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/liveness", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Result-signing Public Key
GET/v2/liveness/public-key
Verify signature over the UTF-8 bytes of signed_payload with this key.
Respons
| Status | Arti |
|---|---|
200 | The instance's key |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/liveness/public-key"import requests
r = requests.get(
"http://localhost:8080/v2/liveness/public-key",
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/liveness/public-key", {
method: "GET",
});
console.log(r.status, await r.text());Start An Active Liveness Session
POST/v2/liveness/sessions
Scope liveness, licence module active_liveness. Spends one call of the licence budget. The body is optional. Connect to ws_url within ttl_seconds.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
flash_opt_out | boolean | Skip the colour-flash step (e.g. for photosensitive users). |
profile | enum | standard, high, accessible |
Respons
| Status | Arti |
|---|---|
200 | The session plan |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
429 | Rate or licence limit reached |
503 | Busy, retry shortly |
Permintaan
curl -s -X POST "http://localhost:8080/v2/liveness/sessions" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/liveness/sessions",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/liveness/sessions", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());State And Signed Result Of A Session
GET/v2/liveness/sessions/{id}
Scope liveness, same tenant as the session. Results stay available for 10 minutes after creation.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Session id. |
Respons
| Status | Arti |
|---|---|
200 | The state; when done, the signed result |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X GET "http://localhost:8080/v2/liveness/sessions/{id}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/liveness/sessions/{id}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/liveness/sessions/{id}", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Active Liveness Stream (WebSocket)
GET/v2/liveness/sessions/{id}/stream
WebSocket, protocol version 1. No API key: the session id is the credential, usable once within 60 s of creation (a second connect is 409, an unknown or expired id 404). Client messages: binary [u32 seq BE][f64 capture time ms BE][JPEG], or text {"type":"frame","seq","ts","image":"<base64 JPEG>"}, or {"type":"cancel"}. Frames: un-mirrored, at least 640x480, 10-15 fps (15 during the colour flash), seq increasing, at most 2 MiB. Server messages (text JSON): ready {session_id, profile, steps, protocol_version}; progress {phase: align|neutral|step|flash, step, of, hint}; instruction {step, of, challenge, text, deadline_ms}; flash {index, of, name, colour, rgb, duration_ms} (fill the screen with this colour until the next flash or flash_end); flash_end; step_result {step, of, challenge, passed, reason, ms}; finally result (an ActiveLivenessEnvelope with type: result) and the socket closes. Fails closed: a protocol error, disconnect or timeout gives passed: false with a reason.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Session id. |
Respons
| Status | Arti |
|---|---|
101 | Switching to the WebSocket protocol |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
409 | Conflict |
Permintaan
curl -s -X GET "http://localhost:8080/v2/liveness/sessions/{id}/stream"import requests
r = requests.get(
"http://localhost:8080/v2/liveness/sessions/{id}/stream",
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/liveness/sessions/{id}/stream", {
method: "GET",
});
console.log(r.status, await r.text());Cameras, Video And Events
Add-on: RTSP cameras, the webcam stream, recognition events, webhooks and live video.
List Cameras
GET/v2/cameras
Scope streams.
Respons
| Status | Arti |
|---|---|
200 | The tenant's cameras |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/cameras" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/cameras",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Add An RTSP Camera
POST/v2/cameras
Scope streams, licence module streaming. An enabled camera starts at once; when it cannot, running is false and note says why. 429 LICENSE_LIMIT at the licence's camera limit.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
cooldown_s | number | Seconds between events for the same identity on this camera. |
enabled | boolean | |
fps | number | Analysed frames per second (default 10). |
min_face_px | integer | Smallest face analysed (default 32). |
namewajib | string | |
urlwajib | string | rtsp:// or rtsps:// URL, credentials included (stored sealed, never returned). |
zones | any[] | Detection zones (polygons in normalised frame coordinates 0-1, a detection is kept when its centre is inside); default none. |
Respons
| Status | Arti |
|---|---|
201 | The camera |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
429 | Rate or licence limit reached |
Permintaan
curl -s -X POST "http://localhost:8080/v2/cameras" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/cameras",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Get A Camera
GET/v2/cameras/{id}
Scope streams.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Camera id. |
Respons
| Status | Arti |
|---|---|
200 | The camera |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X GET "http://localhost:8080/v2/cameras/{id}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/cameras/{id}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras/{id}", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Delete A Camera
DELETE/v2/cameras/{id}
Scope streams.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Camera id. |
Respons
| Status | Arti |
|---|---|
204 | Deleted (its stream stopped) |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X DELETE "http://localhost:8080/v2/cameras/{id}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.delete(
"http://localhost:8080/v2/cameras/{id}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras/{id}", {
method: "DELETE",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Latest Camera Frame
GET/v2/cameras/{id}/snapshot
Scope streams. Only when the tenant policy has camera_snapshots on (403 FEATURE_DISABLED otherwise); 404 when the camera is not running or has no frame yet. With purpose=zones (scope admin), one frame for the zone editor is allowed even with pictures off; that is written to the audit log (camera.frame_for_zones).
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Camera id. |
purpose query | enum | zones: a frame for the zone editor. zones |
Respons
| Status | Arti |
|---|---|
200 | The latest analysed frame, at most 640 px wide, without overlays |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X GET "http://localhost:8080/v2/cameras/{id}/snapshot" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/cameras/{id}/snapshot",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras/{id}/snapshot", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Status Of A Camera Or Webcam Stream
GET/v2/cameras/{id}/status
Scope streams. id: a camera id or a webcam stream's camera_id. A running stream answers its metrics; a stored camera that is not running answers its state.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Camera id. |
Respons
| Status | Arti |
|---|---|
200 | Metrics of the stream |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X GET "http://localhost:8080/v2/cameras/{id}/status" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/cameras/{id}/status",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras/{id}/status", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Live Boxes Of A Camera (WebSocket)
GET/v2/cameras/{id}/tracks
WebSocket. Scope streams, licence module streaming. Live box data of one camera, for drawing boxes and names over its video: one {"type":"tracks", camera_id, seq, ts, width, height, tracks:[{track_id, box:[x1,y1,x2,y2], state: tracking|identified|unknown, uid?, name? (display name), similarity?, groups?:[{name, level}], alert?: alert|notice, attributes?: {gender?, expression?}}]} per analysed frame (at most the camera's fps). box is normalised to the frame (0..1); ts is the frame's capture time (Unix ms); width / height are the analysed frame's size. While the camera is not running the server sends {"type":"idle", camera_id} once and keeps the socket open; boxes resume when the camera starts again. Client messages are ignored. 404 for an unknown camera.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
key query | string | API key, for browsers that cannot set headers on a WebSocket (percent-encoded). Ignored when an Authorization, x-api-key or x-key-secret header is present. |
idwajib path | string | Camera id. |
Respons
| Status | Arti |
|---|---|
101 | Switching to the WebSocket protocol |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X GET "http://localhost:8080/v2/cameras/{id}/tracks" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/cameras/{id}/tracks",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras/{id}/tracks", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Watch A Camera's Live Video (WebRTC, WHEP)
POST/v2/cameras/{id}/whep
WHEP (RFC 9725): send the browser's WebRTC offer (receive-only video) as application/sdp. Scope streams, licence module streaming; the tenant must allow camera pictures (camera_snapshots). The camera's video starts on the first viewer (720p, at most 15 fps, face boxes and zone outlines drawn in, no names) and stops 30 s after the last viewer left; recognition is not affected. Names and scores come from /v2/cameras/{id}/tracks and are drawn by the client. While the camera sends no picture the video shows a "Camera disconnected" frame. 409 when the camera is not running.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Camera id. |
Respons
| Status | Arti |
|---|---|
201 | The SDP answer. Media then flows from the media server over UDP (port 8000 of the server's public address). Location names this viewer's session (end it with DELETE). |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
409 | Conflict |
503 | Busy, retry shortly |
Permintaan
curl -s -X POST "http://localhost:8080/v2/cameras/{id}/whep" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/cameras/{id}/whep",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras/{id}/whep", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());End A Live Video Session
DELETE/v2/cameras/{id}/whep/{session}
Scope streams. Ends the viewer session named by the Location of the WHEP answer (closing the peer connection has the same effect after a few seconds).
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Camera id. |
sessionwajib path | string | The session id from Location. |
Respons
| Status | Arti |
|---|---|
200 | Ended |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X DELETE "http://localhost:8080/v2/cameras/{id}/whep/{session}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.delete(
"http://localhost:8080/v2/cameras/{id}/whep/{session}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras/{id}/whep/{session}", {
method: "DELETE",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Replace A Camera's Zones
PUT/v2/cameras/{id}/zones
Scopes streams and admin. Zones are regions of interest: polygons in normalised frame coordinates (0-1); a face is processed when the centre of its box is inside any zone; no zones = the whole frame. At most 16 zones of 3-64 points. Audited (camera.zones, zone counts before and after).
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Camera id. |
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
zoneswajib | object[] | |
zones[].name | string | |
zones[].pointswajib | number[][] |
Respons
| Status | Arti |
|---|---|
200 | The camera, with its new zones (its stream restarted) |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X PUT "http://localhost:8080/v2/cameras/{id}/zones" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.put(
"http://localhost:8080/v2/cameras/{id}/zones",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/cameras/{id}/zones", {
method: "PUT",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Event Stream (WebSocket)
GET/v2/events
WebSocket. Scope streams, licence module streaming. The server sends {"type":"event","event":StreamEvent} for each recognition event of the tenant (CloudEvents 1.0 structured JSON, see the StreamEvent schema) and {"type":"lagged","dropped":n} when this client fell behind and lost events. Client messages are ignored.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
key query | string | API key, for browsers that cannot set headers on a WebSocket (percent-encoded). Ignored when an Authorization, x-api-key or x-key-secret header is present. |
camera_id query | string | Only this camera's events. |
Respons
| Status | Arti |
|---|---|
101 | Switching to the WebSocket protocol |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/events" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/events",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/events", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Stored Recognition Events
GET/v2/events/history
Scope streams. Every published event is stored and kept for the tenant's event_retention_days.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
camera_id query | string | Only this camera. |
uid query | string | Only this identity. |
before_id query | integer | Paging: next_before_id of the previous page. |
limit query | integer | Page size. |
since query | integer | At or after this time (Unix ms). |
until query | integer | Before this time (Unix ms). |
Respons
| Status | Arti |
|---|---|
200 | Events, newest first |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/events/history" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/events/history",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/events/history", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Running Streams
GET/v2/streams
Scope streams.
Respons
| Status | Arti |
|---|---|
200 | Every running stream of the tenant |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/streams" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/streams",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/streams", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Webcam Stream (WebSocket)
GET/v2/webcam
WebSocket. Scope streams, licence module streaming; the Demo edition allows one stream. The client sends binary messages, each a JPEG frame (at most 4 MiB), optionally prefixed by an 8-byte little-endian Unix-ms capture timestamp; and text messages {"type":"status"} (answered with {"type":"status","status":StreamStatus}) or {"type":"end"} (finish: open tracks are decided, their events delivered, then {"type":"ended","status":StreamStatus} and the socket closes). The server sends {"type":"ready","camera_id"} first, then one {"type":"overlay", seq, ts, width, height, latency_ms, tracks:[{track_id, bbox:[x1,y1,x2,y2], box:[x1,y1,x2,y2] (0..1), face?, state: tracking|identified|unknown, uid?, similarity?, groups?, alert?}]} per analysed frame, {"type":"event","event":StreamEvent} for this camera's events and {"type":"error", code, detail} for a refused frame. 409 when a stream with this camera id is running, 429 at the stream limit.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
key query | string | API key, for browsers that cannot set headers on a WebSocket (percent-encoded). Ignored when an Authorization, x-api-key or x-key-secret header is present. |
camera_id query | string | Camera id used in events and status: 1-64 characters of A-Z a-z 0-9 - _ . (default webcam). |
fps query | number | Analysed frames per second (1-15, default 10). |
Respons
| Status | Arti |
|---|---|
101 | Switching to the WebSocket protocol |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
409 | Conflict |
429 | Rate or licence limit reached |
Permintaan
curl -s -X GET "http://localhost:8080/v2/webcam" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/webcam",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/webcam", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());List Webhooks
GET/v2/webhooks
Scope admin.
Respons
| Status | Arti |
|---|---|
200 | The tenant's webhooks |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/webhooks" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/webhooks",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/webhooks", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Add A Webhook
POST/v2/webhooks
Scope admin, licence module streaming. Each event is POSTed as application/cloudevents+json (a StreamEvent) with headers x-nf-signature: sha256=<hex HMAC-SHA256(secret, body)>, x-nf-event-id and x-nf-webhook-id; failed deliveries are retried with exponential backoff.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
events | enum[] | Event types to deliver; empty: all. |
secret | string | Signing secret; generated when absent. |
urlwajib | string | http:// or https:// URL (at most 2048 characters). |
Respons
| Status | Arti |
|---|---|
201 | The webhook |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X POST "http://localhost:8080/v2/webhooks" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/webhooks",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/webhooks", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Delete A Webhook
DELETE/v2/webhooks/{id}
Scope admin.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Webhook id. |
Respons
| Status | Arti |
|---|---|
204 | Deleted |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X DELETE "http://localhost:8080/v2/webhooks/{id}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.delete(
"http://localhost:8080/v2/webhooks/{id}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/webhooks/{id}", {
method: "DELETE",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Administration
API keys, the tenant policy, request log, audit log and consent (scope admin).
Read The Audit Log
GET/v2/tenant/audit
Scope admin.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
after_seq query | integer | Entries after this sequence number (paging). |
limit query | integer | Page size (at most 1000). |
actor query | string | Only entries whose actor contains this text (an e-mail finds user:<e-mail>). |
action query | string | Only entries whose action starts with this text (identity. finds every identity change). |
since query | integer | Only entries at or after this time (Unix ms). |
until query | integer | Only entries before this time (Unix ms). |
Respons
| Status | Arti |
|---|---|
200 | Entries in sequence order |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/tenant/audit" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/tenant/audit",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/audit", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Export The Audit Log
GET/v2/tenant/audit/export
Scope admin. At most 200,000 rows.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
format query | enum | Only csv. csv |
actor query | string | Only entries of this actor. |
action query | string | Only entries of this action. |
since query | integer | At or after this time (Unix ms). |
until query | integer | Before this time (Unix ms). |
Respons
| Status | Arti |
|---|---|
200 | CSV: seq, time, actor, action, target, detail (JSON). No biometric data. |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/tenant/audit/export" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/tenant/audit/export",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/audit/export", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Check The Audit Hash Chain
GET/v2/tenant/audit/verify
Scope admin. Walks the instance's whole chain.
Respons
| Status | Arti |
|---|---|
200 | Result of the check |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/tenant/audit/verify" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/tenant/audit/verify",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/audit/verify", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Record Privacy-notice Consent
POST/v2/tenant/consent
Scope admin. From the dashboard this needs an elevated session (POST /v2/auth/elevate); 403 otherwise. Stored in the audit log and in the policy's consent; needed before store_crops can be on.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
accepted_bywajib | string | |
notice_versionwajib | string |
Respons
| Status | Arti |
|---|---|
200 | Recorded |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X POST "http://localhost:8080/v2/tenant/consent" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/tenant/consent",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/consent", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Withdraw Consent
DELETE/v2/tenant/consent
Scope admin. From the dashboard this needs an elevated session (POST /v2/auth/elevate); 403 otherwise.
Respons
| Status | Arti |
|---|---|
200 | Withdrawn; store_crops is off |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X DELETE "http://localhost:8080/v2/tenant/consent" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.delete(
"http://localhost:8080/v2/tenant/consent",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/consent", {
method: "DELETE",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());List API Keys
GET/v2/tenant/keys
Scope admin.
Respons
| Status | Arti |
|---|---|
200 | Every key of the tenant, revoked ones included |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/tenant/keys" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/tenant/keys",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/keys", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Create An API Key
POST/v2/tenant/keys
Scope admin. From the dashboard this also needs an elevated session; 403 otherwise.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
expires_at | integer | Expiry (Unix ms). |
name | string | Label for the key. |
scopeswajib | enum[] |
Respons
| Status | Arti |
|---|---|
200 | The new key (secret shown once) |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X POST "http://localhost:8080/v2/tenant/keys" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.post(
"http://localhost:8080/v2/tenant/keys",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/keys", {
method: "POST",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Revoke An API Key
DELETE/v2/tenant/keys/{id}
Scope admin. From the dashboard this also needs an elevated session; 403 otherwise.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Key id. |
Respons
| Status | Arti |
|---|---|
200 | Revoked |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X DELETE "http://localhost:8080/v2/tenant/keys/{id}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.delete(
"http://localhost:8080/v2/tenant/keys/{id}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/keys/{id}", {
method: "DELETE",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Get The Tenant Policy
GET/v2/tenant/policy
Scope admin.
Respons
| Status | Arti |
|---|---|
200 | The policy |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/tenant/policy" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/tenant/policy",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/policy", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Change The Tenant Policy
PUT/v2/tenant/policy
Scope admin. Send only the members to change; members left out keep their current value (objects such as attributes are merged member by member; PATCH does the same). verify_far must be in [1e-7, 0.1], identify_fpir in [1e-4, 0.1], verify_profile access or access-relaxed, event_retention_days and request_retention_days in [1, 3650], audit_retention_days in [30, 3650]. store_crops can only be on while a consent record exists; consent is ignored here (POST /v2/tenant/consent). From the dashboard, changing retention, store_crops or camera_snapshots needs an elevated session.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
attributes | object | |
attributes.expression | boolean | |
attributes.gender | boolean | |
audit_retention_days | integer | The audit chain is shared by the instance: the longest value of all tenants applies. |
camera_snapshots | boolean | Allow GET /v2/cameras/{id}/snapshot. |
consent | one of | Read-only here; see POST /v2/tenant/consent. |
enrol | enum | Enrolment strictness. cctv: for CCTV snapshots, lower accuracy, prefer photos. standard, passport, cctv |
event_retention_days | integer | |
identify_fpir | number | |
request_retention_days | integer | Request-log rows are deleted after this many days. |
store_crops | boolean | Keep aligned enrolment crops; only while a consent record exists. |
verify_far | number | |
verify_profile | enum | access, access-relaxed |
Respons
| Status | Arti |
|---|---|
200 | The stored policy |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X PUT "http://localhost:8080/v2/tenant/policy" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.put(
"http://localhost:8080/v2/tenant/policy",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/policy", {
method: "PUT",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Change Some Members Of The Tenant Policy
PATCH/v2/tenant/policy
Scope admin. Send only the members to change; members left out keep their current value (objects such as attributes are merged member by member; PATCH does the same). verify_far must be in [1e-7, 0.1], identify_fpir in [1e-4, 0.1], verify_profile access or access-relaxed, event_retention_days and request_retention_days in [1, 3650], audit_retention_days in [30, 3650]. store_crops can only be on while a consent record exists; consent is ignored here (POST /v2/tenant/consent). From the dashboard, changing retention, store_crops or camera_snapshots needs an elevated session.
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
attributes | object | |
attributes.expression | boolean | |
attributes.gender | boolean | |
audit_retention_days | integer | The audit chain is shared by the instance: the longest value of all tenants applies. |
camera_snapshots | boolean | Allow GET /v2/cameras/{id}/snapshot. |
consent | one of | Read-only here; see POST /v2/tenant/consent. |
enrol | enum | Enrolment strictness. cctv: for CCTV snapshots, lower accuracy, prefer photos. standard, passport, cctv |
event_retention_days | integer | |
identify_fpir | number | |
request_retention_days | integer | Request-log rows are deleted after this many days. |
store_crops | boolean | Keep aligned enrolment crops; only while a consent record exists. |
verify_far | number | |
verify_profile | enum | access, access-relaxed |
Respons
| Status | Arti |
|---|---|
200 | The stored policy |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X PATCH "http://localhost:8080/v2/tenant/policy" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.patch(
"http://localhost:8080/v2/tenant/policy",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/policy", {
method: "PATCH",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Read The Request Log
GET/v2/tenant/requests
Scope admin. One row per API call: time, method, path, status, latency, the acting key prefix or user, client IP, the endpoint family and the error code; detail holds the request's shape (field names, photo type and size, settings such as model or far; other fields only as given) and an outcome line without people's IDs. Request and response bodies, images and templates are never recorded. The dashboard's own background status reads are left out. Kept for the tenant's request_retention_days.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
since query | integer | Calls at or after this time (Unix ms). |
until query | integer | Calls before this time (Unix ms). |
method query | string | Only this HTTP method. |
path query | string | Only paths starting with this text. |
status query | integer | Status class: 2 = 2xx, 4 = 4xx, 5 = 5xx. |
actor query | string | Only calls whose actor contains this text. |
family query | enum | Only this endpoint family. compare, verify, identify, detect, liveness, enrol, people, video, admin, service, dashboard |
before_id query | integer | Rows older than this row id (paging). |
limit query | integer | Page size (at most 500). |
Respons
| Status | Arti |
|---|---|
200 | API calls, newest first |
400 | Bad request |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
Permintaan
curl -s -X GET "http://localhost:8080/v2/tenant/requests" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/tenant/requests",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/requests", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Read One Logged Call
GET/v2/tenant/requests/{id}
Scope admin. One row of the request log with its detail.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
idwajib path | string | Row id. |
Respons
| Status | Arti |
|---|---|
200 | The call |
401 | Not authenticated |
403 | Not allowed (scope, read-only account, elevation, edition or licence module) |
404 | Not found |
Permintaan
curl -s -X GET "http://localhost:8080/v2/tenant/requests/{id}" \
-H "Authorization: Bearer <API_KEY>"import requests
r = requests.get(
"http://localhost:8080/v2/tenant/requests/{id}",
headers={"Authorization": "Bearer <API_KEY>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/tenant/requests/{id}", {
method: "GET",
headers: { "Authorization": "Bearer <API_KEY>" },
});
console.log(r.status, await r.text());Service
Version, models and their operating points, licence, this description.
Product, Version And Edition
GET/v2/info
Respons
| Status | Arti |
|---|---|
200 | Service information |
Permintaan
curl -s -X GET "http://localhost:8080/v2/info"import requests
r = requests.get(
"http://localhost:8080/v2/info",
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/info", {
method: "GET",
});
console.log(r.status, await r.text());Licence Status
GET/v2/licence
Also served in activation mode. A development build without a licence check answers {state: development, note}.
Respons
| Status | Arti |
|---|---|
200 | The licence summary |
Permintaan
curl -s -X GET "http://localhost:8080/v2/licence"import requests
r = requests.get(
"http://localhost:8080/v2/licence",
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/licence", {
method: "GET",
});
console.log(r.status, await r.text());Activate Online
POST/v2/licence/activate
Activation mode only (before the licence is active); needs the x-setup-token header printed at start-up. Errors are {status, detail} JSON documents, not problem documents. Activates with the licensing server.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
x-setup-tokenwajib header | string | One-off setup token printed at start-up while the installation is not activated. |
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
keywajib | string | The software licence key. |
Respons
| Status | Arti |
|---|---|
200 | Activated; the server restarts into normal operation |
400 | key missing |
401 | Missing or wrong setup token |
409 | Accepted but the licence does not allow analysis |
502 | The licensing server could not be reached or refused |
Permintaan
curl -s -X POST "http://localhost:8080/v2/licence/activate" \
-H "x-setup-token: <X_SETUP_TOKEN>"import requests
r = requests.post(
"http://localhost:8080/v2/licence/activate",
headers={"x-setup-token": "<X_SETUP_TOKEN>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/licence/activate", {
method: "POST",
headers: { "x-setup-token": "<X_SETUP_TOKEN>" },
});
console.log(r.status, await r.text());Import An Offline Licence File
POST/v2/licence/offline-import
Activation mode only (before the licence is active); needs the x-setup-token header printed at start-up. Errors are {status, detail} JSON documents, not problem documents. The body is the licence file as text.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
x-setup-tokenwajib header | string | One-off setup token printed at start-up while the installation is not activated. |
Respons
| Status | Arti |
|---|---|
200 | Imported and activated |
400 | Invalid licence file |
401 | Missing or wrong setup token |
409 | Accepted but the licence does not allow analysis |
Permintaan
curl -s -X POST "http://localhost:8080/v2/licence/offline-import" \
-H "x-setup-token: <X_SETUP_TOKEN>"import requests
r = requests.post(
"http://localhost:8080/v2/licence/offline-import",
headers={"x-setup-token": "<X_SETUP_TOKEN>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/licence/offline-import", {
method: "POST",
headers: { "x-setup-token": "<X_SETUP_TOKEN>" },
});
console.log(r.status, await r.text());Create An Offline Activation Request
POST/v2/licence/offline-request
Activation mode only (before the licence is active); needs the x-setup-token header printed at start-up. Errors are {status, detail} JSON documents, not problem documents. For machines without access to the licensing server.
Parameter
| Kolom | Tipe | Deskripsi |
|---|---|---|
x-setup-tokenwajib header | string | One-off setup token printed at start-up while the installation is not activated. |
Body
| Kolom | Tipe | Deskripsi |
|---|---|---|
keywajib | string | The software licence key. |
Respons
| Status | Arti |
|---|---|
200 | The offline activation request, to take to the licensing portal |
400 | key missing |
401 | Missing or wrong setup token |
403 | The request could not be created for this key |
Permintaan
curl -s -X POST "http://localhost:8080/v2/licence/offline-request" \
-H "x-setup-token: <X_SETUP_TOKEN>"import requests
r = requests.post(
"http://localhost:8080/v2/licence/offline-request",
headers={"x-setup-token": "<X_SETUP_TOKEN>"},
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/licence/offline-request", {
method: "POST",
headers: { "x-setup-token": "<X_SETUP_TOKEN>" },
});
console.log(r.status, await r.text());Embedders And Calibrated Operating Points
GET/v2/models
Respons
| Status | Arti |
|---|---|
200 | The loaded embedders and their calibration |
Permintaan
curl -s -X GET "http://localhost:8080/v2/models"import requests
r = requests.get(
"http://localhost:8080/v2/models",
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/models", {
method: "GET",
});
console.log(r.status, await r.text());This API Description
GET/v2/openapi.json
Respons
| Status | Arti |
|---|---|
200 | OpenAPI 3.1 document for this edition |
Permintaan
curl -s -X GET "http://localhost:8080/v2/openapi.json"import requests
r = requests.get(
"http://localhost:8080/v2/openapi.json",
)
print(r.status_code, r.text)const r = await fetch("http://localhost:8080/v2/openapi.json", {
method: "GET",
});
console.log(r.status, await r.text());