In breve
| Indirizzo | https://<il-tuo-server>:8443/api/v1 |
| Specifica | GET /api/v1/openapi.json (OpenAPI 3.1, pubblica) e nel pannello: Chiavi API › Documentazione API |
| Autenticazione | Authorization: Bearer kpk_… — chiave API personale di un amministratore o di un rivenditore |
| Formato | JSON, UTF-8 |
| Errori | {"error":{"code":"…","message":"…","field":"…","requestId":"…"}} |
| Paginazione | ?limit= (1–500) e ?cursor=; la risposta contiene nextCursor finché ci sono altre pagine |
| Idempotenza | intestazione Idempotency-Key su ogni POST, 24 ore |
| Limiti | 300 richieste al minuto per chiave, intestazioni X-RateLimit-* |
| Webhook | eventi firmati HMAC-SHA256, ripetuti fino a 5 volte |
| Compatibilità | dentro /api/v1 campi e operazioni vengono solo aggiunti, mai tolti o cambiati |
La chiave API
Nel pannello, come amministratore o rivenditore: Chiavi API › Crea chiave. La chiave
(kpk_…) si vede una sola volta e agisce come il suo account:
- la chiave di un rivenditore vede e modifica solo i propri clienti e pacchetti, entro la sua quota;
- per creare rivenditori serve la chiave di un amministratore;
- se l'account viene sospeso o eliminato, le sue chiavi smettono di funzionare;
- ogni operazione compare nel Registro attività con il nome della chiave.
Cosa si può fare
| Area | Operazioni |
|---|---|
| Identità | GET /auth/me, POST /auth/login-link |
| Account di hosting | elenco, creazione, lettura, modifica (email, pacchetto), eliminazione, sospensione, riattivazione, password, link di accesso monouso |
| Rivenditori | elenco, creazione con quota, modifica della quota |
| Pacchetti | elenco, creazione, modifica, eliminazione |
| Utilizzo | spazio usato per account |
| Siti | elenco, creazione, lettura, PHP e domini aggiuntivi, eliminazione |
| Database | elenco, creazione, utenti, password, eliminazione |
| Posta | domini di posta, caselle (creazione, quota, inoltri, password, eliminazione) |
| Webhook | registrazione, modifica, prova, consegne, eliminazione |
L'elenco completo con tutti i campi è nel documento OpenAPI del server.
Esempi
curl
export KP=https://server1.example.com:8443/api/v1
export KEY=kpk_... # Chiavi API › Crea chiave
# un account con la password scelta dal tuo sistema, sicuro da ripetere
curl -sS -X POST "$KP/accounts" \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: order-10452" \
-d '{"username":"mario","email":"mario@example.com","package":"pkg_base","passwordMode":"set","password":"Una-Password-Robusta-2026"}'
# sospensione per fattura scaduta, poi riattivazione
curl -sS -X POST "$KP/accounts/mario/suspend" -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d '{"reason":"Fattura 2026/118 scaduta"}'
curl -sS -X POST "$KP/accounts/mario/unsuspend" -H "Authorization: Bearer $KEY"
# link di accesso monouso (60 secondi) che apre il file manager
curl -sS -X POST "$KP/accounts/mario/login-link" -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d '{"next":"/files"}'
PHP
<?php
$base = 'https://server1.example.com:8443/api/v1';
$key = getenv('KOAPANEL_API_KEY');
function kp(string $method, string $path, ?array $body = null, array $headers = []): array {
global $base, $key;
$ch = curl_init($base . $path);
$h = ['Authorization: Bearer ' . $key, 'Accept: application/json'];
foreach ($headers as $k => $v) { $h[] = "$k: $v"; }
if ($body !== null) { $h[] = 'Content-Type: application/json'; curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body)); }
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => $method, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $h, CURLOPT_TIMEOUT => 30]);
$raw = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$data = $raw === '' ? [] : json_decode($raw, true);
if ($code >= 400) { throw new RuntimeException($data['error']['message'] ?? "HTTP $code", $code); }
return $data ?? [];
}
// tutti gli account, pagina per pagina
$cursor = null;
do {
$page = kp('GET', '/accounts?limit=100' . ($cursor ? '&cursor=' . urlencode($cursor) : ''));
foreach ($page['accounts'] as $a) { echo $a['username'], ' ', $a['usage']['diskBytes'], "\n"; }
$cursor = $page['nextCursor'] ?? null;
} while ($cursor);
// un database per il sito del cliente
$db = kp('POST', '/databases', ['name' => 'mario_shop', 'site' => 'mario.example.com'], ['Idempotency-Key' => 'order-10452-db']);
echo $db['credentials']['user'], ' / ', $db['credentials']['password'], "\n";
Python
import os, requests
BASE = "https://server1.example.com:8443/api/v1"
S = requests.Session()
S.headers["Authorization"] = "Bearer " + os.environ["KOAPANEL_API_KEY"]
def kp(method, path, **kw):
r = S.request(method, BASE + path, timeout=30, **kw)
if r.status_code == 429:
raise RuntimeError(f"rate limit, retry in {r.headers['Retry-After']} s")
if r.status_code >= 400:
raise RuntimeError(r.json()["error"]["message"])
return r.json() if r.content else None
site = kp("POST", "/sites", json={"domain": "mario.example.com", "owner": "mario"},
headers={"Idempotency-Key": "order-10452-site"})
box = kp("POST", "/mailserver/domains/mario.example.com/mailboxes", json={"local": "info", "quotaMB": 2048})
print(box["address"], box.get("password")) # la password generata si vede una volta
Webhook
Registra l'indirizzo del tuo sistema (Chiavi API › Webhook, oppure POST /webhooks) e
ricevi una richiesta POST a ogni evento:
| Evento | Quando |
|---|---|
account.created / account.updated / account.deleted |
account creato, modificato (email, pacchetto), eliminato |
account.suspended / account.unsuspended |
sospensione e riattivazione |
account.usage_over_quota |
lo spazio usato raggiunge il limite del pacchetto (una volta per superamento) |
reseller.created |
nuovo rivenditore |
site.created / site.deleted |
sito creato o eliminato |
ping |
prova dal pannello |
Il corpo è {"id":"evt_…","type":"account.suspended","createdAt":"…","data":{…}} e le
intestazioni X-Koapanel-Event, X-Koapanel-Delivery e
X-Koapanel-Signature: t=<secondi>,v1=<firma>, dove la firma è l'HMAC-SHA256 esadecimale
di "<t>.<corpo>" con il segreto del webhook (mostrato una sola volta alla creazione).
Rispondi 2xx entro 10 secondi; altrimenti il pannello riprova dopo 10 s, 1 min, 5 min,
30 min e 2 h. Gli amministratori ricevono tutti gli eventi, i rivenditori solo quelli dei
propri clienti (e solo verso indirizzi https:// pubblici).
Verifica in PHP:
$body = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_KOAPANEL_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $sig), $p); // t=…, v1=…
$ok = isset($p['t'], $p['v1']) && abs(time() - (int)$p['t']) < 300
&& hash_equals(hash_hmac('sha256', $p['t'] . '.' . $body, getenv('KOAPANEL_WEBHOOK_SECRET')), $p['v1']);
if (!$ok) { http_response_code(400); exit; }
$event = json_decode($body, true);
Verifica in Python:
import hmac, hashlib, time
def verify(secret: str, header: str, body: bytes, tolerance=300) -> bool:
parts = dict(x.split("=", 1) for x in header.split(","))
if abs(time.time() - int(parts.get("t", 0))) > tolerance:
return False
mac = hmac.new(secret.encode(), parts["t"].encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, parts.get("v1", ""))
Moduli pronti
- WHMCS 8.x e 9.x: modulo ufficiale (account, rivenditori, accesso con un clic, licenze per i partner) — vedi la pagina «Integrazione WHMCS».
- In programma: moduli per Blesta e HostBill, e FOSSBilling. Nel frattempo questi sistemi possono usare l'API qui sopra (le operazioni sono le stesse del modulo WHMCS).
Partner API: rivendere licenze KoaPanel
Per i provider che vendono licenze KoaPanel ai propri clienti, con il modulo WHMCS
koapanel_license o con qualsiasi altro sistema di fatturazione. Non è l'API del
pannello: è l'API della console di KoaPanel, con una chiave partner che ti diamo noi.
| Indirizzo | https://console.koapanel.app/api/v1/partner |
| Specifica | /api/v1/partner/openapi.json (OpenAPI 3.1, pubblica) |
| Autenticazione | Authorization: Bearer kpp_… — chiave partner, mostrata una sola volta; sostituzione con 24 ore di sovrapposizione |
| Errori | {"error":{"code":"…","message":"…"}}, messaggi in italiano |
| Idempotenza | Idempotency-Key sulla creazione (7 giorni); externalId unico tra le licenze non revocate |
| Limiti | 120 richieste al minuto per chiave |
| Registro | ogni operazione è registrata con il partner, la chiave e il client (X-Panel-Client) |
| Operazione | Richiesta |
|---|---|
| Partner e piani abilitati | GET /me, GET /plans |
| Elenco licenze | GET /licenses?externalId=&status=&page= |
| Crea una licenza | POST /licenses con plan, externalId, customer (nome, email, Paese) |
| Una licenza con il codice di attivazione | GET /licenses/{id} |
| Sospendi, riattiva, revoca | POST /licenses/{id}/suspend, /reactivate (o /unsuspend), /revoke (o /terminate) |
| Cambia piano | POST /licenses/{id}/plan con plan |
| Libera dal server (per spostarla) | POST /licenses/{id}/release — al massimo 3 volte in 30 giorni |
| Nuovo codice | POST /licenses/{id}/reissue |
| Consumo del mese, rendiconti | GET /usage?month=AAAA-MM, GET /statements, GET /statements/{mese} |
export KPP=kpp_... # la chiave partner
curl -sS -X POST https://console.koapanel.app/api/v1/partner/licenses \
-H "Authorization: Bearer $KPP" -H "Content-Type: application/json" \
-H "Idempotency-Key: ordine-10452" \
-d '{"plan":"pro","externalId":"ordine-10452","customer":{"name":"Bianchi Srl","email":"it@bianchi.example","country":"IT"}}'
La risposta contiene license.id e license.token: il cliente incolla il codice in
KoaPanel › Piano e licenza, e la licenza si lega a quel server. Le licenze dei partner non
mostrano i piani e la pagina d'acquisto di KoaPanel.
Fatturazione all'ingrosso. Ogni licenza costa il prezzo partner mensile del suo piano (listino riservato a fasce di volume, nell'area partner),
in proporzione al tempo nel mese (i cambi di piano si contano dal momento del cambio; una
licenza sospesa si paga, una revocata fino alla fine del giorno della revoca). A inizio mese
ricevi la fattura elettronica del mese precedente, con il dettaglio per licenza in
GET /statements/{mese}. Per diventare partner richiedi l'accreditamento.
Specifica OpenAPI 3.1 della Partner API · OpenAPI del pannello sulla demo