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>"
}/onboard-site-and-asset?
It’s still supported, but we’d recommend migrating to /onboard when convenient.Handling failures
A200 response always means the site and assets were stored and the site is enrolled.
enrolment.status only tells you whether the enrolment was new (enrolled) or already in
place (already_enrolled).
An unsuccessful onboarding returns an HTTP error instead. The call is atomic, so on any error
nothing is persisted: no site, no assets, no enrolment. Fix the cause and send the same request
again.
| Status | Meaning | What to do |
|---|---|---|
400 | Invalid request: missing gave_boundary_meter_consent_at, an invalid email, or no boundary consent terms registered for your organisation. detail is a plain message. | Correct the request. If the message mentions terms and conditions, contact Axle. |
404 | A referenced entity (such as an asset_model) doesn’t exist or isn’t visible to your organisation. | Check the identifiers. |
409 | An asset with this external_id already exists. detail.message says why and detail.existing_asset_id identifies it. | Registered to a different site: offboard the old site first, or use a different external_id. Different type, or owned by another organisation: contact Axle. |
422 with detail.eligibility | The site or assets don’t meet the proposition’s prerequisites. detail.eligibility holds the per-site and per-asset breakdown. | Resolve the missing prerequisite, or use check eligibility up front. |
422 with detail.code: "previously_withdrew" | The site previously withdrew from this proposition, for example via unenrol. | Resend with override_withdrawal: true. Recommended whenever the user asked to re-enrol. |
500 | Something went wrong on our side. | Retry; if it persists, contact Axle. |
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>"
}
