Skip to main content

Subject Management

A subject is the person being verified. The subject record holds the details the verification pipeline scores a submitted document against — name, date of birth, licence number, address — and it is the parent record every submission, message and erasure request hangs off.

Pre-launch

Identiwise has not launched. This page describes the API as it is built today; the endpoints are not open to the public yet, and the hostnames in the examples do not resolve.

Auth: Authorization: Bearer <system token>. Base URL: https://{tenant}.identiwise.com/api/v1 — the tenant is resolved from the Host header, so the hostname you call is the tenancy boundary. Roles: admin and superuser for every route on this page.

Identifiers

Subject ids are UUID strings (UUIDv7, time-ordered), for example 0198c2f4-6b31-7c9a-8d55-2f9a1c7b40e1. Submission, token, preset and workflow ids are the same shape. Only system users and API tokens use integer ids.

external_uid is your own identifier for the person. It is optional and unique within your tenant, and it works as a lookup key in exactly one place: GET /admin/review?externalUid=…. Every other route addresses the subject by its Identiwise UUID — the router matches only the UUID shape — so store the subject_id we return at creation. Without it you cannot mint a token for the subject, read, update or erase them, or fetch a photo.

Submissions carry their own external_uid and verification_id, which you set on the upload that creates them; GET /admin/review?type=…&identifier=external&value=… looks a submission up by that key.


1. List subjects

Endpoint: GET /api/v1/admin/subjects

Query parameters

ParameterTypeDefaultDescription
limitint100Rows to return. Values above 1000 are clamped to 1000.
offsetint0Pagination offset. Negative values are treated as 0.
includeDeletionRequestsboolfalseAttach each subject's erasure requests as deletion_requests.
deletionRequestStatusstringNarrows that attachment to pending or completed.

Rows come back newest first.

curl -s "https://acme.identiwise.com/api/v1/admin/subjects?limit=10&includeDeletionRequests=1" \
-H "Authorization: Bearer $IDENTIWISE_TOKEN"

deletion_requests is present only when you asked for it, and is an empty array for a subject with none.


2. Create a subject

Endpoint: POST /api/v1/admin/subjects

Create the subject first, then mint an ephemeral token for them.

Body

FieldTypeRequiredDescription
first_namestringYes
last_namestringYes
external_uidstringNoYour own id. Must be unused within the tenant.
dob / date_of_birthstringNoYYYY-MM-DD. An empty string is stored as null.
middle_namestringNo
emailstringNo
license_numberstringNoScored against the document's OCR text.
license_statestringNo
license_issue_datestringNoYYYY-MM-DD.
license_expiry_datestringNoYYYY-MM-DD.
addr_street, addr_city, addr_state, addr_zip, addr_countrystringNoaddr_street is scored against the OCR text.
photo_urlstringNo

The fields marked as scored are the ground truth the OCR step compares the document against — the more of them you supply, the more the automated check can decide on its own.

curl -s -X POST "https://acme.identiwise.com/api/v1/admin/subjects" \
-H "Authorization: Bearer $IDENTIWISE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"external_uid": "usr_55920",
"first_name": "Jane",
"last_name": "Doe",
"dob": "1994-03-11",
"license_number": "D1234567",
"addr_street": "123 Main St"
}'
StatusWhen
201Created.
400Body is not valid JSON, or first_name / last_name is missing.
409Another subject in this tenant already uses that external_uid.

3. Get one subject

Endpoint: GET /api/v1/admin/subjects/{id}

ParameterTypeDescription
includestringstatus adds an aggregate verification state block.
curl -s "https://acme.identiwise.com/api/v1/admin/subjects/0198c2f4-6b31-7c9a-8d55-2f9a1c7b40e1?include=status" \
-H "Authorization: Bearer $IDENTIWISE_TOKEN"

license_status and selfie_status are rolled up from that subject's submissions and are one of none, approved, rejected, pending or mixed. A subject id that is malformed, belongs to another tenant, or has been erased answers the same 404 — the API never confirms the existence of another tenant's record.


4. Update a subject

Endpoint: PATCH /api/v1/admin/subjects/{id}

