ampr docs

Connect an OCPP charger

Every charger Ampr controls, whatever the manufacturer, goes through the same three steps: register it, wait for it to connect over OCPP, then control it with plain REST calls. This guide walks through that flow for a brand-new charger and shows what’s happening on the wire while you wait. For the broader pitch on what Ampr’s OCPP charger management layer covers, see that page.

Note

All requests reuse the Authorization: Bearer $AMPR_API_KEY header from the quickstart. Every call below is a real request against https://api.ampr.dev/v1.

Register a charger for any vendor

POST /chargers only needs three fields: serial, vendor, and model. Ampr doesn’t care which manufacturer made it, as long as the hardware speaks OCPP 1.6 or 2.0.1, the same endpoint registers it:

Register a charger
curl https://api.ampr.dev/v1/chargers \
-H "Authorization: Bearer $AMPR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "serial": "ALF-114",
  "vendor": "Alfen",
  "model": "Eve Single Pro-line"
}'
{
  "data": {
    "id": "chg_5xQ2wPk8Rv",
    "serial": "ALF-114",
    "vendor": "Alfen",
    "model": "Eve Single Pro-line",
    "firmware_version": null,
    "status": "pending",
    "location_id": null,
    "tariff_id": null,
    "claim_expires_at": "2026-07-16T12:00:00Z",
    "sockets": [],
    "created_at": "2026-07-14T12:00:00Z"
  }
}

status starts at pending and sockets is empty, Ampr knows the charger exists but hasn’t heard from it yet. claim_expires_at is 48 hours out: if the physical unit never dials in and completes its OCPP handshake in that window, the claim lapses. Point the charger’s OCPP backend URL at your Ampr endpoint (from the dashboard under Settings → Chargers) and power it on to start that handshake.

Optionally pass location_id or tariff_id at registration to bind the charger to a site or a specific price; both can also be set later with PATCH /chargers/{charger_id}.

Inspect its sockets

A charger isn’t one state machine, each physical connector is. Once the charger connects and reports in, GET /chargers/{charger_id}/sockets returns every connector on it independently:

List sockets
curl https://api.ampr.dev/v1/chargers/chg_5xQ2wPk8Rv/sockets \
-H "Authorization: Bearer $AMPR_API_KEY"
{
  "data": [
    {
      "id": "skt_7mR3pQw1",
      "connector_id": 1,
      "type": "Type2",
      "status": "Available",
      "current_session_id": null
    },
    {
      "id": "skt_9kLp4Tn2",
      "connector_id": 2,
      "type": "Type2",
      "status": "Available",
      "current_session_id": null
    }
  ]
}

A dual-socket charger like this one gets two independent skt_-prefixed ids, and each carries its own status. One connector charging a vehicle never blocks or misreports the other. Fetch a single connector directly once you have its id:

Get one socket
curl https://api.ampr.dev/v1/chargers/chg_5xQ2wPk8Rv/sockets/skt_7mR3pQw1 \
-H "Authorization: Bearer $AMPR_API_KEY"

status is always one of Available, Preparing, Charging, SuspendedEV, SuspendedEVSE, Finishing, Faulted, or Unavailable, regardless of which OCPP version the charger speaks. current_session_id points at the active ses_ session once one starts, and is null otherwise.

Send remote commands

Control calls target either the charger (commands that affect the whole unit) or a specific socket (commands scoped to one connector). Reset the whole charger:

Reset the charger
curl -X POST "https://api.ampr.dev/v1/chargers/chg_5xQ2wPk8Rv/reset?wait=true" \
-H "Authorization: Bearer $AMPR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "Soft"}'

type is Soft (restart software, connectors keep any active sessions where possible) or Hard (power-cycle, always interrupts active sessions). To act on a single connector instead, target its socket, unlock releases a stuck cable:

Unlock a connector
curl -X POST "https://api.ampr.dev/v1/chargers/chg_5xQ2wPk8Rv/sockets/skt_7mR3pQw1/unlock?wait=true" \
-H "Authorization: Bearer $AMPR_API_KEY"

Every command returns a cmd_-prefixed Command resource with a status of pending, accepted, rejected, or timeout. wait=true blocks (up to timeout seconds, default 30, max 60) until the charger acknowledges, so you get the final status in one round trip; omit it for a 202 with status: "pending" immediately, then poll the command or listen for its webhook.

Tip

Sockets also take start and stop (covered in the quickstart), plus charging-profile and clear-charging-profile for Dynamic Load Balancing. Chargers additionally take firmware, for pushing an OTA update and reading back FirmwareStatusNotification progress.

OCPP 1.6 and 2.0.1, one API

Real fleets mix hardware generations, older units on OCPP 1.6, newer ones on 2.0.1, sometimes both on the same site. Ampr’s gateway detects which dialect a charger speaks at connection time and terminates it there, so none of the calls above change based on protocol version.

The two versions don’t always model state the same way. OCPP 1.6 reports connector status as a flat string that lines up directly with the status values you saw above. OCPP 2.0.1’s ConnectorStatusEnum is a different, smaller vocabulary, notably Occupied, which only means a cable is plugged in, not that current is flowing. Ampr normalizes it onto the same status set rather than inventing a Charging you didn’t earn: Occupied maps to Finishing (connected, non-drawing), Reserved maps to Unavailable, and so on. Whichever protocol version is on the wall, GET /chargers/{charger_id}/sockets/{socket_id} gives you the same eight-value enum to build against.

The first StatusNotification

Here’s what happens between registering a charger and its sockets showing up. When the physical unit powers on and completes its OCPP handshake, it sends a BootNotification, that’s what flips status from pending to online and clears claim_expires_at back to null. The claim window only covers the initial handshake, so once the charger is online there is nothing left to expire. Immediately after, it sends one StatusNotification per physical connector announcing its initial state, almost always Available. That’s the message that actually creates the rows you fetched above:

Get the socket, right after boot
curl https://api.ampr.dev/v1/chargers/chg_5xQ2wPk8Rv/sockets/skt_7mR3pQw1 \
-H "Authorization: Bearer $AMPR_API_KEY"

From here, every later StatusNotification, someone plugs in (Preparing), a session starts drawing (Charging), a fault trips (Faulted), updates that same status field in place. It’s the same lifecycle whether the charger reconnects after a network blip or you just finished the registration flow above.

Tip

Don’t want to poll a socket to catch that first transition? The handle webhooks guide covers charger.connected, fired on every BootNotification including this first one, and socket.status_changed, fired the moment a socket’s status lands, so your integration finds out the instant a charger comes online instead of on your next poll. charger.online is a related event, but it’s reserved for later reconnects, a charger that goes offline and then boots again, and won’t fire on this first connection.