Skip to content

Api reference

components.id_verification.public.business_logic

actions

id_verification

cancel_and_recreate_id_verification
cancel_and_recreate_id_verification(
    request_id,
    cancelled_by_user_id,
    comment,
    *,
    commit=True
)

To be used from Marmot. Most expected use case: initial ID check was failed (likely abandoned).

:raises ValueError: if current status is not failed It is likely safe to cancel and restart other checks as well, but there are no meaningful cases for that so far.

Source code in components/id_verification/public/business_logic/actions/id_verification.py
def cancel_and_recreate_id_verification(
    request_id: uuid.UUID,
    cancelled_by_user_id: int,
    comment: str,
    *,
    commit: bool = True,
) -> IDVerificationRequest:
    """
    To be used from Marmot.
    Most expected use case: initial ID check was failed (likely abandoned).

    :raises ValueError: if current status is not failed
        It is likely safe to cancel and restart other checks as well, but there are
        no meaningful cases for that so far.
    """
    request = get_or_raise_missing_resource(IDVerificationRequestSQLA, request_id)
    if request.status != IDVerificationRequestStatus.failed:
        raise ValueError(
            f"Only failed ID checks can be recreated manually, while `{request.id}` is `{request.status}`"
        )
    if not comment:
        raise ValueError("Comment must be provided for manual restarts")

    cancel_id_verification_request(
        id_verification_request=request,
        comment=comment,
        cancelled_by_user_id=cancelled_by_user_id,
        commit=False,
    )
    new_request = recreate_id_verification_for_cancelled(
        request_id=request.id,
        trigger=IDVerificationRestartTrigger.manual_restart_from_marmot,
        comment=comment,
        commit=False,
    )
    send_recreated_request_notification(
        email_address=new_request.params.email,
        first_name=new_request.params.first_name,
        workflow_run_url=new_request.onfido_workflow_run.direct_url,
        recreated_because_abandoned=mandatory(
            new_request.attempt_context.restart_context
        ).previous_attempt_was_abandoned,
    )
    if commit:
        current_session.commit()
    return new_request
cancel_id_verification_request_by_workflow_run
cancel_id_verification_request_by_workflow_run(
    workflow_run_id, comment, commit=True
)

Cancels an ID verification request by its workflow run ID. A reason must be passed to explain the cancellation.

Note: This function does not commit directly, it's left up to the calling code to do it, or not.

Source code in components/id_verification/public/business_logic/actions/id_verification.py
def cancel_id_verification_request_by_workflow_run(
    workflow_run_id: uuid.UUID,
    comment: str,
    commit: bool = True,
) -> None:
    """
    Cancels an ID verification request by its workflow run ID.
    A reason must be passed to explain the cancellation.

    Note: This function does not commit directly, it's left up to the calling code to do it, or not.
    """
    workflow_run = get_or_raise_missing_resource(OnfidoWorkflowRunSQLA, workflow_run_id)
    id_verification_request = workflow_run.id_verification_request
    cancel_workflow_run(onfido_workflow_run_id=workflow_run_id, commit=False)
    cancel_id_verification_request(
        id_verification_request, comment=comment, commit=False
    )
    if commit:
        current_session.commit()
create_id_verification_request
create_id_verification_request(
    user_id, company_id, reason, user_info, *, commit=True
)

Creates an ID verification request for a user or a company.

Source code in components/id_verification/public/business_logic/actions/id_verification.py
def create_id_verification_request(
    user_id: int,
    company_id: int | None,
    reason: IDVerificationReason,
    user_info: IDVerificationRequestUserInfo,
    *,
    commit: bool = True,
) -> IDVerificationRequest:
    """
    Creates an ID verification request for a user or a company.
    """
    request = prepare_request_and_create_onfido_workflow_run(
        user_id=user_id,
        company_id=company_id,
        reason=reason,
        user_info=user_info,
    )
    current_session.add(request)

    if commit:
        current_session.commit()

    return id_verification_to_entity(request)
update_id_verification_request
update_id_verification_request(
    company_id, id_verification, user_info, commit=True
)

Updates an existing ID verification request. This function is a placeholder for future updates.

