DokumentasiENarsa.technology

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

KolomTipeDeskripsi
attributesstringComma-separated subset of gender,expression, as for detection.
fpirnumberTarget false positive identification rate (default: tenant policy identify_fpir).
imagewajibstringProbe 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.
kintegerCandidates per face in investigation mode (1-50, default 5).
modeenuminvestigation: return the top-k candidates instead of a decision. investigation
qualityenumProbe quality policy. default, surveillance

Respons

StatusArti
200One result per face
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
413Payload too large
429Rate or licence limit reached
503Busy, 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

KolomTipeDeskripsi
farnumberTarget false accept rate of the access profile (default 1e-4; the nearest stricter calibrated point is used).
image1wajibstringFirst 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.
image2wajibstringSecond 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.
modelstringEmbedder id (GET /v2/models); default: the server's default embedder.
profileenumOperating-point profile. access, access-relaxed
selectenumBy 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

StatusArti
200The comparison
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
413Payload too large
422Refused (image quality or consistency)
429Rate or licence limit reached
503Busy, 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

KolomTipeDeskripsi
farnumberTarget false accept rate (default: tenant policy verify_far).
imagewajibstringProbe 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.
profileenumDefault: tenant policy verify_profile. access, access-relaxed
selectenumBy 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
uidwajibstringThe identity to verify against.

Respons

StatusArti
200The comparison against the identity's best template
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not found
409Conflict
413Payload too large
422Refused (image quality or consistency)
429Rate or licence limit reached
503Busy, 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

StatusArti
200The tenant's groups with member counts
401Not authenticated
403Not 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

KolomTipeDeskripsi
levelenumalert, notice, none
namewajibstring

Respons

StatusArti
200The new group
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
409Conflict

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

KolomTipeDeskripsi
idwajib pathstringGroup id.

Body

KolomTipeDeskripsi
levelenumalert, notice, none
namestring

Respons

StatusArti
200The group
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not found
409Conflict

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

KolomTipeDeskripsi
idwajib pathstringGroup id.

Respons

StatusArti
200Deleted; its members stay enrolled
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
prefix querystringOnly uids starting with this.
after querystringUids after this one (next_after of the previous page).
limit queryintegerPage size (1-1000).

Respons

StatusArti
200A page of identities, by uid
400Bad request
401Not authenticated
403Not 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

KolomTipeDeskripsi
display_namestringOptional display name.
forceboolean | stringtrue: skip the duplicate check (needs the admin scope).
imagewajibstringEnrolment 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.
selectenumAccept an image with several faces when the largest is at least twice the size of the next. largest
uidwajibstringIdentity id: 1-128 characters of A-Z a-z 0-9 . _ : @ -.

Respons

StatusArti
201Enrolled
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
409Conflict
413Payload too large
422Refused (image quality or consistency)
429Rate or licence limit reached
503Busy, 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

KolomTipeDeskripsi
uidwajib pathstringIdentity id.

Respons

StatusArti
200The identity and its templates
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
uidwajib pathstringIdentity id.

Respons

StatusArti
200Deleted with its templates
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
uidwajib pathstringIdentity id.

Body

KolomTipeDeskripsi
groupswajibstring[]Group ids.

Respons

StatusArti
200The person's groups
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
uidwajib pathstringIdentity id.

Body

KolomTipeDeskripsi
imagewajibstringAdditional 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.
selectenumAs for enrolment. largest

Respons

StatusArti
201Templates added
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not found
413Payload too large
422Refused (image quality or consistency)
429Rate or licence limit reached
503Busy, 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

KolomTipeDeskripsi
attributesstringComma-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.
imagewajibstringThe 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.
qualityenumQuality policy the passes / reasons of each face are judged against. default, surveillance, enrol, strict_enrol, passport, cctv

Respons

StatusArti
200The faces found, largest first
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
413Payload too large
429Rate or licence limit reached
503Busy, 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

KolomTipeDeskripsi
imagewajibstringA 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.
profileenumDecision threshold profile. convenience, security

Respons

StatusArti
200The decision
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
413Payload too large
429Rate or licence limit reached
503Busy, 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

StatusArti
200The instance's key
403Not 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

KolomTipeDeskripsi
flash_opt_outbooleanSkip the colour-flash step (e.g. for photosensitive users).
profileenumstandard, high, accessible

Respons

StatusArti
200The session plan
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
429Rate or licence limit reached
503Busy, 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