Send only the fields you want to change; the accepted set is the same as create. Both writes (the subject row and its detail row) happen in one transaction, so a partial update is never left behind.

curl -s -X PATCH "https://acme.identiwise.com/api/v1/admin/subjects/0198c2f4-6b31-7c9a-8d55-2f9a1c7b40e1" \
-H "Authorization: Bearer $IDENTIWISE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "license_number": "D7654321", "addr_city": "Austin" }'
StatusWhen
200Applied.
404No such subject in this tenant — including when an erasure removed it while the update was in flight. The write is rolled back, never partly applied.
503Temporary database contention; nothing was written. Retry (Retry-After is set).

Every accepted change writes an audit-trail entry naming the fields that were written — field names only, never values.


5. Request erasure (Article 17)

Endpoint: POST /api/v1/admin/subjects/{id}/request-deletion

Puts the subject in the privacy queue instead of erasing immediately, so a human confirms the erasure. The data subject can lodge the same request themselves with their ephemeral token via POST /subject/request-deletion. The hosted verification UI does not render a control for this today — surface it from your own client if you need to offer one.

Body

FieldTypeRequiredDescription
confirmationboolNoDefaults to true; send false only to abandon the request (that answers 400).
reasonstringNoFree text, maximum 4096 bytes.

The route is idempotent per subject. One pending request per subject: a repeat while a request is still outstanding answers 200 with the id of the request that already exists and writes nothing new. A request that has been completed does not block a later one.

{
"status": "Deletion request logged",
"subject_id": "0198c2f4-6b31-7c9a-8d55-2f9a1c7b40e1",
"request_id": "0198c31a-77b2-7f04-9c10-6d2ab3e1f550",
"reason": "Customer asked for their data to be removed",
"confirmed": true
}
StatusWhen
201Request lodged.
200A request for this subject was already outstanding; its id is returned.
400confirmation sent as false, or reason is not a string / exceeds 4096 bytes.
404No such subject in this tenant.

Pending requests are visible on the subject list with ?includeDeletionRequests=1.


6. Erase a subject

Endpoint: DELETE /api/v1/admin/subjects/{id}

Irreversible

This erases the subject: every stored image is deleted from object storage and every database record for that person is removed in the same transaction. There is no undo.

Optional reason is read from the JSON request body. (The OpenAPI document expresses it as a query parameter because OpenAPI 3.0 forbids a request body on DELETE.)

The erasure runs as one transaction: it collects every object key the subject has ever had — current submissions and superseded attempts alike — deletes those objects, then removes the rows. If object storage cannot be reached, nothing in the database is changed and the call answers 503, so a retry finishes the job rather than leaving records pointing at deleted files. A subject_deletion_requests row is marked completed and the erasure is recorded on the audit trail, so the fact of the deletion survives the data.

curl -s -X DELETE "https://acme.identiwise.com/api/v1/admin/subjects/0198c2f4-6b31-7c9a-8d55-2f9a1c7b40e1" \
-H "Authorization: Bearer $IDENTIWISE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "reason": "Erasure request 2026-09-04" }'
StatusWhen
200Erased. requestId joins this erasure to its privacy-queue row.
404No such subject in this tenant, or a concurrent erasure finished first.
503The erasure was interrupted; no database record was changed and objects already removed stay removed. Retry the request.
500Unexpected failure. The transaction is rolled back.

Individual submissions can be erased the same way through the generic route DELETE /api/v1/{subjects|licenses|selfies}/{id}, which removes one record and its stored image rather than the whole person.


Error semantics on this page

StatusMeaning
400The request is malformed. Fix it and retry.
401Missing, revoked, expired or suspended bearer token.
403Authenticated, but the role does not allow this route, or the tenant's IP allow-list does not include the caller.
404Not found in this tenant. Malformed, foreign and erased ids are indistinguishable by design.
409A uniqueness conflict — today, a duplicate external_uid.
429The tenant's monthly request quota is spent. The body carries the limit.
503Temporary: database contention or an unreachable dependency. Honour Retry-After and retry.

Error bodies are a stable envelope — {"error": "..."} — and never carry exception, SQL or infrastructure detail.