Source code in components/id_verification/public/business_logic/actions/id_verification.py
def update_id_verification_request(
    company_id: int | None,
    id_verification: IDVerificationRequest,
    user_info: IDVerificationRequestUserInfo,
    commit: bool = True,
) -> IDVerificationRequest:
    """
    Updates an existing ID verification request.
    This function is a placeholder for future updates.
    """
    id_verification, updated = update_id_verification_params_if_needed(
        id_verification, user_info
    )
    # User already has an ID check, but we have no record of it affecting the company.
    if company_id and company_id not in id_verification.affected_company_ids:
        id_verification.affected_company_ids.append(company_id)
        current_session.add(
            RequestAffectsCompanySQLA(
                id_verification_request_id=id_verification.id,
                company_id=company_id,
            )
        )
        updated = True
    if commit and updated:
        current_session.commit()
    return id_verification

notifications

send_id_verification_reminder_for_request
send_id_verification_reminder_for_request(
    request, dry_run=False
)

Sends an ID verification reminder email for the given request.

Source code in components/id_verification/public/business_logic/actions/notifications.py
@retry(
    stop=stop_after_attempt(3),
    retry=retry_if_exception(_is_rate_limit_error),
    wait=wait_fixed(timedelta(seconds=60)),
    reraise=True,
    before_sleep=before_sleep_log(current_logger, logging.WARNING),  # type: ignore[arg-type]
)
def send_id_verification_reminder_for_request(
    request: IDVerificationRequest, dry_run: bool = False
) -> None:
    """
    Sends an ID verification reminder email for the given request.
    """
    from components.id_verification.internal.business_logic.actions.notification import (
        send_id_verification_reminder_for_request as _send_id_verification_reminder_for_request,
    )

    _send_id_verification_reminder_for_request(request, dry_run=dry_run)

queries

id_verification

find_uncompleted_id_verification_requests
find_uncompleted_id_verification_requests(
    max_age_in_days=18,
)

Returns a list of uncompleted ID verification requests that are older than the given age.

Source code in components/id_verification/public/business_logic/queries/id_verification.py
def find_uncompleted_id_verification_requests(
    max_age_in_days: int = 18,
) -> list[IDVerificationRequest]:
    """
    Returns a list of uncompleted ID verification requests that are older than the given age.
    """
    from components.id_verification.internal.business_logic.queries.request import (
        get_uncompleted_id_verification_requests,
    )

    return get_uncompleted_id_verification_requests(max_age_in_days=max_age_in_days)
get_id_verification_request
get_id_verification_request(request_id)

Returns the ID verification request with the given ID.

Source code in components/id_verification/public/business_logic/queries/id_verification.py
def get_id_verification_request(
    request_id: UUID,
) -> IDVerificationRequest | None:
    """
    Returns the ID verification request with the given ID.
    """
    request = current_session.get(IDVerificationRequestSQLA, request_id)
    if not request:
        return None
    return id_verification_to_entity(request)
get_last_active_id_verification_request_for_company
get_last_active_id_verification_request_for_company(
    company_id,
)

