> ## Documentation Index
> Fetch the complete documentation index at: /docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Enrollment states

> The six states of a VPP enrollment, the steps inside them, and the two actions you take.

An [enrollment](/docs/fleet-api/reference/vpp-enrollments/retrieve.md) is a device's membership of one program. `enrollment_state` holds one of six states and `enrollment_step` the step inside it. `vpp_enrollment.update` fires on every change, and those events are the enrollment's history.

## States

```mermaid actions={false} theme={"system"}
stateDiagram-v2
    direction TB
    [*] --> PENDING
    PENDING --> REJECTED : turned down or failed
    PENDING --> Enrolled : approved
    state Enrolled {
        direction LR
        ACTIVE --> SUSPENDED : reason raised
        SUSPENDED --> ACTIVE : reasons cleared
    }
    Enrolled --> LEAVING : asks to leave
    LEAVING --> ENDED : approved, or notice over
    Enrolled --> ENDED : you unenroll
```

| `enrollment_state` | Dispatchable | Meaning |
| :- | :- | :- |
| `PENDING` | No | Onboarding |
| `ACTIVE` | Yes | Enrolled |
| `SUSPENDED` | No | Enrolled but paused. `suspension_reasons` says why |
| `LEAVING` | Only for commitments already made | A request to leave is waiting for your approval. It ends within the `notice_days` of its `terms` |
| `ENDED` | No | Unenrolled, a terminal state. Records are kept |
| `REJECTED` | No | Ineligible, turned down by you, or failed during onboarding. Also terminal |

## Steps

| `enrollment_step` | State | Meaning | Who acts |
| :- | :- | :- | :- |
| `REQUESTED` | `PENDING` | Flatpeak checks the device against the program's `eligibility` | Flatpeak |
| `AWAITING_CONSENT` | `PENDING` | Waiting for the consent scopes the program needs. Cleared in Connect | Customer |
| `AWAITING_TARIFF` | `PENDING` | Waiting for a connected tariff at the device's location. Skipped when one exists | Customer, or you for a managed tariff |
| `AWAITING_TELEMETRY` | `PENDING` | Waiting for the first device state and meter records the program requires. Skipped if none | OEM |
| `TESTING_DISPATCH` | `PENDING` | The device must fetch, confirm and deliver a short `TEST` event. Retried up to 3 times over 7 days, then `FAILED`. Skipped if `enrollment.dispatch_test_required` is `false` | Flatpeak, OEM |
| `AWAITING_APPROVAL` | `PENDING` | Waiting for your approval. Only with `enrollment.approval_required` | You |
| `ACTIVE` | `ACTIVE` | Dispatchable | None |
| `SUSPENDED` | `SUSPENDED` | Not dispatchable until the reasons clear | Flatpeak |
| `UNENROLLING` | `LEAVING` | The customer or the OEM asked to leave. The request waits for your approval | You |
| `UNENROLLED` | `ENDED` | Left the program | None |
| `INELIGIBLE` | `REJECTED` | Fails the program's rules, or you unenrolled it while it was `PENDING` | None |
| `FAILED` | `REJECTED` | Onboarding could not complete, for example a failed dispatch test | None |

## Suspensions

`suspension_reasons` lists why an enrollment is `SUSPENDED`, and is empty in every other state. Flatpeak raises and clears the reasons, and the enrollment returns to `ACTIVE` once every reason has cleared. `DISPATCH_UNRESPONSIVE` and `TELEMETRY_STALLED` are raised only after a grace period, so a short outage does not suspend a device.

| Reason | Meaning |
| :- | :- |
| `DISPATCH_UNRESPONSIVE` | The device is not fetching or confirming instructions |
| `TELEMETRY_STALLED` | A required feed is silent beyond its cadence |
| `CONSENT_REVOKED` | The customer withdrew a consent scope the program requires |
| `USER_PAUSED` | Paused by the customer. The Fleet API has no call for it, and the customer resumes it themselves |

## Requests to leave

A customer or the OEM can ask to leave at any time, and the enrollment moves to `LEAVING` at step `UNENROLLING`. `vpp_enrollment.update` fires when the request arrives.

You approve the request with [approve](/docs/fleet-api/reference/vpp-enrollments/approve.md), which ends the enrollment at once. You cannot refuse it: a request left unanswered ends on its own when the `notice_days` of the enrollment's `terms` have passed, and with `notice_days` at 0 it ends as soon as it is made. Until then the device serves only the commitments it already has.

## Actions

Two endpoints change an enrollment. Approve answers whatever the enrollment is waiting on from you, and unenroll ends it on your initiative. Each is valid only where listed; anywhere else it returns `422`.

| Endpoint | When | Effect |
| :- | :- | :- |
| [approve](/docs/fleet-api/reference/vpp-enrollments/approve.md) | At step `AWAITING_APPROVAL` | `ACTIVE` |
| [approve](/docs/fleet-api/reference/vpp-enrollments/approve.md) | At step `UNENROLLING` | `ENDED` |
| [unenroll](/docs/fleet-api/reference/vpp-enrollments/unenroll.md) | At any `PENDING` step | `REJECTED`, at step `INELIGIBLE` |
| [unenroll](/docs/fleet-api/reference/vpp-enrollments/unenroll.md) | In `ACTIVE`, `SUSPENDED` or `LEAVING` | `ENDED` at once, along with any commitments the device still holds |

Send a `reason` with unenroll, and the customer sees it where the OEM app supports that.

## Terms

The enrollment keeps a copy of the program's `terms` as they stood when the customer joined: `rewards`, `opt_out_policy` and `notice_days`. Rewards are priced from that copy. A later change to the program's `terms` applies only to enrollments made after it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.