Skip to main content

Prerequisites

Before you begin, you’ll need:
  • An Amps AI customer account
  • API keys (sandbox and/or live)
  • A webhook endpoint URL (optional but recommended)
  • Access to the Link UI for device authentication

Step 1: Get your API keys

You will need to request Sandbox access before completing these steps. Get sandbox access to continue.
Every request goes to the same host:
https://api.amps.ai
The environment is decided by the prefix on the API key, not the URL. An sk_test_ key routes to sandbox simulations; an sk_live_ key routes to real OEM devices. Send the key in the x-api-key header.
1

Create Account

Sign up for an Amps AI account in the dashboard
2

Generate API Keys

Create separate API keys for sandbox and live environments in your dashboard settings
3

Configure Webhooks

Set up webhook endpoints to receive device events and action notifications
Keep your API keys secure. Never commit them to version control or expose them in client-side code.

Device-type routes

Five device families, one URL shape. The path segment names the device type and the rest is the same across all of them:
Device typeReadPushSettings
BatteryGET /battery/{deviceId}POST /battery/{deviceId}POST /battery/{deviceId}/settings
EV chargerGET /ev-charger/{deviceId}POST /ev-charger/{deviceId}POST /ev-charger/{deviceId}/settings
HVACGET /hvac/{deviceId}POST /hvac/{deviceId}none
Solar inverterGET /solar-inverter/{deviceId}read-only (push returns 422 DIRECT_ACTION_UNSUPPORTED)none
VehicleGET /vehicle/{deviceId}read-only (push returns 422 DIRECT_ACTION_UNSUPPORTED)none
Solar inverters and vehicles accept no imperative commands. They report state; they do not take actions. Use them for monitoring and inclusion in fleet-wide reads. Direct your end users to the Link UI to authenticate with OEMs and connect their devices: Base URL:
https://auth.amps.ai/{appId}
For Sandbox Testing:
https://auth.amps.ai/{appId}?sandbox=true
For Production:
https://auth.amps.ai/{appId}
Replace {appId} with your actual application ID from the dashboard. The Link UI handles the entire authentication flow including OEM selection, credential entry, MFA (if required), device selection, and consent collection.

Environments Guide

Learn more about Sandbox and Live environments

Step 3: Make your first API call

Test your integration by retrieving device data:
curl
curl -X GET "https://api.amps.ai/battery/{deviceId}" \
  -H "x-api-key: sk_test_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json"

Example response

Every response is wrapped in a consistent envelope: success, data, and meta. A single read carries the device under data; a list carries {items, pagination} under data. See error envelope for the full shape and the failure variant.
{
  "success": true,
  "data": {
    "id": "device_abc123",
    "vendor": "tesla",
    "state": {
      "level": 85,
      "status": "charging",
      "capacity": 13.5,
      "chargeRate": 5.2,
      "dischargeLimit": 10,
      "currentMode": "charge"
    },
    "metadata": {
      "model": "Powerwall 2",
      "source": "projection"
    },
    "sync": {
      "available": true,
      "lastPulledAt": "2026-06-01T10:30:00.000Z"
    },
    "commands": {
      "charge": {
        "parameters": {
          "target": { "unit": "percent", "min": 10, "max": 100 },
          "power": { "unit": "kw", "min": 0, "max": 5.0 }
        },
        "execution": ["immediate", "scheduled"]
      },
      "discharge": {
        "parameters": {
          "target": { "unit": "percent", "min": 10, "max": 100 },
          "power": { "unit": "kw", "min": 0, "max": 5.0 }
        },
        "execution": ["immediate", "scheduled"]
      },
      "auto.balanced": {
        "parameters": {},
        "execution": ["immediate", "scheduled"]
      }
    },
    "conflictStrategies": ["cancel_and_replace", "queue_after"],
    "settings": {
      "discharge_floor": { "value": 10, "unit": "percent", "min": 10, "max": 100 },
      "charge_ceiling": { "value": 100, "unit": "percent", "min": 10, "max": 100 }
    },
    "lastAction": {
      "id": "act_abc123",
      "command": "auto.balanced",
      "state": "completed",
      "createdAt": "2026-06-01T09:15:00.000Z",
      "updatedAt": "2026-06-01T09:15:03.000Z",
      "errorCode": null,
      "errorMessage": null,
      "links": { "self": "/actions/act_abc123" }
    },
    "currentSchedule": null
  },
  "meta": {
    "requestId": "req_8sW2dRtX",
    "environment": "sandbox",
    "timestamp": "2026-06-01T10:30:00.000Z",
    "latencyMs": 42
  }
}
In code, unwrap the envelope before reading device fields:
const res = await fetch("https://api.amps.ai/battery/device_abc123", {
  headers: { "x-api-key": process.env.AMPS_API_KEY },
});
const body = await res.json();          // { success, data, meta }
const device = body.data;                // the device record
console.log(device.state.status);        // "charging"
body = res.json()                        # { success, data, meta }
device = body["data"]                    # the device record
print(device["state"]["status"])         # "charging"
Reading body.state or body["state"] returns undefined / raises KeyError. The shape is the same on every endpoint, so write the unwrap once and reuse it. The state object names what the device is doing now: status is charging, discharging, idle, or standby; currentMode echoes the standing command (a charge that has already reached its target reads status: "idle", currentMode: "charge", chargeRate: 0, because the command stands but no energy is moving). See device state for the full state schema per device type. The conflictStrategies array lists the onConflict values this device accepts when a push collides with an action already in flight. Read it before scheduling; conflict handling is covered in Step 5 and in conflict resolution. lastAction is a summary of the most recent action dispatched to this device, enough to render a device card in one call: the canonical command, the lifecycle state, createdAt and updatedAt (the most recent state transition), and errorCode/errorMessage (populated on a failed action, null otherwise). It is a HATEOAS pointer rather than a denormalised copy: follow lastAction.links.self to GET /actions/{actionId} for the full record (parameters, full result, all timestamps). A just-pushed action can take a moment to appear here; if lastAction looks stale right after a write, read the action by id at links.self for the current lifecycle state. currentSchedule is the schedule governing the device. It is null for now and arrives with the scheduler; setting a schedule via PUT /battery/{deviceId}/schedule returns 501 NOT_IMPLEMENTED until it lands. Following a schedule already works today through the HVAC follow_schedule command.

