Import Transactions
curl --request POST \
--url https://api.plugchoice.com/v3/chargers/{charger}/transactions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"source": "<string>",
"transactions": [
{
"connector_id": 123,
"id_tag": "<string>",
"meter_start": 123,
"meter_stop": 123,
"started_at": "<string>",
"stopped_at": "<string>",
"stop_reason": "<string>",
"reservation_id": 123
}
]
}
'import requests
url = "https://api.plugchoice.com/v3/chargers/{charger}/transactions"
payload = {
"source": "<string>",
"transactions": [
{
"connector_id": 123,
"id_tag": "<string>",
"meter_start": 123,
"meter_stop": 123,
"started_at": "<string>",
"stopped_at": "<string>",
"stop_reason": "<string>",
"reservation_id": 123
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
source: '<string>',
transactions: [
{
connector_id: 123,
id_tag: '<string>',
meter_start: 123,
meter_stop: 123,
started_at: '<string>',
stopped_at: '<string>',
stop_reason: '<string>',
reservation_id: 123
}
]
})
};
fetch('https://api.plugchoice.com/v3/chargers/{charger}/transactions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.plugchoice.com/v3/chargers/{charger}/transactions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'source' => '<string>',
'transactions' => [
[
'connector_id' => 123,
'id_tag' => '<string>',
'meter_start' => 123,
'meter_stop' => 123,
'started_at' => '<string>',
'stopped_at' => '<string>',
'stop_reason' => '<string>',
'reservation_id' => 123
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.plugchoice.com/v3/chargers/{charger}/transactions"
payload := strings.NewReader("{\n \"source\": \"<string>\",\n \"transactions\": [\n {\n \"connector_id\": 123,\n \"id_tag\": \"<string>\",\n \"meter_start\": 123,\n \"meter_stop\": 123,\n \"started_at\": \"<string>\",\n \"stopped_at\": \"<string>\",\n \"stop_reason\": \"<string>\",\n \"reservation_id\": 123\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.plugchoice.com/v3/chargers/{charger}/transactions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"source\": \"<string>\",\n \"transactions\": [\n {\n \"connector_id\": 123,\n \"id_tag\": \"<string>\",\n \"meter_start\": 123,\n \"meter_stop\": 123,\n \"started_at\": \"<string>\",\n \"stopped_at\": \"<string>\",\n \"stop_reason\": \"<string>\",\n \"reservation_id\": 123\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.plugchoice.com/v3/chargers/{charger}/transactions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source\": \"<string>\",\n \"transactions\": [\n {\n \"connector_id\": 123,\n \"id_tag\": \"<string>\",\n \"meter_start\": 123,\n \"meter_stop\": 123,\n \"started_at\": \"<string>\",\n \"stopped_at\": \"<string>\",\n \"stop_reason\": \"<string>\",\n \"reservation_id\": 123\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"message": "<string>",
"created": 123,
"skipped": 123,
"failed": 123,
"errors": [
{
"index": 123,
"errors": {}
}
],
"transactions": [
{}
]
}Transactions
Import Transactions
POST
/
v3
/
chargers
/
{charger}
/
transactions
Import Transactions
curl --request POST \
--url https://api.plugchoice.com/v3/chargers/{charger}/transactions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"source": "<string>",
"transactions": [
{
"connector_id": 123,
"id_tag": "<string>",
"meter_start": 123,
"meter_stop": 123,
"started_at": "<string>",
"stopped_at": "<string>",
"stop_reason": "<string>",
"reservation_id": 123
}
]
}
'import requests
url = "https://api.plugchoice.com/v3/chargers/{charger}/transactions"
payload = {
"source": "<string>",
"transactions": [
{
"connector_id": 123,
"id_tag": "<string>",
"meter_start": 123,
"meter_stop": 123,
"started_at": "<string>",
"stopped_at": "<string>",
"stop_reason": "<string>",
"reservation_id": 123
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
source: '<string>',
transactions: [
{
connector_id: 123,
id_tag: '<string>',
meter_start: 123,
meter_stop: 123,
started_at: '<string>',
stopped_at: '<string>',
stop_reason: '<string>',
reservation_id: 123
}
]
})
};
fetch('https://api.plugchoice.com/v3/chargers/{charger}/transactions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.plugchoice.com/v3/chargers/{charger}/transactions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'source' => '<string>',
'transactions' => [
[
'connector_id' => 123,
'id_tag' => '<string>',
'meter_start' => 123,
'meter_stop' => 123,
'started_at' => '<string>',
'stopped_at' => '<string>',
'stop_reason' => '<string>',
'reservation_id' => 123
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.plugchoice.com/v3/chargers/{charger}/transactions"
payload := strings.NewReader("{\n \"source\": \"<string>\",\n \"transactions\": [\n {\n \"connector_id\": 123,\n \"id_tag\": \"<string>\",\n \"meter_start\": 123,\n \"meter_stop\": 123,\n \"started_at\": \"<string>\",\n \"stopped_at\": \"<string>\",\n \"stop_reason\": \"<string>\",\n \"reservation_id\": 123\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.plugchoice.com/v3/chargers/{charger}/transactions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"source\": \"<string>\",\n \"transactions\": [\n {\n \"connector_id\": 123,\n \"id_tag\": \"<string>\",\n \"meter_start\": 123,\n \"meter_stop\": 123,\n \"started_at\": \"<string>\",\n \"stopped_at\": \"<string>\",\n \"stop_reason\": \"<string>\",\n \"reservation_id\": 123\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.plugchoice.com/v3/chargers/{charger}/transactions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source\": \"<string>\",\n \"transactions\": [\n {\n \"connector_id\": 123,\n \"id_tag\": \"<string>\",\n \"meter_start\": 123,\n \"meter_stop\": 123,\n \"started_at\": \"<string>\",\n \"stopped_at\": \"<string>\",\n \"stop_reason\": \"<string>\",\n \"reservation_id\": 123\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"message": "<string>",
"created": 123,
"skipped": 123,
"failed": 123,
"errors": [
{
"index": 123,
"errors": {}
}
],
"transactions": [
{}
]
}Records charging sessions that happened outside the OCPP flow — for example sessions
captured by the Plugchoice app, a Peblar local API, or a backend you are migrating away
from. Every transaction created here is flagged with
imported: true and stamped with the
source you supply, so imported sessions stay distinguishable from ones reported by the
charger itself.
The payload is always a list. Importing a single session means sending a list of one.
Rows are judged individually: valid ones are created, duplicates are skipped, and anything
else is reported against its position in the list. One bad row never discards the rest of
the batch.
Only finished sessions can be imported — stopped_at and meter_stop are required.
string
required
The UUID of the charger the sessions belong to.
string
required
Where these sessions came from, e.g.
Plugchoice app or peblar local. Free text, up to
100 characters. Stamped onto every transaction in the batch.object[]
required
The sessions to import. Between 1 and 500 items.
Show transactions
Show transactions
integer
required
The OCPP connector id on this charger. The connector must already exist — importing
does not create hardware.
string
required
The RFID tag or token that authorized the session. Maximum 20 characters.
integer
required
The meter reading at the start of the session, in Wh.
integer
required
The meter reading at the end of the session, in Wh. Must be greater than or equal to
meter_start. Energy is always expressed as meter readings — total_kwh is derived
from them and cannot be sent.string
required
ISO 8601 timestamp of when the session started. Cannot be in the future.
string
required
ISO 8601 timestamp of when the session stopped. Must be at or after
started_at, and
cannot be in the future.string
Why the session ended. One of
DeAuthorized, EmergencyStop, EVDisconnected,
HardReset, Local, Other, PowerLoss, Reboot, Remote, SoftReset, or
UnlockCommand. May be omitted.integer
The reservation id, if the session was tied to one.
Duplicates
A session is treated as a duplicate when the charger, connector andstarted_at all match
one that already exists — whether it was imported earlier or reported live by the charger.
Duplicates are counted in skipped rather than rejected, so re-sending a batch is safe and
will not double-count energy.
Response
string
Always
Import completed.integer
How many transactions were created.
integer
How many rows were skipped as duplicates.
integer
How many rows failed validation.
object[]
One entry for every row that was not created — both failures and skipped duplicates.
object[]
The transactions that were created, in the order they were sent. Each one has the same
shape as a transaction returned by List Transactions,
including the generated
total_kwh and the assigned id.⌘I