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,
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 scrivici.
Specifica OpenAPI 3.1 della Partner API · OpenAPI del pannello sulla demo