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

# Programs

> Define a VPP program or a managed tariff, set its estimate, terms and eligibility, publish it and take enrollments.

A program is your offer to customers. In a VPP program the customer keeps their tariff and earns from grid events. In a managed tariff you supply the energy and run the device. Customers enroll in your app or in their OEM's app.

## Create

Call [create a VPP program](/docs/fleet-api/reference/vpp-programs/create.md). The program starts in `DRAFT`, private to you. `display_name`, `currency_code` and `estimate` are required.

```json theme={"system"}
{
  "display_name": "Community Powershare",
  "description": "Earn from grid events while you keep your tariff.",
  "url": "https://pick.com/programs/community-powershare",
  "managed": false,
  "listed": true,
  "currency_code": "USD",
  "estimate": {
    "amount": 612,
    "period": "YEAR",
    "dispatch_events_per_month": 15
  },
  "terms": {
    "rewards": [
      { "basis": "FIXED", "rate": 15, "period": "QUARTER" },
      { "basis": "DELIVERED_KWH", "rate": 0.35 }
    ],
    "opt_out_policy": "ALLOWED",
    "notice_days": 30
  },
  "eligibility": {
    "oem_ids": ["oem_6a3a8dada72cd0a249ff9528"],
    "device_types": ["BATTERY_STORAGE"],
    "min_storage_capacity_kwh": 5,
    "min_export_power_kw": 3,
    "telemetry_requirements": {
      "state_interval_seconds": 300,
      "device_meter_interval_seconds": 900
    }
  },
  "enrollment": {
    "join_days": 10,
    "approval_required": false,
    "dispatch_test_required": true
  }
}
```

| Group | What goes in |
| :- | :- |
| Top level | What the program is: its name and copy, `managed`, `listed` and `currency_code` |
| `estimate` | What the customer is told to expect. A projection, never binding |
| `terms` | What binds both sides once the customer has joined |
| `eligibility` | Which devices can join |
| `enrollment` | How a device gets in |

`currency_code` is the currency of `estimate.amount` and of every reward `rate`. Set it on create, because it cannot be changed afterwards.

## Estimate

`estimate` is the figure the customer sees before they join: `amount` for one `period` (`YEAR` or `MONTH`), and optionally the `dispatch_events_per_month` it assumes. The copy follows `managed`: bill savings for a managed tariff, earnings otherwise.

## Terms

`terms` holds what you and the customer are bound by after they join.

