Entrust / Onfido

components/entrust/client.py wraps the Onfido REST API that backs Entrust Identity Verification. Consumed by POST /entrust/sessions and the aftermath task (validate_identity_document (Entrust / Onfido)).

Au10tix / Entrust coexistence. Entrust Identity Verification (Onfido) is the eventual replacement for Au10tix identity-document validation. During the rollout both vendors run under the same validate_identity_document process, so the GSI, dedup logic and admin reporting keep working unchanged. ToolType.get_by_process_name therefore still maps validate_identity_document to AU10TIX; the Entrust code paths reference ToolType.ENTRUST directly. Do not switch that mapping until Au10tix is fully retired.

Configuration

Base URL

ENTRUST_API_URL — e.g. https://api.us.onfido.com/v3.6; it already includes the version segment, so paths are appended verbatim.

Workflow

ENTRUST_WORKFLOW_ID — the Onfido Studio workflow every run is created against.

Auth

Authorization: Token token={ENTRUST_TOKEN} (Secrets Manager).

Webhook secret

ENTRUST_WEBHOOK_TOKEN — used by the webhook handler, not by this client.

Session HTTP behaviour

The client builds a requests.Session with a retrying adapter:

retry = HTTPRetry(
    total=5,
    backoff_factor=0.5,
    status_forcelist=(429, 500, 502, 503, 504),
    respect_retry_after_header=True,
    raise_on_status=False,
)

Only idempotent methods are retried (urllib3’s default allowed set excludes POST), so creating an applicant or a workflow run is never silently duplicated. respect_retry_after_header honours Onfido’s 429 backoff, and raise_on_status=False lets the client’s own raise_for_status() surface the final error unchanged. This matters because reconciling one completed run fans out into many GETs — one per workflow step and one per downloaded file.

Methods

Session creation

Method Call

create_applicant(first_name, last_name)

POST /applicants/ → the applicant object (at least id).

create_workflow_run(applicant_id, custom_data=None)

POST /workflow_runs with workflow_id = ENTRUST_WORKFLOW_ID; custom_data carries {"supplier_key": …} so a run can be correlated back. Returns {id, sdk_token, link: {url, expires_at}, applicant_id, …}.

Reading a run

Method Call

get_workflow_run(workflow_run_id)

GET /workflow_runs/{id} — the authoritative status (approved, declined, review, abandoned, error) and output. The webhook body carries only a minimal object, so this re-read is the source of truth.

list_workflow_run_tasks(id)

GET /workflow_runs/{id}/tasks — step summaries without input/output. Tolerates both a bare array and a {"tasks": […]} envelope.

get_workflow_run_task(id, task_id)

GET /workflow_runs/{id}/tasks/{task_id} — one step with its input and output.

get_workflow_run_tasks(id)

Convenience: list, then hydrate every step (skipping entries without an id). This is what makes the S3 archive contain the full decision trail — including the document check’s output.properties.date_of_expiry.

Captured artifacts

Method Call

list_documents(applicant_id) / download_document(id)

GET /documents?applicant_id=… / GET /documents/{id}/download (raw bytes).

list_live_photos(applicant_id) / download_live_photo(id)

The selfie.

list_live_videos(applicant_id) / download_live_video(id)

Liveness video.

list_motion_captures(applicant_id) / download_motion_capture(id)

Onfido’s alternative liveness modality — a run produces one or the other depending on the SDK flow.

get_applicant_files(applicant_id)

Downloads all documents and live photos as `EntrustFile`s, ready for multipart re-upload.

get_applicant_liveness_files(applicant_id)

Same for live videos and motion captures, kept separate so the aftermath task can upload liveness media in its own request after the documents.

EntrustFile

@dataclass
class EntrustFile:
    filename: str
    content_type: str
    content: bytes
    side: Optional[str] = None            # front / back — documents only
    document_type: Optional[str] = None   # Onfido type, e.g. driving_licence
    issuing_country: Optional[str] = None # ISO country, e.g. LKA
    kind: str = KIND_DOCUMENT             # document | live_photo | live_video | motion_capture

kind lets callers decide which per-file metadata applies (an expiry attaches to documents and liveness media but not to the selfie). content_type is mapped from Onfido’s file_type (jpg/jpeg → image/jpeg, png, pdf, mov → video/quicktime, mp4, webm), defaulting to application/octet-stream. A missing file_name becomes {kind}_{id}.{file_type}.

Error handling

Every call ends in response.raise_for_status(), so non-2xx raises requests.HTTPError. Callers translate that:

  • POST /entrust/sessions passes an Onfido 4xx through with the parsed upstream body under entrust_error, and maps 5xx or connection errors to 502.

  • The aftermath task lets download failures propagate so the SWF activity fails and retries; step hydration is the one best-effort exception (the run summary is archived without tasks).

Tests

development/unit_tests/components_/entrust/test_client.py.

The full vendor contract is available as an attachment: Onfido Public API v3.6 spec.