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

# Unenroll a device

> Ends an enrollment on your initiative. At any `PENDING` step it turns the request to join down, and the enrollment goes `REJECTED` at step `INELIGIBLE`. In `ACTIVE`, `SUSPENDED` or `LEAVING` it ends the enrollment at once, along with any commitments the device still holds, and the enrollment goes `ENDED`. The `reason` is shown to the customer where the OEM app supports it. A terminal state returns `422` `enrollment_ended`. Fires `vpp_enrollment.update`.



## OpenAPI

````yaml fleet-api/reference/flatpeak-api-spec-fleet.json POST /vpp-enrollments/{enrollment_id}/unenroll
openapi: 3.0.3
info:
  description: >-
    The Flatpeak Fleet API. VPP operators use it to enroll, dispatch and settle
    OEM device fleets.


    ## Authentication

    Exchange your `account_id` and `secret_key` for a bearer token at `POST
    /auth/tokens` with basic auth. Send it as `Authorization: Bearer <token>` on
    every request.


    ## Base URL

    `https://fleet-api.flatpeak.com`


    ## Pagination

    Lists return `data`, `next_cursor` and `url`. Pass `next_cursor` as `cursor`
    until it is `null`. A cursor is opaque and bound to the endpoint and query
    parameters that issued it.


    ## Errors

    Every non-2xx response carries `object`, `type`, `code` and `message`.
    Branch on `code`.


    ## Money and energy

    Money is a decimal number paired with a `currency_code` (ISO 4217). Power is
    in kW and energy in kWh. Positive is import, negative is export.


    ## Live and test

    `live_mode` marks live or test data.
  version: 2027-01-01.busbar
  title: Flatpeak Fleet API
  termsOfService: https://flatpeak.com/legal
  contact:
    name: Technical Support
    email: support@flatpeak.com
    url: https://flatpeak.com/contact
  license:
    name: Apache 2.0
    url: https://flatpeak.com/legal
servers:
  - url: https://fleet-api.flatpeak.com
security:
  - bearerAuth: []
tags:
  - name: Authentication
    description: >-
      Exchange your account credentials for a bearer token to authenticate API
      requests.
  - name: Connect
    description: >-
      Create and retrieve Connect sessions for onboarding customers through your
      own app.
  - name: OEMs
    description: List and retrieve the OEMs whose devices your fleet can reach.
  - name: Devices
    description: >-
      Retrieve, list and update the devices connected to your account, and read
      their consent history and shared settings.
  - name: Locations
    description: >-
      Read the location a device sits behind: its timezone, currency, metering
      and your devices there.
  - name: Dispatch
    description: Submit plans to devices on managed tariffs and read the dispatch timeline.
  - name: Telemetry
    description: Read device state, device meter records and location meter records.
  - name: Tariffs
    description: Create, retrieve, delete, and list tariffs.
  - name: Events
    description: Retrieve and list account events.
  - name: VPP Programs
    description: >-
      Define and manage VPP programs: the commercial terms devices enroll under,
      and their live state.
  - name: Groups
    description: >-
      Named collections of devices for bulk dispatch and aggregated reads. OEMs
      see them read-only as [groups](/energy-api/reference/groups/list) on the
      Energy API.
  - name: Zones
    description: >-
      Network areas resolved to devices by Flatpeak, for locational eligibility
      and grouping without handling addresses.
  - name: Exports
    description: >-
      Package settlement evidence, consent records and enrollment registers as
      downloadable files.
  - name: Rewards
    description: >-
      The reward ledger: entries accrue from delivery, you approve them, pay the
      customers and mark them paid.
  - name: VPP Enrollments
    description: Track and act on device enrollments across your programs.
  - name: VPP Events
    description: >-
      Commit market activations: create an event for a group, then read its
      state and its devices' results.
externalDocs:
  description: Documentation
  url: https://docs.flatpeak.com
