Connect API - Push Meter Onboarding
TL;DR
POST /vendors/{id}/connectionswithtype: "push"→ response includes aflowOptionsarray carryingpushUrl+apiKey→ display the URL to the user → user pastes it into their vendor portal- The placeholder returned from
GET /vendors/{id}/devicesis a synthetic stand-in (externalId == connectionId); it staysavailable: truebefore any push has been received
Step 1: List Vendors
Code
Response:
Code
Step 2: Get Connection Options
Code
Response:
Code
Note:
formOptionsis intentionally empty for push connections — the push URL does not exist yet at this point. It is minted on the next call (Step 3).
Step 3: Create Connection
Code
Request:
Code
The push flow needs no data payload — the connection itself is what mints the credential.
Response:
Code
The flowOptions array is a generic {name, data} list for surfacing flow-specific values back to the partner. For push connections it contains:
name | data |
|---|---|
pushUrl | The full URL the user pastes into the vendor portal. Contains homeId, vendor slug, connectionId, and the apiKey query parameter. |
apiKey | The bare API key, useful for vendor portals that ask for URL and credential in separate fields. |
Step 4: User Interaction required: Paste Push URL in Vendor Portal
Ask the meter owner to:
- Open the configuration interface of their vendor portal or device.
- Locate the setting for the Push API URL (sometimes called callback URL, webhook URL, or backend URL).
- Paste the
pushUrlvalue from Step 3: Create Connection into that field. If the portal asks for URL and key separately, use thepushUrl(with the?code=stripped) plus the bareapiKeyvalue. - Save/apply the configuration. The vendor will start posting telemetry to that URL on its own cadence.
The synthetic device entry returned from Step 6: List Devices from Vendor is available immediately after connection creation - you do not need to wait for the first push to arrive before continuing to Step 7.
Step 5: Delete Connection (Optional)
If the connection is no longer needed (e.g. the user changed their mind, or the credentials were never pasted into the vendor portal), it can be deleted:
Code
Response: 204 on success
Important: All devices attached to the connection must be deleted first (via
DELETE /v1/users/{userId}/devices/{deviceId}). If you attempt to delete a connection with linked devices, the endpoint returns409 Conflict.
Step 6: List Devices from Vendor
Code
Response:
Code
Note: Unlike OCPP, the
?connectionId=query parameter is not required here — the listing already returns the single synthetic placeholder for each open push connection. The push flow has no remote device list to enumerate; the connection itself is the device, soexternalId === connectionId.
Step 7: Create Device in Connect API
Code
Request:
Code
Response:
Code
The connect capability echoes the flowOptions from the original connection inside its data block — so a partner who holds only the device record can still recover the push URL and API key (see next section).
Sending Telemetry to the Push URL
The Push API lets the meter owner POST their own electricity meter, inverter and battery readings to Clever-PV. It is the right fit when the device cannot be polled directly from the internet — for example, a script running on the local network that reads the inverter and forwards the values to us at a regular cadence. Once those readings arrive, the device participates in surplus control just like any other meter.
Tip for developers: the same endpoint doubles as a solar-inverter simulator. POSTing crafted payloads from a script or test harness lets you exercise the Connect API end-to-end — surplus control, battery telemetry, frontend display — without any physical hardware.
Request shape
Send an HTTP POST to the pushUrl minted in Step 3. The ?code=<API_KEY> query parameter is the authentication token and must be kept on the URL.
- Method:
POST - Content-Type:
application/json - Body: the JSON object documented below. All numeric values are transmitted as whole numbers (integers).
| Field | Type | Required | Description |
|---|---|---|---|
watt | integer | yes | Net grid power. Negative = feed-in (surplus), positive = consumption from the grid. |
producingWatt | integer | optional | Current PV production. Always positive. |
soc | integer (0–100) | optional | Battery state of charge, in percent. |
chargingPower | integer | optional | Battery charge / discharge power in W. Always positive — the direction is given by powerStorageState. |
powerStorageState | integer | optional | Battery state: 0 = Idle, 1 = Charging, 2 = Disabled, 3 = Discharging. |
batteryControlEnabled | boolean | optional | Set to true to opt in to battery control commands in the response. |
Examples
Grid power only. Only the home's net consumption is submitted. A negative value means surplus is being fed into the public grid; a positive value means power is being drawn from it.
Code
Grid power + PV production. producingWatt carries the current PV production and is always positive.
Code
Grid power + PV production + battery telemetry. soc is the current battery state of charge in percent, chargingPower is the charge or discharge power in W (always positive), and powerStorageState carries the direction.
Code
Opting in to battery control. Add batteryControlEnabled: true to receive battery control commands in the response.
Code
Response when battery control is enabled
When batteryControlEnabled is true, the 202 response body carries the next battery control command for the home:
Code
The response is event-driven: a body is only returned when clever-PV has a pending command for the device — typically while a surplus charging or load-management cycle is actively overriding the battery state. An empty 202 body is the normal case during idle periods and does not mean batteryControlEnabled is being ignored. Once issued, the latest command stays in the response on every subsequent request until clever-PV issues a new one or the command expires after 24 hours, so the device should apply commands idempotently and only act on transitions.
batteryControlState | Meaning |
|---|---|
0 | Normal |
1 | Idle |
2 | Force Charge |
3 | Force Discharge |
4 | Limit Charge Power |
For Force Charge, Force Discharge and Limit Charge Power, batteryForcePowerWatt carries the target power in W. For Normal and Idle it is null.
Status code, cadence, and limits
- A successful submission is acknowledged with
202 Accepted. - Posted values are buffered for further processing — they are not reflected in the frontend instantly. The displayed value refreshes roughly every 15 seconds.
- The endpoint is rate-limited to 180 requests per minute.