# Enrollment Design

![App Enrollment Flow](images/app-enrollment-flow.png)

## Example

- **[Enrollment walkthrough](https://support.saltosystems.com/xs4face/user-guide/user/enrollment/):** example behaviour for image quality feedback and retakes.
- **[User disenrollment](https://support.saltosystems.com/xs4face/user-guide/user/disenrollment/):** example consent deletion.

The endpoints referenced below are documented in the [Enrollment API](App/_api). Accessing this API requires an authentication token with the scope `xs4face.featureapi.enrollment`.

## User enrollment profile

The user profile, including their current status, can be retrieved with the
following endpoint:

[`GET /features/{featureId}/users/{userId}`](App/_api)

``` json
{
    "userId": "AB-11-22",
    "email": "john@example.com",
    "status": "PENDING_IMAGE",
    "lastUpdated": "2026-08-04T10:57:43.332Z"
}
```

The user status is described in [User Lifecycle](MMS/_user_lifecycle).

## Terms of service

Record acceptance of the **Salto End User Licence Agreement** for face
recognition. The following text **MUST** be displayed to the user to allow Salto
Systems to manage face enrollment. Show this step every time the user accesses
the feature while their status is not yet `ACTIVE`.

> [!info]
> Face recognition services are provided by Salto Systems, S.L. By continuing, you accept that the use of this service is subject to Salto's [End User Licence Agreement](https://saltosystems.com/en/legal-data/software-terms/access-control-cloud-applications/end-user-license-agreement/).
>
> Salto Systems, S.L. will process and store your personal data in the EU, in order to manage face recognition services, in accordance with Salto Systems' [Privacy Policy](https://saltosystems.com/en/legal-data/privacy/privacy-policies/access-control-cloud-applications/).

- **Partner responsibility:** display the End User Licence Agreement for face recognition to the user and record acceptance.
- **Display when:** the user accesses the feature and their status is not `ACTIVE`.
- **Endpoint:** [`PUT /features/{featureId}/users/{userId}/termsOfService`](App/_api)
- **Complete when:** the user status is not `PENDING_TOS`.
- **Blocked while:** acceptance is missing — the journey cannot finish even if all other enrollment data is present.

> [!note]
> A user may have already accepted the **Salto End User Licence Agreement**
> through their use of face recognition at another provider. To avoid confusion,
> and to make sure the user knows who will be managing their data within the app,
> show this step whenever they have not yet provided consent.

## Image submission

Capture a user image and handle the API's validation outcome.

- **Integrator responsibility:** take the picture and show clear feedback on validation failures so the user knows what to fix before retaking.
- **Endpoint:** [`POST /features/{featureId}/users/{userId}/image`](App/_api)
- **Complete when:** the image is accepted.
- **Blocked by:** image quality or validation errors. A valid image is a core prerequisite for enrollment.

> [!tip]
> A user may have already enrolled an image through their use of face
> recognition at another provider. When this is the case they can be taken
> straight to consent capture, so check the status returned for the user rather
> than assuming every enrollment starts at `PENDING_TOS`.

### Key response codes

| Status | Meaning |
| --- | --- |
| `204` | Image accepted — the user moves to `PENDING_CONSENT`. |
| `400` | Image validation failed — see the image error codes below. |

A `400` response returns one or more image error codes in the `errors` array:

| Code | Meaning | Example |
| --- | --- | --- |
| `IE001` | Multiple faces found | [image](/images/multiple_faces.jpg) |
| `IE002` | Poor resolution | [image](/images/poor_resolution.jpg) |
| `IE003` | Poor exposure | [image](/images/poor_exposure.jpg) |
| `IE004` | Poor greyscale profile | None |
| `IE005` | Image contains hotspots | [image](/images/no_hot_spots.jpg) |
| `IE006` | Face not facing forward | [image](/images/not_frontal.jpg) |
| `IE007` | Lighting is not uniform | [image](/images/not_uniform_lighting.jpg) |
| `IE008` | Eyes are not open | [image](/images/eyes_not_open.jpg) |
| `IE009` | Tint on glasses | [image](/images/tinted_glasses.jpg) |
| `IE010` | Image is not sharp | [image](/images/not_sharp.jpg) |
| `IE011` | Mouth is not closed | [image](/images/mouth_open.jpg) |

> [!note]
> Once accepted, the user image cannot be retrieved through the API or replaced.
> This prevents a user swapping their image to give someone else — a family
> member, for example — access.

## Consent capture

Allow the user to view the privacy policy that details the use of face
recognition, and record their consent.

- **Integrator responsibility:** give the user access to a valid privacy policy.
- **Endpoint:** [`PUT /features/{featureId}/users/{userId}/consent`](App/_api)
- **Complete when:** the user status is `ACTIVE`.
- **Blocked while:** consent is missing. Present this step clearly, as consent is a prerequisite for approval.

## Revocation and removal

This is the user-triggered withdrawal of consent through the app. Membership
expiry, and other reasons for removing a user, are handled by the MMS. Both are
required to fulfil the obligations of GDPR.

- **Integrator responsibility:** provide a mechanism in the app for the user to opt out of face recognition.
- **Endpoint:** [`DELETE /features/{featureId}/users/{userId}/consent`](App/_api)
- **Complete when:** the user no longer exists — see below.

> [!important]
> Revocation removes the user rather than moving them to an earlier state. Every
> endpoint in this API then responds `400` with the error code `E002` (resource
> does not exist), so treat revocation as terminal in your app. Re-enrolling
> means adding the user again through the [Users API](MMS/_api) and starting
> from `PENDING_TOS`.

