Official SDKs are in preparation. Until then, a small helper function is all you need: the examples below sign every request correctly and work as they are. Do not install packages that claim to be KernelHost SDKs; we will announce official packages in the changelog.

PHP

<?php

function kh_api(string $method, string $path, ?array $payload = null, ?string $idempotencyKey = null): array
{
    $body  = $payload === null ? '' : json_encode($payload, JSON_UNESCAPED_SLASHES);
    $ts    = (string) time();
    $nonce = bin2hex(random_bytes(16));
    $sig   = hash_hmac('sha256', "{$method}\n{$path}\n{$ts}\n{$nonce}\n" . hash('sha256', $body), getenv('KH_SECRET'));

    $headers = [
        'User-Agent: my-panel/1.0',
        'KH-Key: ' . getenv('KH_KEY'),
        "KH-Timestamp: {$ts}",
        "KH-Nonce: {$nonce}",
        "KH-Signature: {$sig}",
    ];
    if ($idempotencyKey !== null) {
        $headers[] = "Idempotency-Key: {$idempotencyKey}";
    }

    $options = [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_TIMEOUT        => 60,
    ];
    if ($body !== '') {
        $headers[] = 'Content-Type: application/json';
        $options[CURLOPT_POSTFIELDS] = $body;
    }
    $options[CURLOPT_HTTPHEADER] = $headers;

    $ch = curl_init('https://www.kernelhost.com/cp/kernelhost_api' . $path);
    curl_setopt_array($ch, $options);
    $raw    = (string) curl_exec($ch);
    $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

    return [$status, json_decode($raw, true) ?? $raw];
}

[$status, $products] = kh_api('GET', '/v1/products');

[$status, $order] = kh_api('POST', '/v1/orders', [
    'product_id'    => 42,
    'billing_cycle' => 'monthly',
    'hostname'      => 'web01.example.com',
], 'order-web01-20260923');

[$status, $service]     = kh_api('GET', '/v1/services/1234');
[$status, $credentials] = kh_api('GET', '/v1/services/1234/credentials');

Python

import hashlib
import hmac
import json
import os
import secrets
import time
import urllib.error
import urllib.request


def kh_api(method, path, payload=None, idempotency_key=None):
    body = b"" if payload is None else json.dumps(payload, separators=(",", ":")).encode()
    ts = str(int(time.time()))
    nonce = secrets.token_hex(16)
    signing = f"{method}\n{path}\n{ts}\n{nonce}\n{hashlib.sha256(body).hexdigest()}"
    sig = hmac.new(os.environ["KH_SECRET"].encode(), signing.encode(), hashlib.sha256).hexdigest()

    headers = {
        "User-Agent": "my-panel/1.0",
        "KH-Key": os.environ["KH_KEY"],
        "KH-Timestamp": ts,
        "KH-Nonce": nonce,
        "KH-Signature": sig,
    }
    if body:
        headers["Content-Type"] = "application/json"
    if idempotency_key:
        headers["Idempotency-Key"] = idempotency_key

    request = urllib.request.Request(
        "https://www.kernelhost.com/cp/kernelhost_api" + path,
        data=body or None,
        headers=headers,
        method=method,
    )
    try:
        with urllib.request.urlopen(request, timeout=60) as response:
            status, raw = response.status, response.read()
    except urllib.error.HTTPError as error:
        status, raw = error.code, error.read()
    try:
        return status, json.loads(raw)
    except ValueError:
        return status, raw


status, products = kh_api("GET", "/v1/products")

status, order = kh_api("POST", "/v1/orders", {
    "product_id": 42,
    "billing_cycle": "monthly",
    "hostname": "web01.example.com",
}, idempotency_key="order-web01-20260923")

status, service = kh_api("GET", "/v1/services/1234")
status, credentials = kh_api("GET", "/v1/services/1234/credentials")

Using another language? The signing logic takes only a few lines, see the authentication page. The endpoint reference lists every route.