Returns the last active ID verification request that affects the given company. The company can have multiple associated requests (e.g. different users started onboarding), the requests' priority for us is: 1. passed (whenever it happened, for now let's be permissive) 2. most recent otherwise (created, started or failed, it doesn't matter)

Source code in components/id_verification/public/business_logic/queries/id_verification.py
def get_last_active_id_verification_request_for_company(
    company_id: int,
) -> IDVerificationRequest | None:
    """
    Returns the last active ID verification request that affects the given company.
    The company can have multiple associated requests (e.g. different users started onboarding),
     the requests' priority for us is:
    1. passed (whenever it happened, for now let's be permissive)
    2. most recent otherwise (created, started or failed, it doesn't matter)
    """
    records = (
        current_session.query(RequestAffectsCompanySQLA)  # noqa: ALN085
        .join(IDVerificationRequestSQLA)
        .filter(
            RequestAffectsCompanySQLA.company_id == company_id,
            IDVerificationRequestSQLA.status != IDVerificationRequestStatus.cancelled,
        )
        .order_by(IDVerificationRequestSQLA.created_at.desc())
        .all()
    )

    if not records:
        return None

    most_recent = None
    for record in records:
        if record.id_verification_request.status == IDVerificationRequestStatus.passed:
            return id_verification_to_entity(record.id_verification_request)
        most_recent = most_recent or record.id_verification_request

    return id_verification_to_entity(mandatory(most_recent))
get_last_active_id_verification_request_for_user
get_last_active_id_verification_request_for_user(
    user_id, reason=None, session=current_session
)

Returns the last active ID verification request for the given user.

Source code in components/id_verification/public/business_logic/queries/id_verification.py
def get_last_active_id_verification_request_for_user(
    user_id: int,
    reason: IDVerificationReason | None = None,
    session: Session = current_session,
) -> IDVerificationRequest | None:
    """
    Returns the last active ID verification request for the given user.
    """
    requests = (
        session.query(IDVerificationRequestSQLA)  # noqa: ALN085
        .filter(
            IDVerificationRequestSQLA.user_id == user_id,
            IDVerificationRequestSQLA.status != IDVerificationRequestStatus.cancelled,
            IDVerificationRequestSQLA.reason == reason if reason else true(),
        )
        .order_by(IDVerificationRequestSQLA.created_at.desc())
        .all()
    )
    if not requests:
        return None

    if len(requests) > 1:
        raise ValueError(
            f"Multiple active IDVerificationRequests for user `{user_id}`"
            f" (IDs: {', '.join(str(r.id) for r in requests)})"
        )

    return id_verification_to_entity(requests[0])
get_or_request_id_verification
get_or_request_id_verification(
    user_id, company_id, reason, user_info, *, commit=True
)

This method is an entry point into the ID verification flow. If there is a pending ID verification request for the user it will be returned, otherwise a new one will be created. We want to maintain an invariant - no more than one active ID verification request per user. For that reason, we lock the user for ID verification before creating a new request.

we create ID checks for users, not companies.

Company ID provides helpful contextual information, but we don't rely on it much inside this module. It makes sense if you remember that the primary use case is onboarding. However, the state of ID check might impact that of the company(-ies). Imagine the following events: - user_1 signs up onboarding company_1 - Request_1 is created for the ID check new employee policies are blocked - user_1 signs up onboarding company_2 - user is still prompted to complete Request_1 new employee policies should be blocked, but we need to be able to map company_2 to Request_1 - Request_1 is passed: Policies of employees of both company_1 and company_2 should be unblocked. We need to map Request_1 to [company_1, company_2] In all these cases, we shouldn't rely on knowing that user_1 started the required check. Let's store the relevant mapping between ID checks and companies in the DB. For that purpose, we use IDVerificationRequestAffectsCompany.

:param user_id: user_id corresponds to who we're asking to pass the ID verification Can be admin of the newly created company or newly signed TNS :param company_id: company_id corresponds to the company during creating which the user (admin) is asked to pass the ID verification :param reason: IDVerificationReason.onboarding_tns or IDVerificationReason.onboarding_company :param user_info: information necessary to perform the ID check :param commit: whether to commit the session after creating the request

Source code in components/id_verification/public/business_logic/queries/id_verification.py
def get_or_request_id_verification(
    user_id: int,
    company_id: int | None,
    reason: IDVerificationReason,
    user_info: IDVerificationRequestUserInfo,
    *,
    commit: bool = True,
) -> IDVerificationRequest:
    """
    This method is an entry point into the ID verification flow.
    If there is a pending ID verification request for the user it will be returned,
    otherwise a new one will be created.
    We want to maintain an invariant - no more than one active ID verification request per user.
    For that reason, we lock the user for ID verification before creating a new request.

    NB: we create ID checks for *users*, not companies.
     Company ID provides helpful contextual information, but we don't rely on it much
     inside this module.
     It makes sense if you remember that the primary use case is onboarding.
     However, the state of ID check might impact that of the company(-ies).
     Imagine the following events:
     - user_1 signs up onboarding company_1 - Request_1 is created for the ID check
        new employee policies are blocked
     - user_1 signs up onboarding company_2 - user is still prompted to complete Request_1
        new employee policies should be blocked, but we need to be able to map
        `company_2` to `Request_1`
     - Request_1 is passed:
        Policies of employees of both `company_1` and `company_2` should be unblocked.
        We need to map `Request_1` to [`company_1`, `company_2`]
     In all these cases, we shouldn't rely on knowing that `user_1` started the required check.
     Let's store the relevant mapping between ID checks and companies in the DB.
     For that purpose, we use `IDVerificationRequestAffectsCompany`.

    :param user_id: user_id corresponds to who we're asking to pass the ID verification
    Can be admin of the newly created company or newly signed TNS
    :param company_id: company_id corresponds to the company during creating which the user (admin)
    is asked to pass the ID verification
    :param reason: `IDVerificationReason.onboarding_tns` or `IDVerificationReason.onboarding_company`
    :param user_info: information necessary to perform the ID check
    :param commit: whether to commit the session after creating the request
    """
    if company_id is None and reason == IDVerificationReason.onboarding_company:
        raise ValueError(
            "company_id must be provided when verifying ID for a company admin"
        )

    lock_user_for_id_check(user_id)
    if id_verification := get_last_active_id_verification_request_for_user(user_id):
        return update_id_verification_request(
            id_verification=id_verification,
            company_id=company_id,
            user_info=user_info,
            commit=commit,
        )

    return create_id_verification_request(
        user_id=user_id,
        company_id=company_id,
        reason=reason,
        user_info=user_info,
        commit=commit,
    )

components.id_verification.public.entities

id_verification_request

IDVerificationCancellationContext dataclass

IDVerificationCancellationContext(
    cancelled_by_user_id, comment
)

Bases: DataClassJsonMixin

Context for cancelling an ID verification request.

cancelled_by_user_id instance-attribute
cancelled_by_user_id
comment instance-attribute
comment

IDVerificationDocumentType

Bases: AlanBaseEnum

Type of document used for ID verification.

driving_licence class-attribute instance-attribute
driving_licence = 'driving_licence'
national_identity_card class-attribute instance-attribute
national_identity_card = 'national_identity_card'
other class-attribute instance-attribute
other = 'other'
passport class-attribute instance-attribute
passport = 'passport'
residence_permit class-attribute instance-attribute
residence_permit = 'residence_permit'

IDVerificationReason

Bases: AlanBaseEnum

Reason for the ID verification request.

clinic_teleconsultation class-attribute instance-attribute
clinic_teleconsultation = 'clinic_teleconsultation'
onboarding_company class-attribute instance-attribute
onboarding_company = 'onboarding_company'
onboarding_tns class-attribute instance-attribute
onboarding_tns = 'onboarding_tns'

IDVerificationRequest dataclass

IDVerificationRequest(
    id,
    created_at,
    user_id,
    company_id,
    reason,
    status,
    bypassed_by_user_id,
    bypassed_with_comment,
    params,
    onfido_workflow_run,
    affected_company_ids,
    attempt_context,
    document_type=None,
)

Represents an ID verification request.

affected_company_ids instance-attribute
affected_company_ids
attempt_context instance-attribute
attempt_context
bypassed_by_user_id instance-attribute
bypassed_by_user_id
bypassed_with_comment instance-attribute
bypassed_with_comment
company_id instance-attribute
company_id
created_at instance-attribute
created_at
document_type class-attribute instance-attribute
document_type = None
id instance-attribute
id
is_final
is_final()

Check if the ID verification request is in a final state.

Source code in components/id_verification/public/entities/id_verification_request.py
def is_final(self) -> bool:
    """
    Check if the ID verification request is in a final state.
    """
    return self.status in (
        IDVerificationRequestStatus.passed,
        IDVerificationRequestStatus.failed,
        IDVerificationRequestStatus.cancelled,
    )
is_successful
is_successful()

Check if the ID verification request was successful.

Source code in components/id_verification/public/entities/id_verification_request.py
def is_successful(self) -> bool:
    """
    Check if the ID verification request was successful.
    """
    return self.status == IDVerificationRequestStatus.passed
onfido_workflow_run instance-attribute
onfido_workflow_run
params instance-attribute
params
reason instance-attribute
reason
status instance-attribute
status
user_id instance-attribute
user_id

IDVerificationRequestAttemptContext dataclass

IDVerificationRequestAttemptContext(
    attempt, restart_context, cancellation_context=None
)

Bases: DataClassJsonMixin

Context for an attempt of an ID verification request.

attempt instance-attribute
attempt
cancellation_context class-attribute instance-attribute
cancellation_context = None
first_attempt classmethod
first_attempt()

Creates the first attempt context for an ID verification request.

Source code in components/id_verification/public/entities/id_verification_request.py
@classmethod
def first_attempt(cls) -> "IDVerificationRequestAttemptContext":
    """
    Creates the first attempt context for an ID verification request.
    """
    return cls(attempt=1, restart_context=None, cancellation_context=None)
restart_context instance-attribute
restart_context

IDVerificationRequestStatus

Bases: AlanBaseEnum

Status of an ID verification request.

cancelled class-attribute instance-attribute
cancelled = 'cancelled'
created class-attribute instance-attribute
created = 'created'
failed class-attribute instance-attribute
failed = 'failed'
is_final
is_final()

Check if the ID verification request status is final. A final status is one that indicates the request has been completed or terminated, such as passed, failed, or cancelled.

Source code in components/id_verification/public/entities/id_verification_request.py
def is_final(self) -> bool:
    """
    Check if the ID verification request status is final.
    A final status is one that indicates the request has been completed
    or terminated, such as passed, failed, or cancelled.
    """
    return self in (
        IDVerificationRequestStatus.passed,
        IDVerificationRequestStatus.failed,
        IDVerificationRequestStatus.cancelled,
    )
passed class-attribute instance-attribute
passed = 'passed'
started class-attribute instance-attribute
started = 'started'

IDVerificationRequestUserInfo dataclass

IDVerificationRequestUserInfo(
    first_name, last_name, email, additional_properties=None
)

Bases: DataClassJsonMixin

User information required for ID verification request.

additional_properties class-attribute instance-attribute
additional_properties = None
email instance-attribute
email
first_name instance-attribute
first_name
last_name instance-attribute
last_name

IDVerificationRequestUserInfoAdditionalProperties dataclass

IDVerificationRequestUserInfoAdditionalProperties(
    birth_last_name,
    gender,
    date_of_birth,
    place_of_birth,
    session_id,
)

Bases: DataClassJsonMixin

Additional properties for user information in ID verification request. It is used for the clinic id verification flow.

birth_last_name instance-attribute
birth_last_name
date_of_birth instance-attribute
date_of_birth
gender instance-attribute
gender
place_of_birth instance-attribute
place_of_birth
session_id instance-attribute
session_id

IDVerificationRestartContext dataclass

IDVerificationRestartContext(
    previous_request_id,
    trigger,
    comment,
    previous_attempt_was_abandoned=False,
)

Bases: DataClassJsonMixin

Context for restarting an ID verification request.

comment instance-attribute
comment
previous_attempt_was_abandoned class-attribute instance-attribute
previous_attempt_was_abandoned = False
previous_request_id instance-attribute
previous_request_id
trigger instance-attribute
trigger

IDVerificationRestartTrigger

Bases: AlanBaseEnum

Trigger for restarting an ID verification request.

auto_restart_on_failed class-attribute instance-attribute
auto_restart_on_failed = 'auto_restart_on_failed'
manual_restart_from_marmot class-attribute instance-attribute
manual_restart_from_marmot = 'manual_restart_from_marmot'

components.id_verification.public.events

IdVerificationValidatedForClinic dataclass

IdVerificationValidatedForClinic(
    id_verification_request_id, app_user_id, app_id
)

Bases: Message

IdVerificationForClinicValidated event (matches IdVerificationRequestReason.clinic_teleconsultation).

app_id instance-attribute

app_id

app_user_id instance-attribute

app_user_id

id_verification_request_id instance-attribute

id_verification_request_id

IdVerificationValidatedForOnboarding dataclass

IdVerificationValidatedForOnboarding(
    id_verification_request_id,
)

Bases: Message

IdVerificationForOnboardingValidated event (matches IdVerificationRequestReason.onboarding_tns or IDVerificationRequestReason.onboarding_company).

id_verification_request_id instance-attribute

id_verification_request_id