KolomTipeDeskripsi
idwajib pathstringSession id.

Respons

StatusArti
200The state; when done, the signed result
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
idwajib pathstringSession id.

Respons

StatusArti
101Switching to the WebSocket protocol
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not found
409Conflict

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

StatusArti
200The tenant's cameras
401Not authenticated
403Not 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

KolomTipeDeskripsi
cooldown_snumberSeconds between events for the same identity on this camera.
enabledboolean
fpsnumberAnalysed frames per second (default 10).
min_face_pxintegerSmallest face analysed (default 32).
namewajibstring
urlwajibstringrtsp:// or rtsps:// URL, credentials included (stored sealed, never returned).
zonesany[]Detection zones (polygons in normalised frame coordinates 0-1, a detection is kept when its centre is inside); default none.

Respons

StatusArti
201The camera
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
429Rate 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

KolomTipeDeskripsi
idwajib pathstringCamera id.

Respons

StatusArti
200The camera
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
idwajib pathstringCamera id.

Respons

StatusArti
204Deleted (its stream stopped)
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
idwajib pathstringCamera id.
purpose queryenumzones: a frame for the zone editor. zones

Respons

StatusArti
200The latest analysed frame, at most 640 px wide, without overlays
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
idwajib pathstringCamera id.

Respons

StatusArti
200Metrics of the stream
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
key querystringAPI 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 pathstringCamera id.

Respons

StatusArti
101Switching to the WebSocket protocol
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
idwajib pathstringCamera id.

Respons

StatusArti
201The 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).
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
409Conflict
503Busy, 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

KolomTipeDeskripsi
idwajib pathstringCamera id.
sessionwajib pathstringThe session id from Location.

Respons

StatusArti
200Ended
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
idwajib pathstringCamera id.

Body

KolomTipeDeskripsi
zoneswajibobject[]
zones[].namestring
zones[].pointswajibnumber[][]

Respons

StatusArti
200The camera, with its new zones (its stream restarted)
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
key querystringAPI 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 querystringOnly this camera's events.

Respons

StatusArti
101Switching to the WebSocket protocol
401Not authenticated
403Not 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

KolomTipeDeskripsi
camera_id querystringOnly this camera.
uid querystringOnly this identity.
before_id queryintegerPaging: next_before_id of the previous page.
limit queryintegerPage size.
since queryintegerAt or after this time (Unix ms).
until queryintegerBefore this time (Unix ms).

Respons

StatusArti
200Events, newest first
400Bad request
401Not authenticated
403Not 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

StatusArti
200Every running stream of the tenant
401Not authenticated
403Not 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

KolomTipeDeskripsi
key querystringAPI 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 querystringCamera id used in events and status: 1-64 characters of A-Z a-z 0-9 - _ . (default webcam).
fps querynumberAnalysed frames per second (1-15, default 10).

Respons

StatusArti
101Switching to the WebSocket protocol
400Bad request
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
409Conflict
429Rate 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

StatusArti
200The tenant's webhooks
401Not authenticated
403Not 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

KolomTipeDeskripsi
eventsenum[]Event types to deliver; empty: all.
secretstringSigning secret; generated when absent.
urlwajibstringhttp:// or https:// URL (at most 2048 characters).

Respons

StatusArti
201The webhook
400Bad request
401Not authenticated
403Not 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

KolomTipeDeskripsi
idwajib pathstringWebhook id.

Respons

StatusArti
204Deleted
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

KolomTipeDeskripsi
after_seq queryintegerEntries after this sequence number (paging).
limit queryintegerPage size (at most 1000).
actor querystringOnly entries whose actor contains this text (an e-mail finds user:<e-mail>).
action querystringOnly entries whose action starts with this text (identity. finds every identity change).
since queryintegerOnly entries at or after this time (Unix ms).
until queryintegerOnly entries before this time (Unix ms).

Respons

StatusArti
200Entries in sequence order
400Bad request
401Not authenticated
403Not 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

KolomTipeDeskripsi
format queryenumOnly csv. csv
actor querystringOnly entries of this actor.
action querystringOnly entries of this action.
since queryintegerAt or after this time (Unix ms).
until queryintegerBefore this time (Unix ms).

Respons

StatusArti
200CSV: seq, time, actor, action, target, detail (JSON). No biometric data.
400Bad request
401Not authenticated
403Not 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

StatusArti
200Result of the check
401Not authenticated
403Not 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

