A canonical action is the single body shape every device push accepts, regardless of OEM. Build it from the device’s read response, post it, get anDocumentation Index
Fetch the complete documentation index at: https://docs.amps.ai/llms.txt
Use this file to discover all available pages before exploring further.
actionId back. The same shape works for immediate dispatch, scheduled dispatch, and slots inside a schedule.
Agents do not click buttons. They read a response, choose a verb, build the parameters, and post. One read and one write are enough to drive any supported device.
The action shape
command selects the variant. parameters carries the mode’s inputs. start and end declare temporal intent. onConflict resolves collisions with active actions on the same device. Every field you send is either honoured, validated, or rejected. No keys are silently discarded.
Battery command vocabulary
The canonical battery surface defines exactly six commands:| Command | Meaning |
|---|---|
charge | Direct imperative. Charge from grid or solar, optionally bounded by target and power. |
discharge | Direct imperative. Discharge to load or grid, optionally bounded by target and power. |
idle | Pause charge and discharge. Battery sits. |
auto.balanced | Optimise for self-consumption. Charge when solar is plentiful, discharge when the home needs it. |
auto.reserve | Reserve capacity for grid outage. Stay charged above the reserve floor. |
auto.export | Maximise grid export. Discharge when the export tariff is attractive. |
charge, discharge, idle) are explicit instructions. Auto commands name the device’s own self-managing modes in canonical vocabulary. auto.balanced maps to the self-consumption mode; auto.reserve to the backup mode; auto.export to the grid-export mode. The canonical name stays the same across every supported device; your request shape does not change when you move between devices.
A command present in the device’s commands map is supported. Absent means rejected. There is no supported: boolean field. See capabilities for how each device declares its subset.
Self-documenting parameters
Every numeric parameter is aQuantity: { value, unit }. The unit travels with the value, so 80 is never ambiguous between percent, kilowatts, or amps.
target, power, reserve. target is an upper bound for charge and a lower bound for discharge. power caps the rate. reserve preserves a state-of-charge floor while the mode runs.
Send a unit the device does not accept and the API returns 422 UNSUPPORTED_UNIT. Send a parameter the mode does not accept and you get 422 UNSUPPORTED_PARAMETER with a deviceCapabilities snapshot in details, so you can fix the call in one round trip. See error envelope for the full list.
Per-mode execution support
Each command on a device declares anexecution array drawn from { "immediate", "scheduled", "windowed" }. Each token names a distinct request shape:
| Token | Request shape | Meaning |
|---|---|---|
immediate | No start | Fire on receipt. |
scheduled | start only | Defer firing until start. |
windowed | start and end | Run between start and end, then revert. |
execution array returns 422 EXECUTION_NOT_SUPPORTED with details: { requestedExecution, supportedExecution }. On a typical device, auto.balanced declares ["immediate", "scheduled"] (no end makes sense for a long-running mode), while charge may declare ["immediate", "scheduled", "windowed"].
Conflict resolution
Only one non-terminal action can target a device at a time. Submit a new push while another is active or scheduled andonConflict decides what happens:
| Strategy | Behaviour |
|---|---|
cancel_and_replace | Cancel the conflicting action, run the new one. |
queue_after | Defer the new action’s start to the conflict’s end. |
| Omitted | Return 409 with the conflicting action ID and the available strategies. |
Datetime and time-window contract
start and end are ISO 8601 strings with an explicit timezone offset (Z or +01:00). Naive datetimes are rejected. Relative durations (30m, 1.5h) are accepted on start and normalised to absolute instants. start must be in the future and within 30 days. end must be after start. Time windows are right-open intervals [start, end).
Capability introspection
The device read returns the same vocabulary you post back:commands.charge.parameters on the read maps directly to action.parameters on the write. The bounds (min, max) and units come straight from the device’s declared capabilities. Bounds are optional; an absent min or max means open-ended on that side, as documented in capabilities. Presence in the map means supported. Absence means rejected.
Actions are not settings
Actions answer “what should the device do?” Settings answer “within what bounds should it operate?” Actions are time-boundable, conflict-able, and audited. Settings are persistent, non-conflicting, and fire-and-forget. The litmus test: “does this make sense with a time window?” You can say “charge from 6pm to 10pm”. You would never say “set the safety reserve to 10% from 6pm to 10pm”. If it is time-boundable, it is an action. Actions run throughPOST /battery/{deviceId} and create an audit record. Settings run through POST /battery/{deviceId}/settings and do not. See the API reference for both.
Frequently asked questions
What is auto.balanced?
auto.balanced is a canonical battery mode that names the device’s self-managing self-consumption mode under a shared name. It is one of three auto modes (auto.balanced, auto.reserve, auto.export) that every supported battery speaks. The client sends the same request shape across every device.
How do I push an action with a time window?
Sendstart and end as ISO 8601 strings with explicit timezone offsets in the action body. The command must declare windowed in its execution array; charge and discharge do, the auto modes typically do not. The action runs from start, then reverts at end. Naive datetimes without an offset are rejected. Sub-minute windows are rejected on devices whose schedulers operate at minute resolution.
What is the difference between command and parameters in the action body?
command is the verb: charge, discharge, idle, auto.balanced, auto.reserve, or auto.export. parameters is the bag of inputs the verb accepts: target (an SOC bound), power (a rate cap), reserve (an SOC floor preserved during the command). Direct commands like charge accept all three. Auto commands accept none on the canonical surface; their behaviour is intent-only.
Can I send a charge and an auto.balanced action at the same time?
Only one non-terminal action targets a device at a time. Submitauto.balanced while a charge is scheduled or acknowledged and the API returns 409 CONFLICT unless onConflict resolves it. Use cancel_and_replace to drop the existing action and run the new one, or queue_after to defer the new action until the existing one ends. One active intent per device.
What happens if I send a parameter the device does not support?
The API returns 422UNSUPPORTED_PARAMETER with details.unsupportedParameters[] and a deviceCapabilities snapshot. The snapshot has the same shape as the GET response, so the caller rebuilds the request from the rejection without a second fetch. Sending an unknown unit returns 422 UNSUPPORTED_UNIT with the supported units. Sending a value outside the declared range returns 422 PARAMETER_OUT_OF_RANGE.
Related concepts
Capabilities
How the device tells you which commands, parameters, and units it accepts.
Conflict Resolution
How
onConflict handles overlap and 409s.Scheduling
Push as the only execution primitive. Schedules as coordinators.
Error Envelope
The shape of every rejection, including
UNSUPPORTED_UNIT and EXECUTION_NOT_SUPPORTED.