paths:
  /vpp-enrollments/{enrollment_id}/unenroll:
    parameters:
      - schema:
          type: string
        name: enrollment_id
        in: path
        required: true
        description: Flatpeak VPP Enrollment ID.
    post:
      tags:
        - VPP Enrollments
      summary: Unenroll a device
      description: >-
        Ends an enrollment on your initiative. At any `PENDING` step it turns
        the request to join down, and the enrollment goes `REJECTED` at step
        `INELIGIBLE`. In `ACTIVE`, `SUSPENDED` or `LEAVING` it ends the
        enrollment at once, along with any commitments the device still holds,
        and the enrollment goes `ENDED`. The `reason` is shown to the customer
        where the OEM app supports it. A terminal state returns `422`
        `enrollment_ended`. Fires `vpp_enrollment.update`.
      operationId: unenroll-a-device
      parameters: []
      requestBody:
        $ref: '#/components/requestBodies/VppEnrollmentAction'
      responses:
        '200':
          $ref: '#/components/responses/VppEnrollment'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '403':
          $ref: '#/components/responses/403'
        '404':
          $ref: '#/components/responses/404'
        '422':
          $ref: '#/components/responses/422'
        '429':
          $ref: '#/components/responses/429'
        '500':
          $ref: '#/components/responses/500'
        '503':
          $ref: '#/components/responses/503'
