Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.amps.ai/llms.txt

Use this file to discover all available pages before exploring further.

The canonical surface defines what the API accepts in general. Every device declares which subset of that surface it actually supports. One read is enough to know which commands fire, which parameters apply, which units are accepted, and which execution shapes the device can run. That declaration is the contract for that specific device.

Capabilities live on the read response

There is no separate /capabilities endpoint. The declaration ships inside GET /battery/{deviceId} (and the equivalent endpoint for other device types). One read returns state, commands, and settings:
{
  "id": "device_abc123",
  "state": { "status": "charging", "level": 50 },
  "commands": {
    "charge": {
      "parameters": {
        "power":  { "unit": "kw", "min": 0, "max": 5.0 },
        "target": { "unit": "percent", "min": 10, "max": 100 }
      },
      "execution": ["immediate", "scheduled", "windowed"]
    },
    "auto.balanced": { "parameters": {}, "execution": ["immediate", "scheduled"] }
  },
  "settings": {
    "discharge_floor": { "value": 10, "unit": "percent", "min": 0, "max": 100 }
  }
}
The shape mirrors the push body. commands.charge.parameters.power on the read maps directly to action.parameters.power on the write. See canonical actions.

Presence-based support

A capability is supported iff its key is present. Absent means rejected.
{ "commands": { "charge": "...", "auto.balanced": "..." } }
A push for discharge against this device returns 422 UNSUPPORTED_MODE. A push for charge with a reserve parameter (when parameters.reserve is absent on the read) returns 422 UNSUPPORTED_PARAMETER. There is no supported: boolean field, and no “parameter exists but is disabled” middle state. Either the device declares it and honours it, or the API rejects with structured detail you can act on. An empty parameters: {} is a valid declaration. It means the command is supported and takes no parameters. auto.* commands typically declare empty parameters because their behaviour is intent-only; strategy parameters belong to the underlying optimisation, not to the canonical request.

Per-mode execution shapes

Each command declares an execution array drawn from { "immediate", "scheduled", "windowed" }:
TokenRequest shapeValidation
immediateNo startAllowed only if "immediate" is in the array.
scheduledstart onlyAllowed only if "scheduled" is in the array.
windowedstart and endAllowed only if "windowed" is in the array.
A request shape that does not match the array returns 422 EXECUTION_NOT_SUPPORTED with details: { requestedExecution, supportedExecution }. Validation is per-mode, not per-device: a device may accept auto.balanced immediately but require charge to be windowed, and the capability response expresses that directly.

Units and bounds

Numeric parameters carry a unit and optional bounds:
{ "power": { "unit": "kw", "min": 0, "max": 5.0 } }
FieldBehaviour
unitRequired. Sending a different unit returns 422 UNSUPPORTED_UNIT.
minOptional. Open-ended on the lower side if absent.
maxOptional. Open-ended on the upper side if absent.
A push value outside the declared range returns 422 PARAMETER_OUT_OF_RANGE. Bounds reflect the OEM’s documented operating range merged with Amps-side safety. They do not change the canonical name (power) or unit (kw); they only constrain the value.

Settings

settings declares persistent device configuration with current values and bounds. The canonical writable surface:
SettingTypeUnitMeaning
safety_reservenumberpercentLowest SOC the battery will reach, even during a power cut.
discharge_floornumberpercentLowest SOC during normal operation.
charge_ceilingnumberpercentHighest SOC the battery will charge to.
export_limitnumberwattsMaximum power the battery sends to the grid.
max_charge_ratenumberampsMaximum charge current.
max_discharge_ratenumberampsMaximum discharge current.
Read-only settings (e.g., scheduler_enabled) appear in the read response but reject writes with 422 READ_ONLY_SETTING. Unknown setting keys reject with 422 UNSUPPORTED_SETTING. Writes use a separate endpoint and a sparse-map body. See What to expect on read-write parity.

Computed at read time

The capability declaration is computed at read time, not cached separately; an updated declaration takes effect on the next read. Only canonical names appear in the response.

Availability tiers

Devices graduate from sandbox-only simulations to general availability. Sandbox devices are visible only in the sandbox environment. Generally-available devices are visible to everyone in live. Tiers in between gate visibility while an integration is firming up; promotion to general requires no per-customer changes.

How agents use this

A read-then-build-then-write loop is what makes agentic device control mechanical. The agent does not need to know which OEM is on the other end; the device tells it what is acceptable and the agent picks a valid request shape. This is the “build once, control any device” property in code.
const device = await api.get(`/battery/${deviceId}`);
const cmd = device.commands.charge ? 'charge' : 'auto.balanced';
const spec = device.commands[cmd];

const parameters = Object.fromEntries(
  Object.entries(spec.parameters).map(([name, c]) => [name, { value: chooseValue(name, c), unit: c.unit }])
);
const shape = spec.execution.includes('immediate') ? {} : { start: '30m', end: '1h' };

await api.post(`/battery/${deviceId}`, { action: { command: cmd, parameters, ...shape } });
No OEM-specific knowledge needed. The device tells the agent what is acceptable, the agent builds and posts. Build once, control any device.

Frequently asked questions

How do I check if a battery supports auto.balanced?

Read the device with GET /battery/{deviceId} and inspect commands. If auto.balanced is a key on the commands map, the device supports it. If absent, a push for auto.balanced returns 422 UNSUPPORTED_MODE. There is no supported: boolean field; capability is declared by presence. The same rule holds for parameters and units: present means supported, absent means rejected.

Where is the capability response defined?

Capabilities live on the device read response, not at a separate /capabilities endpoint. GET /battery/{deviceId} (and equivalent endpoints for other device types) returns state, commands, and settings in one body. The shape mirrors the push body: commands.charge.parameters.power on the read maps directly to action.parameters.power on the write. One read is enough to construct a valid command without external documentation.

Can capabilities change at runtime?

Yes. The capability declaration is computed at read time, not cached separately, so an updated declaration takes effect on the next read. State values change continuously; the capability declaration changes when the platform ships an update. New capabilities surface automatically; clients that already poll the read pick them up without code changes.

What are availability tiers?

Devices graduate from sandbox-only simulations to general availability. Sandbox devices are visible only in the sandbox environment. Generally-available devices are visible to everyone in live. Tiers in between gate visibility while an integration is firming up. Promotion does not require per-customer changes.

Canonical Actions

The action shape that mirrors the capability response.

Device State

Where the capability response lives on the API.

What to expect

Why presence-based support and self-describing parameters.

Error Envelope

UNSUPPORTED_MODE, UNSUPPORTED_PARAMETER, READ_ONLY_SETTING.
For agent-driven walkthroughs that ground requests in capability data, see the MCP examples.