Skip to main content

Overview

To sell stored energy during peak-rate hours, or shave household load during a demand event, push a windowed discharge. The body mirrors a windowed charge: command: "discharge", start, end, and a target that names the lower SoC at which discharging stops. Confirm the device supports windowed discharge before pushing. Look for windowed in commands.discharge.execution.

Step 1: Check capability before pushing

Every response is wrapped in { success, data, meta }; the device sits under data.
curl -X GET https://api.amps.ai/battery/device_abc123 \
  -H "x-api-key: sk_test_xxxxxxxxxxxxxxxxxxxxxxxx"
{
  "success": true,
  "data": {
    "id": "device_abc123",
    "vendor": "foxess",
    "sync": { "available": true, "lastPulledAt": "2026-05-08T08:29:48.000Z" },
    "metadata": { "model": "FoxESS H1-5.0-E", "source": "projection" },
    "state": {
      "status": "idle",
      "capacity": 10.4,
      "level": 92,
      "chargeRate": 0,
      "dischargeLimit": 10,
      "currentMode": "auto.balanced"
    },
    "conflictStrategies": ["cancel_and_replace", "queue_after"],
    "commands": {
      "discharge": {
        "parameters": {
          "target": { "unit": "percent", "min": 10, "max": 100 },
          "power": { "unit": "kw", "min": 0, "max": 5 },
          "reserve": { "unit": "percent", "min": 0, "max": 100 }
        },
        "execution": ["windowed"]
      }
    },
    "lastAction": null,
    "currentSchedule": null
  },
  "meta": {
    "requestId": "req_7qI1eQtH",
    "environment": "sandbox",
    "timestamp": "2026-05-08T08:29:48.000Z",
    "latencyMs": 27
  }
}
For a discharge, target is the lower bound: discharging stops at this level. reserve is a second floor that protects against full depletion if the window overruns.

Step 2: Push the windowed discharge

Discharge from late afternoon into the early evening, stopping at 30%.
curl -X POST https://api.amps.ai/battery/device_abc123 \
  -H "x-api-key: sk_test_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "action": {
      "command": "discharge",
      "start": "2026-05-08T16:00:00",
      "end": "2026-05-08T19:00:00",
      "parameters": {
        "target": { "value": 30, "unit": "percent" },
        "power": { "value": 5, "unit": "kw" },
        "reserve": { "value": 20, "unit": "percent" }
      }
    }
  }'
You get back the scheduled action under data.
{
  "success": true,
  "data": {
    "id": "act_pending_007",
    "deviceId": "device_abc123",
    "deviceType": "battery",
    "command": "discharge",
    "parameters": {
      "target": { "value": 30, "unit": "percent" },
      "power": { "value": 5, "unit": "kw" },
      "reserve": { "value": 20, "unit": "percent" }
    },
    "state": "scheduled",
    "createdAt": "2026-05-08T08:30:00.000Z",
    "start": "2026-05-08T16:00:00.000Z",
    "end": "2026-05-08T19:00:00.000Z",
    "links": { "self": "/actions/act_pending_007" }
  },
  "meta": {
    "requestId": "req_8rJ2fSvI",
    "environment": "sandbox",
    "timestamp": "2026-05-08T08:30:00.000Z",
    "latencyMs": 88
  }
}

Step 3: Confirm the window opened

After start, read the action to confirm dispatch. acknowledgedAt is the moment the OEM accepted the write.
curl -X GET https://api.amps.ai/actions/act_pending_007 \
  -H "x-api-key: sk_test_xxxxxxxxxxxxxxxxxxxxxxxx"
{
  "success": true,
  "data": {
    "id": "act_pending_007",
    "deviceId": "device_abc123",
    "deviceType": "battery",
    "command": "discharge",
    "parameters": {
      "target": { "value": 30, "unit": "percent" },
      "power": { "value": 5, "unit": "kw" },
      "reserve": { "value": 20, "unit": "percent" }
    },
    "state": "acknowledged",
    "result": null,
    "errorCode": null,
    "errorMessage": null,
    "createdAt": "2026-05-08T08:30:00.000Z",
    "updatedAt": "2026-05-08T16:00:02.000Z",
    "acknowledgedAt": "2026-05-08T16:00:02.000Z",
    "completedAt": null,
    "start": "2026-05-08T16:00:00.000Z",
    "end": "2026-05-08T19:00:00.000Z",
    "links": { "self": "/actions/act_pending_007" }
  },
  "meta": {
    "requestId": "req_9sK3gTwJ",
    "environment": "sandbox",
    "timestamp": "2026-05-08T16:00:03.000Z",
    "latencyMs": 18
  }
}
A second GET /battery/device_abc123 confirms live state. While the window is active, data.state.status reports discharging and data.state.chargeRate is negative.
{
  "success": true,
  "data": {
    "id": "device_abc123",
    "vendor": "foxess",
    "sync": { "available": true, "lastPulledAt": "2026-05-08T16:30:11.000Z" },
    "metadata": { "model": "FoxESS H1-5.0-E", "source": "projection" },
    "state": {
      "status": "discharging",
      "level": 78,
      "chargeRate": -4.8,
      "capacity": 10.4,
      "dischargeLimit": 10,
      "currentMode": "discharge"
    },
    "conflictStrategies": ["cancel_and_replace", "queue_after"],
    "lastAction": {
      "id": "act_pending_007",
      "command": "discharge",
      "state": "acknowledged",
      "createdAt": "2026-05-08T08:30:00.000Z",
      "updatedAt": "2026-05-08T16:00:02.000Z",
      "errorCode": null,
      "errorMessage": null,
      "links": { "self": "/actions/act_pending_007" }
    },
    "currentSchedule": null
  },
  "meta": { "requestId": "req_0tL4hUxK", "environment": "sandbox", "timestamp": "2026-05-08T16:30:11.000Z", "latencyMs": 41 }
}
At end, the action transitions to completed and the battery reverts to idle, holding the level the window left behind. The window closes, the device stops moving energy. To resume self-management, push auto.balanced (or any other standing mode) next.

What next

Schedule a charge for later

The mirrored recipe for cheap-rate charging.

Subscribe to webhooks

Get push.completed events when the window closes.

Handle conflicts

What to do when a colliding action is in flight.

Canonical actions

The semantic model behind charge, discharge, and auto.