KolomTipeDeskripsi
accepted_bywajibstring
notice_versionwajibstring

Respons

StatusArti
200Recorded
400Bad request
401Not authenticated
403Not 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

StatusArti
200Withdrawn; store_crops is off
401Not authenticated
403Not 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

StatusArti
200Every key of the tenant, revoked ones included
401Not authenticated
403Not 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

KolomTipeDeskripsi
expires_atintegerExpiry (Unix ms).
namestringLabel for the key.
scopeswajibenum[]

Respons

StatusArti
200The new key (secret shown once)
400Bad request
401Not authenticated
403Not 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

KolomTipeDeskripsi
idwajib pathstringKey id.

Respons

StatusArti
200Revoked
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

StatusArti
200The policy
401Not authenticated
403Not 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

KolomTipeDeskripsi
attributesobject
attributes.expressionboolean
attributes.genderboolean
audit_retention_daysintegerThe audit chain is shared by the instance: the longest value of all tenants applies.
camera_snapshotsbooleanAllow GET /v2/cameras/{id}/snapshot.
consentone ofRead-only here; see POST /v2/tenant/consent.
enrolenumEnrolment strictness. cctv: for CCTV snapshots, lower accuracy, prefer photos. standard, passport, cctv
event_retention_daysinteger
identify_fpirnumber
request_retention_daysintegerRequest-log rows are deleted after this many days.
store_cropsbooleanKeep aligned enrolment crops; only while a consent record exists.
verify_farnumber
verify_profileenumaccess, access-relaxed

Respons

StatusArti
200The stored policy
400Bad request
401Not authenticated
403Not 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

KolomTipeDeskripsi
attributesobject
attributes.expressionboolean
attributes.genderboolean
audit_retention_daysintegerThe audit chain is shared by the instance: the longest value of all tenants applies.
camera_snapshotsbooleanAllow GET /v2/cameras/{id}/snapshot.
consentone ofRead-only here; see POST /v2/tenant/consent.
enrolenumEnrolment strictness. cctv: for CCTV snapshots, lower accuracy, prefer photos. standard, passport, cctv
event_retention_daysinteger
identify_fpirnumber
request_retention_daysintegerRequest-log rows are deleted after this many days.
store_cropsbooleanKeep aligned enrolment crops; only while a consent record exists.
verify_farnumber
verify_profileenumaccess, access-relaxed

Respons

StatusArti
200The stored policy
400Bad request
401Not authenticated
403Not 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

KolomTipeDeskripsi
since queryintegerCalls at or after this time (Unix ms).
until queryintegerCalls before this time (Unix ms).
method querystringOnly this HTTP method.
path querystringOnly paths starting with this text.
status queryintegerStatus class: 2 = 2xx, 4 = 4xx, 5 = 5xx.
actor querystringOnly calls whose actor contains this text.
family queryenumOnly this endpoint family. compare, verify, identify, detect, liveness, enrol, people, video, admin, service, dashboard
before_id queryintegerRows older than this row id (paging).
limit queryintegerPage size (at most 500).

Respons

StatusArti
200API calls, newest first
400Bad request
401Not authenticated
403Not 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

KolomTipeDeskripsi
idwajib pathstringRow id.

Respons

StatusArti
200The call
401Not authenticated
403Not allowed (scope, read-only account, elevation, edition or licence module)
404Not 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

StatusArti
200Service 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

StatusArti
200The 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

KolomTipeDeskripsi
x-setup-tokenwajib headerstringOne-off setup token printed at start-up while the installation is not activated.

Body

KolomTipeDeskripsi
keywajibstringThe software licence key.

Respons

StatusArti
200Activated; the server restarts into normal operation
400key missing
401Missing or wrong setup token
409Accepted but the licence does not allow analysis
502The 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

KolomTipeDeskripsi
x-setup-tokenwajib headerstringOne-off setup token printed at start-up while the installation is not activated.

Respons

StatusArti
200Imported and activated
400Invalid licence file
401Missing or wrong setup token
409Accepted 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

KolomTipeDeskripsi
x-setup-tokenwajib headerstringOne-off setup token printed at start-up while the installation is not activated.

Body

KolomTipeDeskripsi
keywajibstringThe software licence key.

Respons

StatusArti
200The offline activation request, to take to the licensing portal
400key missing
401Missing or wrong setup token
403The 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

StatusArti
200The 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

StatusArti
200OpenAPI 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());
Apakah halaman ini membantu?