| Field | What it sets |
| :- | :- |
| `rewards` | What you pay. See [Rewards](#rewards) |
| `opt_out_policy` | `ALLOWED`, `ALLOWED_BEFORE_LOCK` (until the allocation locks at `time_allocation_locks`) or `NOT_ALLOWED`. Opted-out commitments show as `OPTED_OUT` and do not count as failures |
| `notice_days` | The longest leaving may take once the customer is enrolled. A [request to leave](/docs/fleet-api/guides/programs/enrollment-states.md#requests-to-leave) that you have not approved ends after this many days |

The enrollment keeps a copy of the `terms` it joined with. A change to `terms` applies only to enrollments made after it.

### Rewards

Each entry in `rewards` has a `basis` and a `rate`, in the program's `currency_code`. A `basis` appears at most once.

| `basis` | `rate` is paid | Extra field |
| :- | :- | :- |
| `FIXED` | Once per `period` | `period`: `MONTH`, `QUARTER` or `YEAR`, required |
| `EVENT` | Per 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 an event | |

* A `FIXED` reward follows calendar periods, pro-rated by the days the enrollment is `ACTIVE`.
* `COMMITTED_KWH` pays whatever the device then delivers, and nothing for a commitment the customer opted out of.
* A listed VPP program needs at least one reward before it can go `LIVE` (`422` `terms_required`). A managed tariff may have none.

## Eligibility

`eligibility` decides which devices are offered the program: `oem_ids`, `device_types`, an optional `zone_id`, and the minimums `min_storage_capacity_kwh` and `min_export_power_kw`. Omit `oem_ids` to enable every OEM on your account.

`telemetry_requirements` sets the sampling a device must sustain, with a minimum of 60 seconds.

| Field | What it sets |
| :- | :- |
| `state_interval_seconds` | How often the device samples its state |
| `device_meter_interval_seconds` | How finely the device meters its own flows. Required, because every program settles on device meter records |

While its state is unchanged, a device sends a heartbeat. Flatpeak sets the heartbeat at three times `state_interval_seconds`, and never below 300 seconds.

Each OEM declares the finest sampling its fleet can sustain. A program that samples finer is not offered to that OEM's devices.

## Enrollment settings

| Field | What it sets |
| :- | :- |
| `join_days` | The longest enrollment may take after the customer asks to join |
| `approval_required` | `true` holds every enrollment for your decision |
| `dispatch_test_required` | `true` (default): each device delivers a short `TEST` event before approval. `false` skips it |
| `signup_url` | Where a customer signs up for a listed managed tariff. See [Sign-up from an OEM app](#sign-up-from-an-oem-app) |

## Managed tariffs

With a managed tariff you supply the electricity and run the device with [plans](/docs/fleet-api/guides/dispatch/plans.md). Plans are accepted only under a managed program.

1. Set `managed` to `true`. Your account must be set up to supply energy.
2. When a customer joins, your tariff replaces theirs. [Create it](/docs/fleet-api/guides/devices/tariffs.md) as a device tariff, with the enrolled device's `device_id` and `location_id`.

A managed tariff needs no rewards, so the smallest one is:

```json theme={"system"}
{
  "display_name": "Phase Flex",
  "managed": true,
  "currency_code": "USD",
  "estimate": { "amount": 104, "period": "MONTH" },
  "enrollment": { "signup_url": "https://phase.com/tariffs/flex/signup" }
}
```

You can offer several managed tariffs.

### Sign-up from an OEM app

A listed managed tariff appears on the OEM app's Programs page, and the customer signs up for it on your site. Consent is the last step, after the customer has read your offer and signed up.

```mermaid actions={false} theme={"system"}
sequenceDiagram
    participant U as Customer
    participant O as OEM app
    participant S as Your site
    participant B as Your backend
    participant F as Flatpeak
    U->>O: Choose your tariff on the Programs page
    O->>S: Open enrollment.signup_url (program_id, oem_id, signup_reference)
    U->>S: Sign up
    S->>B: Customer signed up
    B->>F: Create Connect session (oem_id, program_id, signup_reference)
    F-->>B: Session ID, target_url
    S->>O: Open target_url
    U->>O: Grant consent
    F-->>B: device.connect
    B->>F: Create the device tariff
```

1. Set `enrollment.signup_url` to your sign-up page. A listed managed program cannot go `LIVE` without it (`422` `signup_url_required`).

2. The customer chooses your tariff in the OEM app, and Flatpeak opens that URL with three query parameters. At this point you hold no consent, no enrollment and no device.

   ```http theme={"system"}
   https://phase.com/tariffs/flex/signup?program_id=vpr_5c8d1f2a7b3e4d6c9a0f1b2c&oem_id=oem_6a3a8dada72cd0a249ff9528&signup_reference=sur_6a7c0e2f9b4d1a3c5e8f7b6d
   ```

3. Sign the customer up, and keep the three values with their record.

4. [Create a Connect session](/docs/fleet-api/reference/connect-sessions/create.md) with the same `oem_id` and `program_id`, add `signup_reference`, and open `target_url` as in [Connect](/docs/fleet-api/guides/connect.md). The OEM app opens Connect on the device the customer started from, so they do not pick it again.

5. The customer grants consent and Flatpeak creates the enrollment. On `device.connect`, [create the tariff](/docs/fleet-api/guides/devices/tariffs.md) for the device, which clears the `AWAITING_TARIFF` step.

A `signup_reference` works once, for 30 days. It identifies nobody on its own: the device reaches you only after the customer signs in to the OEM app and consents. If the reference is missing, used or expired (`422` `signup_reference_invalid`), create the session without it, and the customer picks the device in the OEM app.

An unlisted managed tariff needs no `enrollment.signup_url`: the customer starts in your app.

## Publish

Request `LIVE` with [update a VPP program](/docs/fleet-api/reference/vpp-programs/update.md). `program.update` fires on every status change.

| Program | What happens |
| :- | :- |
| Unlisted | Goes `LIVE` at once. Customers enroll only from your app, with `program_id` on the [Connect session](/docs/fleet-api/guides/connect.md) |
| Listed | Moves to `PENDING_APPROVAL`. Each OEM in `eligibility` approves it, then it is `LIVE` and appears on the OEM app's Programs page. Managed tariffs appear under their own heading, and choosing one sends the customer to your `enrollment.signup_url`. A rejection sets `REJECTED` with the OEM's `rejection_reason`: fix it and resubmit |

`PAUSED` keeps enrolled devices and stops new enrollments. Editing a live program's customer-facing copy or estimates sends it back for OEM approval. Flatpeak decides which devices see a live program, so you cannot target individual customers.

![A VPP program offered to a customer in Connect](https://mintcdn.com/flatpeak-d495c19e/2B6nWUU4-c2pSD3E/images/fleet/connect/vpp-programs.png?fit=max\&auto=format\&n=2B6nWUU4-c2pSD3E\&q=85\&s=d0823afac7bab764931950457be37f7b)

A listed program in Connect

## Enrollments

Flatpeak approves an enrollment once it passes consent, tariff, telemetry and the dispatch test. With `enrollment.approval_required`, it waits at step `AWAITING_APPROVAL` for you. See [Enrollment states](/docs/fleet-api/guides/programs/enrollment-states.md) for the lifecycle and your two actions.

| To read | Use |
| :- | :- |
| Uptake and capacity | [Program summary](/docs/fleet-api/reference/vpp-programs/retrieve-summary.md): `enrollment_counts` and `capacity` |
| Members | [List all VPP enrollments](/docs/fleet-api/reference/vpp-enrollments/list.md), filtered by `program_id` |
| A program's devices, kept current | A [group](/docs/fleet-api/guides/organize/groups-and-zones.md) that filters on `program_id` |


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