Onboard
Initialise a site and its assets, and enrol them in a proposition in a single call.
This endpoint will:
- Upsert the site and asset(s)
- Enrol in the relevant flex proposition
The response returns the site and asset IDs you’ll use in subsequent calls, and the enrolment outcome.
Onboarding is idempotent and atomic — sending the same site or asset returns the existing record with any new fields merged in; if enrolment fails, nothing is stored.
A 200 response always means the site and assets exist and the site is enrolled; enrolment.status only distinguishes a new enrolment from an existing one. Any failure returns an HTTP error with nothing persisted, so fix the cause and send the same request again.
curl --request POST \
--url https://api-sandbox.axle.energy/entities/site/onboard \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"site": {
"email": "resident@example.com",
"gave_boundary_meter_consent_at": "2026-01-15T10:30:00Z",
"mpan": "1234567890123",
"postcode": "SW1A 1AA",
"street_address": "10 Downing Street"
},
"assets": [
{
"asset_model": "charger-model",
"external_id": "charger-001",
"installation_date": "2025-06-15",
"properties": {
"power_kw": 7.4
},
"type": "charger"
}
],
"proposition": "limited_pause",
"override_withdrawal": false
}
'import requests
url = "https://api-sandbox.axle.energy/entities/site/onboard"
payload = {
"site": {
"email": "resident@example.com",
"gave_boundary_meter_consent_at": "2026-01-15T10:30:00Z",
"mpan": "1234567890123",
"postcode": "SW1A 1AA",
"street_address": "10 Downing Street"
},
"assets": [
{
"asset_model": "charger-model",
"external_id": "charger-001",
"installation_date": "2025-06-15",
"properties": { "power_kw": 7.4 },
"type": "charger"
}
],
"proposition": "limited_pause",
"override_withdrawal": False
}
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({
site: {
email: 'resident@example.com',
gave_boundary_meter_consent_at: '2026-01-15T10:30:00Z',
mpan: '1234567890123',
postcode: 'SW1A 1AA',
street_address: '10 Downing Street'
},
assets: [
{
asset_model: 'charger-model',
external_id: 'charger-001',
installation_date: '2025-06-15',
properties: {power_kw: 7.4},
type: 'charger'
}
],
proposition: 'limited_pause',
override_withdrawal: false
})
};
fetch('https://api-sandbox.axle.energy/entities/site/onboard', 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-sandbox.axle.energy/entities/site/onboard",
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([
'site' => [
'email' => 'resident@example.com',
'gave_boundary_meter_consent_at' => '2026-01-15T10:30:00Z',
'mpan' => '1234567890123',
'postcode' => 'SW1A 1AA',
'street_address' => '10 Downing Street'
],
'assets' => [
[
'asset_model' => 'charger-model',
'external_id' => 'charger-001',
'installation_date' => '2025-06-15',
'properties' => [
'power_kw' => 7.4
],
'type' => 'charger'
]
],
'proposition' => 'limited_pause',
'override_withdrawal' => false
]),
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-sandbox.axle.energy/entities/site/onboard"
payload := strings.NewReader("{\n \"site\": {\n \"email\": \"resident@example.com\",\n \"gave_boundary_meter_consent_at\": \"2026-01-15T10:30:00Z\",\n \"mpan\": \"1234567890123\",\n \"postcode\": \"SW1A 1AA\",\n \"street_address\": \"10 Downing Street\"\n },\n \"assets\": [\n {\n \"asset_model\": \"charger-model\",\n \"external_id\": \"charger-001\",\n \"installation_date\": \"2025-06-15\",\n \"properties\": {\n \"power_kw\": 7.4\n },\n \"type\": \"charger\"\n }\n ],\n \"proposition\": \"limited_pause\",\n \"override_withdrawal\": false\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-sandbox.axle.energy/entities/site/onboard")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"site\": {\n \"email\": \"resident@example.com\",\n \"gave_boundary_meter_consent_at\": \"2026-01-15T10:30:00Z\",\n \"mpan\": \"1234567890123\",\n \"postcode\": \"SW1A 1AA\",\n \"street_address\": \"10 Downing Street\"\n },\n \"assets\": [\n {\n \"asset_model\": \"charger-model\",\n \"external_id\": \"charger-001\",\n \"installation_date\": \"2025-06-15\",\n \"properties\": {\n \"power_kw\": 7.4\n },\n \"type\": \"charger\"\n }\n ],\n \"proposition\": \"limited_pause\",\n \"override_withdrawal\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-sandbox.axle.energy/entities/site/onboard")
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 \"site\": {\n \"email\": \"resident@example.com\",\n \"gave_boundary_meter_consent_at\": \"2026-01-15T10:30:00Z\",\n \"mpan\": \"1234567890123\",\n \"postcode\": \"SW1A 1AA\",\n \"street_address\": \"10 Downing Street\"\n },\n \"assets\": [\n {\n \"asset_model\": \"charger-model\",\n \"external_id\": \"charger-001\",\n \"installation_date\": \"2025-06-15\",\n \"properties\": {\n \"power_kw\": 7.4\n },\n \"type\": \"charger\"\n }\n ],\n \"proposition\": \"limited_pause\",\n \"override_withdrawal\": false\n}"
response = http.request(request)
puts response.read_body{
"site": {
"site_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"site_created": true,
"fields_updated": [],
"warnings": []
},
"assets": [
{
"asset_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"external_id": "charger-001",
"asset_created": true,
"fields_updated": [],
"warnings": []
}
],
"enrolment": {
"site_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "enrolled"
}
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": {
"message": "Asset with external_id charger-001 already exists with id b2c3d4e5-f6a7-8901-bcde-f23456789012. Asset charger-001 is registered to a different site",
"existing_asset_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"external_id": "charger-001"
}
}{
"detail": {
"message": "Prerequisites not met for proposition: limited_pause",
"code": "previously_withdrew",
"eligibility": {
"site_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"site_properties": {
"has_meter_consent": true,
"is_half_hourly_settled": false
},
"eligible_propositions": [],
"assets": []
}
}
}{
"detail": "<string>"
}Supplying site.email
A user email is required to process payments through our provider, Stripe. We also send payment-failure notifications and, if enabled, two-factor authentication codes to this address. See the Payments overview for details.Authorizations
The access token received from the authorization server in the OAuth 2.0 flow.
Body
Request model for the onboard endpoint.
Site information
Show child attributes
Show child attributes
{
"email": "resident@example.com",
"gave_boundary_meter_consent_at": "2026-01-15T10:30:00Z",
"mpan": "1234567890123",
"postcode": "SW1A 1AA",
"street_address": "10 Downing Street"
}
One or more assets to register at the site
Show child attributes
Show child attributes
[
{
"asset_model": "charger-model",
"external_id": "charger-001",
"installation_date": "2025-06-15",
"properties": { "power_kw": 7.4 },
"type": "charger"
}
]
Proposition to enrol the site in
limited_pause, full_asset_schedule_control, price_curve "limited_pause"
Set to true to re-enrol regardless of previous withdrawals.
false
Response
Site and assets persisted, site enrolled in proposition
Was this page helpful?
curl --request POST \
--url https://api-sandbox.axle.energy/entities/site/onboard \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"site": {
"email": "resident@example.com",
"gave_boundary_meter_consent_at": "2026-01-15T10:30:00Z",
"mpan": "1234567890123",
"postcode": "SW1A 1AA",
"street_address": "10 Downing Street"
},
"assets": [
{
"asset_model": "charger-model",
"external_id": "charger-001",
"installation_date": "2025-06-15",
"properties": {
"power_kw": 7.4
},
"type": "charger"
}
],
"proposition": "limited_pause",
"override_withdrawal": false
}
'import requests
url = "https://api-sandbox.axle.energy/entities/site/onboard"
payload = {
"site": {
"email": "resident@example.com",
"gave_boundary_meter_consent_at": "2026-01-15T10:30:00Z",
"mpan": "1234567890123",
"postcode": "SW1A 1AA",
"street_address": "10 Downing Street"
},
"assets": [
{
"asset_model": "charger-model",
"external_id": "charger-001",
"installation_date": "2025-06-15",
"properties": { "power_kw": 7.4 },
"type": "charger"
}
],
"proposition": "limited_pause",
"override_withdrawal": False
}
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({
site: {
email: 'resident@example.com',
gave_boundary_meter_consent_at: '2026-01-15T10:30:00Z',
mpan: '1234567890123',
postcode: 'SW1A 1AA',
street_address: '10 Downing Street'
},
assets: [
{
asset_model: 'charger-model',
external_id: 'charger-001',
installation_date: '2025-06-15',
properties: {power_kw: 7.4},
type: 'charger'
}
],
proposition: 'limited_pause',
override_withdrawal: false
})
};
fetch('https://api-sandbox.axle.energy/entities/site/onboard', 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-sandbox.axle.energy/entities/site/onboard",
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([
'site' => [
'email' => 'resident@example.com',
'gave_boundary_meter_consent_at' => '2026-01-15T10:30:00Z',
'mpan' => '1234567890123',
'postcode' => 'SW1A 1AA',
'street_address' => '10 Downing Street'
],
'assets' => [
[
'asset_model' => 'charger-model',
'external_id' => 'charger-001',
'installation_date' => '2025-06-15',
'properties' => [
'power_kw' => 7.4
],
'type' => 'charger'
]
],
'proposition' => 'limited_pause',
'override_withdrawal' => false
]),
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-sandbox.axle.energy/entities/site/onboard"
payload := strings.NewReader("{\n \"site\": {\n \"email\": \"resident@example.com\",\n \"gave_boundary_meter_consent_at\": \"2026-01-15T10:30:00Z\",\n \"mpan\": \"1234567890123\",\n \"postcode\": \"SW1A 1AA\",\n \"street_address\": \"10 Downing Street\"\n },\n \"assets\": [\n {\n \"asset_model\": \"charger-model\",\n \"external_id\": \"charger-001\",\n \"installation_date\": \"2025-06-15\",\n \"properties\": {\n \"power_kw\": 7.4\n },\n \"type\": \"charger\"\n }\n ],\n \"proposition\": \"limited_pause\",\n \"override_withdrawal\": false\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-sandbox.axle.energy/entities/site/onboard")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"site\": {\n \"email\": \"resident@example.com\",\n \"gave_boundary_meter_consent_at\": \"2026-01-15T10:30:00Z\",\n \"mpan\": \"1234567890123\",\n \"postcode\": \"SW1A 1AA\",\n \"street_address\": \"10 Downing Street\"\n },\n \"assets\": [\n {\n \"asset_model\": \"charger-model\",\n \"external_id\": \"charger-001\",\n \"installation_date\": \"2025-06-15\",\n \"properties\": {\n \"power_kw\": 7.4\n },\n \"type\": \"charger\"\n }\n ],\n \"proposition\": \"limited_pause\",\n \"override_withdrawal\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-sandbox.axle.energy/entities/site/onboard")
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 \"site\": {\n \"email\": \"resident@example.com\",\n \"gave_boundary_meter_consent_at\": \"2026-01-15T10:30:00Z\",\n \"mpan\": \"1234567890123\",\n \"postcode\": \"SW1A 1AA\",\n \"street_address\": \"10 Downing Street\"\n },\n \"assets\": [\n {\n \"asset_model\": \"charger-model\",\n \"external_id\": \"charger-001\",\n \"installation_date\": \"2025-06-15\",\n \"properties\": {\n \"power_kw\": 7.4\n },\n \"type\": \"charger\"\n }\n ],\n \"proposition\": \"limited_pause\",\n \"override_withdrawal\": false\n}"
response = http.request(request)
puts response.read_body{
"site": {
"site_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"site_created": true,
"fields_updated": [],
"warnings": []
},
"assets": [
{
"asset_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"external_id": "charger-001",
"asset_created": true,
"fields_updated": [],
"warnings": []
}
],
"enrolment": {
"site_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "enrolled"
}
}{
"detail": "<string>"
}{
"detail": "<string>"
}{
"detail": {
"message": "Asset with external_id charger-001 already exists with id b2c3d4e5-f6a7-8901-bcde-f23456789012. Asset charger-001 is registered to a different site",
"existing_asset_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"external_id": "charger-001"
}
}{
"detail": {
"message": "Prerequisites not met for proposition: limited_pause",
"code": "previously_withdrew",
"eligibility": {
"site_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"site_properties": {
"has_meter_consent": true,
"is_half_hourly_settled": false
},
"eligible_propositions": [],
"assets": []
}
}
}{
"detail": "<string>"
}
