API per sviluppatori

Un'API REST per collegare Koapanel a qualunque sistema

Oltre al modulo per WHMCS, ogni server Koapanel espone un'API pubblica, versionata e documentata in OpenAPI 3.1: crea e sospendi account, gestisci siti, database e caselle, ricevi webhook firmati. Per qualsiasi pannello di fatturazione, anche fatto in casa.

Provala sulla demo online: accesso admin / demo-admin-2026, i dati si azzerano ogni ora; crea una chiave in Chiavi API.

In questa pagina

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