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 Amps API is built so an agent never needs device-specific code. Every read response carries the contract for the next write. Every error envelope carries enough structure for the agent to fix its own request. The same shape that drives reasoning drives rendering. This page is for integrators wiring an agent at the API layer or building a UI on top of it. The patterns work against the deployed REST API and the Documentation MCP, and will work against the Execution MCP when it ships.

Capabilities ship with state

There is no separate /capabilities endpoint. A GET /battery/{deviceId} returns the device’s current state and its full capability declaration in the same body:
{
  "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 of commands.charge.parameters.power on the read maps directly to action.parameters.power on the write. One read tells the agent which commands fire, which parameters apply with which units and bounds, and which execution shapes (immediate, scheduled, windowed) the device can run. See capabilities for the full presence-based contract: a key is present iff the device supports it. There is no supported: boolean middle state.

The Quantity envelope

Every numeric parameter is a Quantity: an object with both value and unit. The unit travels with the value, so 80 is never ambiguous between percent, kilowatts, or amps:
{ "value": 80,  "unit": "percent" }
{ "value": 3.5, "unit": "kw" }
{ "value": 10,  "unit": "percent" }
Units are drawn from a fixed enum: percent, kw, watts, kwh, amps, volts, hours, minutes, celsius, fahrenheit. The enum is the shape that lets a UI component switch on unit at the type layer, so a numeric scalar with unit: "percent" renders as a state-of-charge ring while unit: "kw" renders as a power gauge with kilowatt scale. Same component shape, different visual output, no per-device branching.

Build the next request from the read

The agent’s loop:
  1. GET /battery/{deviceId} (or get_battery via the Execution MCP)
  2. Inspect commands to choose a verb (charge, discharge, auto.balanced, etc.)
  3. Inspect commands.<verb>.parameters to know which parameters apply, with what units and bounds
  4. Inspect commands.<verb>.execution to choose immediate, scheduled, or windowed
  5. Build the body and POST /battery/{deviceId}
A first cut in TypeScript:
type Capability = {
  parameters: Record<string, { unit: string; min?: number; max?: number }>;
  execution: ("immediate" | "scheduled" | "windowed")[];
};

function buildChargeAction(cap: Capability, target = 80) {
  const targetParam = cap.parameters.target;
  const clampedTarget = Math.min(targetParam.max ?? 100, Math.max(targetParam.min ?? 0, target));
  return {
    action: {
      command: "charge",
      parameters: {
        target: { value: clampedTarget, unit: targetParam.unit },
      },
    },
  };
}
The agent does not know in advance whether a device accepts target in percent or in kwh. It reads, it builds. The same code works across every device family that follows the canonical model.

Errors carry the contract

A 422 rejection ships a structured details block the agent can act on. The most powerful field is deviceCapabilities: a snapshot of the device’s current capability declaration, in the same shape the GET returns.
{
  "success": false,
  "error": {
    "code": "UNSUPPORTED_PARAMETER",
    "message": "Parameter `reserve` is not accepted by command `charge` on this device.",
    "details": {
      "command": "charge",
      "parameter": "reserve",
      "deviceCapabilities": {
        "commands": {
          "charge": {
            "parameters": {
              "power":  { "unit": "kw",      "min": 0,  "max": 5.0 },
              "target": { "unit": "percent", "min": 10, "max": 100 }
            },
            "execution": ["immediate", "scheduled", "windowed"]
          }
        }
      }
    }
  },
  "meta": { "requestId": "req_..." }
}
The agent rebuilds the body from the snapshot in details.deviceCapabilities, drops the unsupported parameter, and retries. One round trip, no human intervention. The same pattern fires for UNSUPPORTED_UNIT, UNSUPPORTED_MODE, and EXECUTION_NOT_SUPPORTED. See error envelope for the full code list. This is what makes the API safe for an agent to drive autonomously. The error envelope is not a dead end. It is the next read.

Conflict envelopes are self-describing too

When a push collides with an active action and the request did not declare onConflict, the API returns 409 CONFLICT with the conflicting action IDs in details.conflictingActionIds. The agent reads, decides whether to cancel_and_replace or queue_after, and re-posts with onConflict set. Same pattern: rejection plus enough structure to fix the next call. See conflict resolution for the strategies and trade-offs.

What this unlocks

A single agent loop, one set of UI primitives, every device family in the canonical model. The agent’s reasoning code does not branch per OEM. The UI components do not branch per OEM. Every device that joins the model gets the same tooling for free. This is what “self-describing” delivers in practice. Not a marketing line. A shape choice that makes agents possible.

What next

Dynamic UI rendering

How the same envelope drives generative UI. Shape-to-primitive mapping, streaming placeholders.

Confirmation gates

Why destructive tool calls do not free-fire from the model.

Capabilities

The presence-based capability contract in full.

Error envelope

The full code taxonomy and the details shapes the agent can act on.