Introduction
API REST di SunPilot: anagrafica impianti fotovoltaici e telemetria canonica, vendor-agnostic.
L'API v1 di SunPilot espone anagrafica impianti fotovoltaici e telemetria canonica del tuo tenant, qualunque sia il vendor dell'hardware in campo.
Base URL: https://sunpilot.cloud/api/v1 · Versioning: il prefisso /v1 è il contratto; le evoluzioni incompatibili arriveranno come /v2.
Convenzioni:
- Tutti i timestamp sono ISO 8601 in UTC (
2026-07-09T12:00:00.000000Z). - Ogni token opera nel tenant del client: vedi e scrivi solo i dati della tua organizzazione.
- Le liste sono paginate (parametro
per_page, default e massimi indicati per endpoint) con il formato standard Laravel:data[],links,meta. - Gli errori usano i codici HTTP standard:
401token mancante o scaduto,403scope o ruolo insufficiente,404risorsa inesistente o fuori dal tuo tenant,422validazione fallita (corpo{"message": "...", "errors": {campo: [messaggi]}}),429rate limit superato.
Authenticating requests
To authenticate requests, include an Authorization header with the value "Bearer {ACCESS_TOKEN}".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
Le credenziali OAuth2 (client id/secret) si ottengono su richiesta scrivendo a info@sunpilot.cloud. Ottenute le credenziali, il flusso è authorization code + PKCE: autorizzazione su /oauth/authorize, scambio del codice su /oauth/token, refresh su /oauth/token/refresh. Scope disponibili: read (lettura di anagrafica e telemetria) e write (scrittura di anagrafica; le route di scrittura richiedono anche un ruolo abilitato alla scrittura nel tenant). Lo scope command è riservato per i futuri comandi verso i device e oggi non è consumato da nessun endpoint.
Autenticazione
Issue an access token.
requires authentication
Example request:
curl --request POST \
"https://sunpilot.cloud/oauth/token" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/oauth/token';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/oauth/token"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Authorize a client to access the user's account.
requires authentication
Get a fresh transient token cookie for the authenticated user.
requires authentication
Example request:
curl --request POST \
"https://sunpilot.cloud/oauth/token/refresh" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/oauth/token/refresh';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/oauth/token/refresh"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Identità
Chi sono (senza tenant)
requires authentication
A differenza di GET /me, non richiede un tenant risolto: è l'endpoint
da chiamare subito dopo l'auth, prima ancora di scegliere il tenant.
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/whoami" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/whoami';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/whoami"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Chi sono
requires authentication
Restituisce l'utente autenticato, il tenant in cui opera il token e il suo ruolo. Utile come primo endpoint di verifica dopo aver ottenuto un access token.
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/me" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/me';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/me"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"user": {
"id": 1,
"name": "Mario Rossi",
"email": "mario@installatore.it"
},
"tenant": {
"id": 1
},
"role": "owner"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Impianti
Gli impianti (sites) sono l'anagrafica di base: ogni device, misura e KPI appartiene a un impianto del tuo tenant.
Elenca gli impianti
requires authentication
Impianti del tenant, paginati, con stato di collegamento cloud e ultima misura.
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/sites?per_page=25" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/sites';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'per_page' => '25',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/sites"
);
const params = {
"per_page": "25",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": 1,
"name": "Capannone Rossi",
"timezone": "Europe/Rome",
"cloud_connected": true,
"cloud_links": [
{
"id": "0198f2b0-1a2b-73e0-8b1c-8f6c2e2b9a10",
"provider": "fusionsolar",
"label": null,
"station_code": "NE=12345678",
"last_synced_at": "2026-07-09T11:45:00.000000Z",
"backfill_status": null,
"backfill_result": null,
"backfill_error": null
}
],
"online": true,
"last_seen_at": "2026-07-09T11:45:12.000000Z",
"last_measurement_at": "2026-07-09T11:00:00.000000Z",
"latitude": 45.4642,
"longitude": 9.19,
"address": "Via Baldini 12, Milano",
"peak_power_kwp": 60,
"tilt": 10,
"azimuth": 0,
"has_pv_geometry": true,
"diagnostics_severity": null,
"diagnostics_computed_at": null
}
],
"links": {
"first": "https://sunpilot.cloud/api/v1/sites?page=1",
"last": "https://sunpilot.cloud/api/v1/sites?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"per_page": 25,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Dettaglio impianto
requires authentication
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/sites/1" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/sites/1';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/sites/1"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"id": 1,
"name": "Capannone Rossi",
"timezone": "Europe/Rome",
"cloud_connected": true,
"cloud_links": [
{
"id": "0198f2b0-1a2b-73e0-8b1c-8f6c2e2b9a10",
"provider": "fusionsolar",
"label": null,
"station_code": "NE=12345678",
"last_synced_at": "2026-07-09T11:45:00.000000Z",
"backfill_status": null,
"backfill_result": null,
"backfill_error": null
}
],
"online": true,
"last_seen_at": "2026-07-09T11:45:12.000000Z",
"last_measurement_at": "2026-07-09T11:00:00.000000Z",
"latitude": 45.4642,
"longitude": 9.19,
"address": "Via Baldini 12, Milano",
"peak_power_kwp": 60,
"tilt": 10,
"azimuth": 0,
"has_pv_geometry": true,
"diagnostics_severity": null,
"diagnostics_computed_at": null
}
}
Example response (404):
{
"message": "Not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Dettaglio impianto (aggregato)
requires authentication
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/sites/1/detail" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/sites/1/detail';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/sites/1/detail"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Elenco dispositivi del sito
requires authentication
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/sites/architecto/devices" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/sites/architecto/devices';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/sites/architecto/devices"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Dettaglio dispositivo
requires authentication
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/devices/architecto" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/devices/architecto';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/devices/architecto"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Crea un impianto
requires authentication
Richiede scope write e un ruolo abilitato alla scrittura. Se il piano del
tenant ha raggiunto il limite di impianti risponde 402 con error: plan_limit.
Example request:
curl --request POST \
"https://sunpilot.cloud/api/v1/sites" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"name\": \"Capannone Rossi\",
\"customer_label\": \"n\",
\"timezone\": \"Europe\\/Rome\",
\"address\": \"Via Roma 1, Milano\",
\"latitude\": -90,
\"longitude\": -180,
\"peak_power_kwp\": 6.5,
\"battery_capacity_kwh\": 1,
\"commissioned_date\": \"2024-05-10\"
}"
$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/sites';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'name' => 'Capannone Rossi',
'customer_label' => 'n',
'timezone' => 'Europe/Rome',
'address' => 'Via Roma 1, Milano',
'latitude' => -90,
'longitude' => -180,
'peak_power_kwp' => 6.5,
'battery_capacity_kwh' => 1,
'commissioned_date' => '2024-05-10',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/sites"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Capannone Rossi",
"customer_label": "n",
"timezone": "Europe\/Rome",
"address": "Via Roma 1, Milano",
"latitude": -90,
"longitude": -180,
"peak_power_kwp": 6.5,
"battery_capacity_kwh": 1,
"commissioned_date": "2024-05-10"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"id": 2,
"name": "Capannone Rossi",
"timezone": "Europe/Rome",
"cloud_connected": false,
"cloud_links": [],
"online": false,
"last_seen_at": null,
"last_measurement_at": null,
"latitude": null,
"longitude": null,
"address": null,
"peak_power_kwp": null,
"tilt": null,
"azimuth": null,
"has_pv_geometry": false
}
}
Example response (402):
{
"error": "plan_limit",
"message": "Hai raggiunto il limite di impianti del piano Free. Passa a un piano superiore per aggiungerne altri."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Telemetria
KPI del sito
requires authentication
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/sites/architecto/kpis" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/sites/architecto/kpis';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/sites/architecto/kpis"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Serie energetica del sito
requires authentication
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/sites/architecto/energy-series" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/sites/architecto/energy-series';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/sites/architecto/energy-series"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Serie produzione del sito
requires authentication
Serie produzione filtrabile (giorno/settimana/mese) atteso-vs-reale. Finestra di default per granularità; from/to opzionali (Y-m-d).
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/sites/architecto/production-series" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/sites/architecto/production-series';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/sites/architecto/production-series"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Misure di un device
requires authentication
Serie temporale delle misure canoniche di un device, dalla più recente. Ogni misura è la "scheda di lettura" SunPilot: metrica, valore, unità, fase, qualità, timestamp UTC — identica per qualunque vendor.
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/devices/1/measurements?metric=pv_production_power&phase=total&from=2026-07-08T00%3A00%3A00Z&to=2026-07-09T00%3A00%3A00Z&per_page=100" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/devices/1/measurements';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'metric' => 'pv_production_power',
'phase' => 'total',
'from' => '2026-07-08T00:00:00Z',
'to' => '2026-07-09T00:00:00Z',
'per_page' => '100',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/devices/1/measurements"
);
const params = {
"metric": "pv_production_power",
"phase": "total",
"from": "2026-07-08T00:00:00Z",
"to": "2026-07-09T00:00:00Z",
"per_page": "100",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"metric": "pv_production_power",
"value": 41800,
"unit": "W",
"sampling": "instantaneous",
"phase": "total",
"quality": "measured",
"timestamp_utc": "2026-07-09T11:00:00.000000Z",
"source_driver": "fusionsolar"
}
],
"links": {
"first": "https://sunpilot.cloud/api/v1/devices/1/measurements?page=1",
"last": "https://sunpilot.cloud/api/v1/devices/1/measurements?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"per_page": 50,
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Allarmi
Allarmi canonici (ADR-027): stato corrente del parco, popolato da
sunpilot:scan-alarms. Diverso da GET /alarms (regola "device
scollegato" a 30 minuti, non persistita) — qui la soglia è la
staleness canonica (6h/24h).
Allarmi live
requires authentication
Regola "device scollegato" a 30 minuti, calcolata live e non persistita.
Diversa da GET /alarms/canonical, che usa la soglia di staleness
canonica (6h/24h) sullo stato persistito.
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/alarms" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/alarms';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/alarms"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Elenca gli allarmi attivi
requires authentication
Solo allarmi correnti (non ancora rientrati), ordinati per severità decrescente (critical prima di warning prima di info).
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/alarms/canonical" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/alarms/canonical';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/alarms/canonical"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": "9c3b1e2a-...",
"site_id": "7a1f...",
"site_name": "Villa Bianchi",
"device_id": "4b2e...",
"device_name": "Shelly Pro 3EM",
"code": "device_offline",
"severity": "warning",
"status": "raised",
"message": "Nessuna misura da 9h",
"raised_at": "2026-07-13T08:00:00.000000Z",
"last_seen_at": "2026-07-13T11:00:00.000000Z"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Conferma presa in carico allarme
requires authentication
Example request:
curl --request POST \
"https://sunpilot.cloud/api/v1/alarms/architecto/acknowledge" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"note\": \"b\",
\"maintenance_log_id\": \"a4855dc5-0acb-33c3-b921-f4291f719ca0\"
}"
$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/alarms/architecto/acknowledge';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'note' => 'b',
'maintenance_log_id' => 'a4855dc5-0acb-33c3-b921-f4291f719ca0',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/alarms/architecto/acknowledge"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"note": "b",
"maintenance_log_id": "a4855dc5-0acb-33c3-b921-f4291f719ca0"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Risolvi allarme
requires authentication
Example request:
curl --request POST \
"https://sunpilot.cloud/api/v1/alarms/architecto/resolve" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"note\": \"b\",
\"maintenance_log_id\": \"a4855dc5-0acb-33c3-b921-f4291f719ca0\"
}"
$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/alarms/architecto/resolve';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'note' => 'b',
'maintenance_log_id' => 'a4855dc5-0acb-33c3-b921-f4291f719ca0',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/alarms/architecto/resolve"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"note": "b",
"maintenance_log_id": "a4855dc5-0acb-33c3-b921-f4291f719ca0"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Diagnostica
Dashboard diagnostica di portfolio: triage a scala su migliaia di impianti, con filtri, ordinamento e aggregati calcolati lato SQL sui campi persistiti da ScanDiagnosticsForSite (nessuna fetch live di PlantDiagnostics::analyze() per riga — vedi design doc 2026-07-24-diagnostica-portfolio-dashboard-design.md).
Elenca gli impianti con la loro diagnostica precalcolata
requires authentication
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/diagnostics?severity=critical&search=Rossi&sort=savings&per_page=25" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/diagnostics';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'severity' => 'critical',
'search' => 'Rossi',
'sort' => 'savings',
'per_page' => '25',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/diagnostics"
);
const params = {
"severity": "critical",
"search": "Rossi",
"sort": "savings",
"per_page": "25",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Diagnostica del sito
requires authentication
Diagnostica installatore di un impianto: performance vs atteso, autoconsumo/ autosufficienza, dimensionamento FV/batteria + upsell, anomalie, risparmio/CO2.
È una vista DERIVATA su una finestra di 1 anno (aggrega decine di migliaia di righe): pesante da ricalcolare a ogni apertura e non real-time. La cachiamo per un'ora — le misure nuove si riflettono entro la finestra, come per la cache PVGIS. Chiave per-sito (id UUID globale, nessuna collisione cross-tenant).
Example request:
curl --request GET \
--get "https://sunpilot.cloud/api/v1/sites/architecto/diagnostics" \
--header "Authorization: Bearer {ACCESS_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"$client = new \GuzzleHttp\Client();
$url = 'https://sunpilot.cloud/api/v1/sites/architecto/diagnostics';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {ACCESS_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));const url = new URL(
"https://sunpilot.cloud/api/v1/sites/architecto/diagnostics"
);
const headers = {
"Authorization": "Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.