components:
  requestBodies:
    VppEnrollmentAction:
      description: Optional context for the action.
      required: false
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/VppEnrollmentAction'
  responses:
    '400':
      description: >-
        400 Bad Request: the request was malformed or a required field is
        missing or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    '401':
      description: '401 Unauthorized: authentication credentials are missing or invalid.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    '403':
      description: '403 Forbidden: you do not have permission to access this resource.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    '404':
      description: '404 Not Found: the requested resource does not exist.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    '422':
      description: >-
        422 Unprocessable Entity: the request is well-formed but cannot be
        processed, for example missing required data.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    '429':
      description: '429 Too Many Requests: the rate limit has been exceeded.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    '500':
      description: >-
        500 Internal Server Error: an unexpected error occurred. Safe to retry
        with backoff.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    '503':
      description: >-
        503 Service Unavailable: the service is temporarily down for
        maintenance. Retry after the indicated delay.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    VppEnrollment:
      description: Enrollment response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/VppEnrollment'
          example:
            id: enr_7c2f1a9e4b0d8f3a6e2c5d10
            object: vpp_enrollment
            live_mode: true
            device_id: dev_63a6087272941ef077a8fd3e
            program_id: vpr_5c8d1f2a7b3e4d6c9a0f1b2c
            oem_id: oem_6a3a8dada72cd0a249ff9528
            enrollment_state: ACTIVE
            enrollment_step: ACTIVE
            suspension_reasons: []
            terms:
              rewards:
                - basis: FIXED
                  rate: 15
                  period: QUARTER
                - basis: DELIVERED_KWH
                  rate: 0.35
              opt_out_policy: ALLOWED
              notice_days: 30
            time_enrolled: '2026-01-24T14:15:22Z'
            time_unenrolled: null
            time_created: '2026-01-20T09:30:00Z'
            time_updated: '2026-08-12T09:30:00Z'
  schemas:
    VppEnrollmentAction:
      type: object
      title: VppEnrollmentAction
      additionalProperties: false
      description: Optional context for an enrollment action.
      properties:
        reason:
          type: string
          nullable: true
          description: >-
            Why the action was taken. Shown to the customer where the OEM app
            supports it.
          example: The device's export capacity is below the program minimum.
    VppEnrollment:
      title: VppEnrollment
      type: object
      description: >-
        A device's membership of a VPP program. When the program has
        `enrollment.approval_required`, requests wait for your decision at
        `AWAITING_APPROVAL`. Changes fire `vpp_enrollment.update`.
      properties:
        id:
          type: string
          description: Flatpeak VPP Enrollment ID.
          example: enr_7c2f1a9e4b0d8f3a6e2c5d10
        object:
          type: string
          description: Object type, `vpp_enrollment`.
          example: vpp_enrollment
          enum:
            - vpp_enrollment
        live_mode:
          type: boolean
          description: '`true` for live data, `false` for test data.'
          example: true
        device_id:
          type: string
          description: Flatpeak Device ID.
          example: dev_63a6087272941ef077a8fd3e
        program_id:
          type: string
          description: Program the enrollment belongs to.
          example: vpr_5c8d1f2a7b3e4d6c9a0f1b2c
        oem_id:
          type: string
          pattern: ^oem_[A-Za-z0-9]+$
          example: oem_6a3a8dada72cd0a249ff9528
          description: 'Flatpeak OEM ID: the OEM that connects the enrolled device.'
        enrollment_state:
          type: string
          enum:
            - PENDING
            - ACTIVE
            - SUSPENDED
            - LEAVING
            - ENDED
            - REJECTED
          description: >-
            The enrollment lifecycle state. `enrollment_step` gives the detail.


            | Value | Meaning |

            | :--- | :--- |

            | `PENDING` | Onboarding: consent, a connected tariff, telemetry,
            the dispatch test and approval |

            | `ACTIVE` | Enrolled and dispatchable |

            | `SUSPENDED` | Not dispatchable until every reason in
            `suspension_reasons` clears |

            | `LEAVING` | A request to leave is waiting for your approval. The
            device serves only the commitments it already has |

            | `ENDED` | Unenrolled. Terminal |

            | `REJECTED` | Ineligible, turned down by you, or onboarding failed.
            Terminal |
          example: ACTIVE
        enrollment_step:
          type: string
          enum:
            - REQUESTED
            - AWAITING_CONSENT
            - AWAITING_TARIFF
            - AWAITING_TELEMETRY
            - TESTING_DISPATCH
            - AWAITING_APPROVAL
            - ACTIVE
            - SUSPENDED
            - UNENROLLING
            - UNENROLLED
            - INELIGIBLE
            - FAILED
          description: >-
            The step within `enrollment_state`.


            | Step | State | Meaning |

            | :--- | :--- | :--- |

            | `REQUESTED` | `PENDING` | The customer asked to join |

            | `AWAITING_CONSENT` | `PENDING` | Waiting for the consent scopes
            the program needs |

            | `AWAITING_TARIFF` | `PENDING` | Waiting for a connected tariff at
            the device's location |

            | `AWAITING_TELEMETRY` | `PENDING` | Waiting for the feeds the
            program requires |

            | `TESTING_DISPATCH` | `PENDING` | Running the program's dispatch
            test |

            | `AWAITING_APPROVAL` | `PENDING` | Waiting for your approval |

            | `ACTIVE` | `ACTIVE` | Enrolled |

            | `SUSPENDED` | `SUSPENDED` | Suspended |

            | `UNENROLLING` | `LEAVING` | The customer or the OEM asked to
            leave. The request waits for your approval |

            | `UNENROLLED` | `ENDED` | Unenrolled |

            | `INELIGIBLE` | `REJECTED` | The device does not qualify, or you
            unenrolled it while it was `PENDING` |

            | `FAILED` | `REJECTED` | Onboarding could not complete, for example
            a failed dispatch test |
          example: ACTIVE
        suspension_reasons:
          type: array
          description: >-
            Why the enrollment is suspended: an empty list unless
            `enrollment_state` is `SUSPENDED`.


            | Value | Meaning |

            | :--- | :--- |

            | `DISPATCH_UNRESPONSIVE` | The device is not acting on instructions
            |

            | `CONSENT_REVOKED` | The customer withdrew a consent scope the
            program requires |

            | `TELEMETRY_STALLED` | A required feed is present but silent beyond
            its cadence |

            | `USER_PAUSED` | Paused by the customer |
          uniqueItems: true
          items:
            type: string
            enum:
              - DISPATCH_UNRESPONSIVE
              - CONSENT_REVOKED
              - TELEMETRY_STALLED
              - USER_PAUSED
            example: TELEMETRY_STALLED
        terms:
          type: object
          description: >-
            The program's `terms` as they stood when the customer joined.
            Rewards are priced from this copy, and a later change to the
            program's `terms` does not alter it.
          properties:
            rewards:
              type: array
              description: What the customer is paid. Each `basis` appears at most once.
              example:
                - basis: FIXED
                  rate: 15
                  period: QUARTER
                - basis: DELIVERED_KWH
                  rate: 0.35
              items:
                type: object
                required:
                  - basis
                  - rate
                properties:
                  basis:
                    type: string
                    enum:
                      - FIXED
                      - EVENT
                      - DELIVERED_KWH
                      - COMMITTED_KWH
                    example: DELIVERED_KWH
                    description: >-
                      What `rate` is paid for.


                      | Value | `rate` is paid |

                      | :--- | :--- |

                      | `FIXED` | Once per `period`, by calendar period,
                      pro-rated by the days the enrollment is `ACTIVE` |

                      | `EVENT` | Per VPP event the device delivers in full |

                      | `DELIVERED_KWH` | Per kWh delivered, from the device's
                      meter records |

                      | `COMMITTED_KWH` | Per kWh of the device's locked share
                      of a VPP event, whatever the device then delivers. Nothing
                      for a commitment the customer opted out of |
                  rate:
                    type: number
                    example: 0.35
                    description: >-
                      Amount paid per unit of `basis`, in the program's
                      `currency_code`, in large currency units.
                  period:
                    type: string
                    enum:
                      - MONTH
                      - QUARTER
                      - YEAR
                    example: QUARTER
                    description: >-
                      Period a `FIXED` reward is paid for. Required when `basis`
                      is `FIXED`, and not used otherwise.
            opt_out_policy:
              type: string
              enum:
                - ALLOWED
                - ALLOWED_BEFORE_LOCK
                - NOT_ALLOWED
              example: ALLOWED
              description: >-
                Whether customers may skip individual VPP events.


                | Value | Meaning |

                | :--- | :--- |

                | `ALLOWED` | Default. Any scheduled or active VPP event.
                Skipped intervals are recorded as `OPTED_OUT`. |

                | `ALLOWED_BEFORE_LOCK` | Only until the allocation locks. |

                | `NOT_ALLOWED` | No per-event opt-out. Customers can still
                unenroll. |
            notice_days:
              type: integer
              example: 30
              description: >-
                Maximum number of days leaving may take once the customer is
                enrolled. A request to leave that you have not approved ends
                after this many days. Shown to the customer before they join and
                when they leave.
          readOnly: true
        time_enrolled:
          type: string
          format: date-time
          nullable: true
          description: Time when the enrollment went `ACTIVE`, in UTC. `null` until then.
          example: '2026-01-24T14:15:22Z'
        time_unenrolled:
          type: string
          format: date-time
          nullable: true
          description: >-
            Time the enrollment reached `UNENROLLED`, in UTC. `null` while the
            enrollment has not ended.
          example: '2026-08-01T09:12:44Z'
        time_created:
          type: string
          format: date-time
          example: '2026-01-20T09:30:00Z'
          description: Time the enrollment was created, in UTC.
        time_updated:
          type: string
          format: date-time
          example: '2026-08-12T09:30:00Z'
          description: Time the enrollment was last updated, in UTC.
      required:
        - id
        - object
        - live_mode
        - device_id
        - program_id
        - oem_id
        - enrollment_state
        - enrollment_step
        - suspension_reasons
        - terms
        - time_enrolled
        - time_unenrolled
        - time_created
        - time_updated
    Error:
      type: object
      description: Standard error envelope returned by every non-2xx response.
      required:
        - object
        - type
        - code
        - message
      properties:
        object:
          type: string
          enum:
            - error
          default: error
          description: Object type.
        type:
          type: string
          description: High-level error category.
          example: invalid_request
        code:
          type: string
          description: Machine-readable error code, safe to branch on.
          example: access_denied
        message:
          type: string
          description: Human-readable description of the error.
          example: The requested resource could not be found.
        context:
          type: string
          description: Optional originating context, for example `api.price_get`.
          example: api.price_get
        live_mode:
          type: boolean
          description: Whether the error occurred against live or test data.
        time_created:
          type: string
          format: date-time
          description: Time the error was generated (RFC 3339).
          example: '2026-01-01T00:00:00Z'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Authenticate with the `bearer_token` obtained from the
        [authentication](/fleet-api/reference/authentication) endpoint.

````

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