Step 4: Set up webhooks

Configure webhook endpoints to receive real-time notifications:
  1. Add your webhook URL in the dashboard
  2. Subscribe to the events you want to receive:
    • device.connected
    • device.disconnected
    • push.completed
    • push.failed
  3. Verify webhook signatures to ensure requests are from Amps AI

Webhook Setup Guide

Learn more about configuring webhooks

Step 5: Push your first command

Send a command to a device. Every device type takes the same body: an action with a command and its parameters. This example holds a thermostat in auto between a heat and cool setpoint in degrees Celsius:
curl
curl -X POST "https://api.amps.ai/hvac/{deviceId}" \
  -H "x-api-key: sk_test_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "action": {
      "command": "auto",
      "parameters": {
        "heatSetpoint": { "value": 20, "unit": "celsius" },
        "coolSetpoint": { "value": 24, "unit": "celsius" }
      }
    }
  }'
The response carries the new action under data, wrapped in the same {success, data, meta} envelope as the read:
{
  "success": true,
  "data": {
    "id": "act_hvac_004",
    "deviceId": "device_hvac_001",
    "deviceType": "hvac",
    "command": "auto",
    "parameters": {
      "heatSetpoint": { "value": 20, "unit": "celsius" },
      "coolSetpoint": { "value": 24, "unit": "celsius" }
    },
    "state": "acknowledged",
    "createdAt": "2026-06-01T10:30:00.000Z",
    "links": { "self": "/actions/act_hvac_004" }
  },
  "meta": {
    "requestId": "req_9tX3eRuY",
    "environment": "sandbox",
    "timestamp": "2026-06-01T10:30:00.000Z",
    "latencyMs": 84
  }
}
const body = await res.json();
const { id, deviceId, state, links } = body.data;  // unwrap before reading
An immediate push (no start) lands in acknowledged and reaches completed. A deferred or windowed push (with start, optionally with end) waits in scheduled until the fire time, then moves to acknowledged and on to completed. Use webhooks or GET /actions/{actionId} (via links.self) to track the transition. The same shape drives a battery (charge, discharge, the auto modes) and an EV charger (charge, idle); only the verb and its parameters change. Live battery control is available; live EV charger and thermostat control is coming soon, and the sandbox environment runs the full surface in the meantime.

Next steps

Authentication

Learn about API authentication

Link UI Guide

Understand the device connection flow

Webhooks

Set up webhook endpoints

API Reference

Explore the full API documentation

Common tasks

Use the sandbox environment. The sandbox simulates the full canonical surface across every device type. When you push a command in sandbox, the next read reflects that command right away: push charge and the device reads charging, push idle and it reads idle. See the Environments Guide for the full sandbox model.
Each device has a unique deviceId. List all devices of a type with GET /battery, GET /ev-charger, GET /hvac, GET /solar-inverter, or GET /vehicle. The response shape is { success: true, data: { items: [...], pagination: {...} }, meta }. Filter to one end-user with ?userId=.
It depends on the failure. A transient OEM error (network blip, timeout, rate limit) falls back to the last-known state: you get a 200 with sync.available: false so you know the data is not fresh. A permanent error (DEVICE_OFFLINE, DEVICE_NOT_FOUND) returns a 4xx with the standard error envelope and no stale fallback. See device state for the full three-tier model. Webhooks notify you of connection status changes.
Actions are processed asynchronously. The push response’s data.links.self points at the action. Poll GET /actions/{actionId} (device-agnostic, the same route for every device type) and unwrap body.data for the current lifecycle state, or subscribe to push.completed and push.failed webhooks. An immediate push starts at acknowledged; a deferred or windowed push waits in scheduled until the fire time.