API Documentation

Code examples in Python, JavaScript, PHP, Go, Rust, CLI, and curl for the PidginHost API. Manage cloud servers, Kubernetes clusters, DNS, and dedicated servers programmatically.

Authentication

All API requests require an authentication token. Include it in the Authorization header of every request.

You can generate a token from your Dashboard under API Tokens, or authenticate via the CLI with phctl auth login.

Set up your client

from pidginhost_sdk.client import PidginHost

client = PidginHost("your-api-token")
import { PidginHost } from "@pidginhost/sdk";

const client = new PidginHost(process.env.PIDGINHOST_API_TOKEN);
<?php
require_once 'vendor/autoload.php';
$client = new PidginHost(getenv('PIDGINHOST_API_TOKEN'));
package main
import (
    "context"
    "os"
    pidginhost "github.com/pidginhost/sdk-go"
)
func main() {
    client := pidginhost.New(os.Getenv("PIDGINHOST_API_TOKEN"))
    ctx := context.Background()
use pidginhost_sdk::Client;

let client = Client::new(
    std::env::var("PIDGINHOST_API_TOKEN").unwrap()
);
# Authenticate with token
phctl auth login --token YOUR_API_TOKEN

# Or interactive browser login
phctl auth login
curl -X GET https://www.pidginhost.com/api/v1/cloud/servers/ \
  -H "Authorization: Token YOUR_API_TOKEN"

MCP Server (AI assistants)

Our hosted Model Context Protocol server gives compatible AI assistants a set of tools for PidginHost. They can manage cloud servers, Kubernetes clusters, DNS records, S3 storage, and support tickets, and read billing information. Each tool calls the PidginHost REST API with your scoped OAuth access token.

The server works with Claude, Codex, and other remote MCP clients that support OAuth.

Endpoint: https://mcp.pidginhost.com/mcp

Connect from Claude (web or desktop app)

  1. Add a custom connector using the URL https://mcp.pidginhost.com/mcp.
  2. PidginHost opens its sign-in and consent pages. Sign in, review the requested capabilities, and approve or cancel the request.
  3. The connection uses the OAuth 2.1 authorization code flow with PKCE and dynamic client registration. You do not need to create or copy an API key.

Connect from Claude Code (CLI)

  1. Add the server. The -s user flag makes it available in every project:
    claude mcp add -s user --transport http pidginhost https://mcp.pidginhost.com/mcp
  2. Run /mcp in Claude Code, select pidginhost, and choose Authenticate. Your browser opens the PidginHost sign-in and consent pages.
  3. Approve the request. The token is saved for you, so there is no API key to copy.

Connect from Codex (CLI or IDE extension)

  1. Add the server. Codex stores it in your user configuration, shared by the CLI and IDE extension:
    codex mcp add pidginhost --url https://mcp.pidginhost.com/mcp
  2. Authenticate with PidginHost:
    codex mcp login pidginhost
  3. Your browser opens the PidginHost sign-in and consent pages. After approving access, restart any running Codex client. In the Codex CLI, run /mcp to confirm that pidginhost is connected.

Other MCP clients

Use a remote MCP client that supports OAuth 2.1, PKCE, and dynamic client registration. The endpoint advertises the authorization server (https://www.pidginhost.com) in its protected-resource metadata.

Scopes

The client requests OAuth scopes. The consent screen lists each requested capability before you approve or cancel access. Read and write access are separate, and each area of the platform has separate scopes:

ScopeGrants
cloud:read / cloud:writeRead or manage cloud servers, volumes, snapshots, and firewalls
k8s:read / k8s:writeRead or manage Kubernetes clusters and node pools
dns:read / dns:writeRead domains and DNS records, or manage DNS records
s3:read / s3:writeRead or manage S3 buckets
billing:readRead balance, invoices, and subscriptions
support:read / support:writeRead or manage support tickets
account:read / account:writeRead your profile and SSH keys, or add an SSH key

Security

  • The MCP server validates every access token through OAuth token introspection before a tool can use it.
  • Every tool uses your OAuth token, and the API limits access to resources in your account.
  • Clients can request read-only scopes without requesting write access.
  • Changing your password immediately revokes all MCP access tokens.

Cloud Servers

Create, manage, and destroy cloud virtual machines via the API.

List all servers - GET /api/v1/cloud/servers/

servers = client.cloud.cloud_servers_list()
for server in servers:
    print(server.name, server.status)
const { data: servers } = await client.cloud.cloudServersList();
servers.forEach(s => console.log(s.name, s.status));
$servers = $client->cloud->cloudServersList();
foreach ($servers as $server) {
    echo $server->getName() . "\n";
}
servers, _, err := client.CloudAPI.CloudServersList(ctx).Execute()
for _, s := range servers {
    fmt.Println(s.GetName(), s.GetStatus())
}
let servers = client.cloud_api().cloud_servers_list().await?;
for s in &servers {
    println!("{} {}", s.name, s.status);
}
phctl compute server list
curl -X GET https://www.pidginhost.com/api/v1/cloud/servers/ \
  -H "Authorization: Token YOUR_API_TOKEN"

Create a server - POST /api/v1/cloud/servers/

server = client.cloud.cloud_servers_create(
    hostname="web-01",
    image="ubuntu24",
    package="cloudv-4",
    ssh_pub_key="ssh-ed25519 AAAA...",
)
print(f"Created: {server.name}")
const { data: server } = await client.cloud.cloudServersCreate({
  hostname: "web-01",
  image: "ubuntu24",
  package: "cloudv-4",
  sshPubKey: "ssh-ed25519 AAAA...",
});
console.log("Created:", server.name);
$server = $client->cloud->cloudServersCreate([
    'hostname' => 'web-01',
    'image' => 'ubuntu24',
    'package' => 'cloudv-4',
    'ssh_pub_key' => 'ssh-ed25519 AAAA...',
]);
echo "Created: " . $server->getName();
req := client.CloudAPI.CloudServersCreate(ctx)
req = req.CloudServerCreateRequest(pidginhost.CloudServerCreateRequest{
    Hostname: "web-01",
    Image: "ubuntu24",
    Package: "cloudv-4",
    SshPubKey: "ssh-ed25519 AAAA...",
})
server, _, err := req.Execute()
let server = client.cloud_api()
    .cloud_servers_create()
    .hostname("web-01")
    .image("ubuntu24")
    .package("cloudv-4")
    .ssh_pub_key("ssh-ed25519 AAAA...")
    .await?;
phctl compute server create \
  --hostname web-01 \
  --image ubuntu24 \
  --package cloudv-4
curl -X POST https://www.pidginhost.com/api/v1/cloud/servers/ \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"hostname":"web-01","image":"ubuntu24","package":"cloudv-4","ssh_pub_key":"ssh-ed25519 AAAA..."}'

Destroy a server - DELETE /api/v1/cloud/servers/{id}/

client.cloud.cloud_servers_destroy(server_id=42)
await client.cloud.cloudServersDestroy({ id: 42 });
$client->cloud->cloudServersDestroy(42);
_, err := client.CloudAPI.CloudServersDestroy(ctx, 42).Execute()
client.cloud_api().cloud_servers_destroy(42).await?;
phctl compute server destroy 42
curl -X DELETE https://www.pidginhost.com/api/v1/cloud/servers/42/ \
  -H "Authorization: Token YOUR_API_TOKEN"

Power management - POST /api/v1/cloud/servers/{id}/power-management/

client.cloud.cloud_servers_power_management_create(
    server_id=42, action="restart"
)
await client.cloud.cloudServersPowerManagementCreate(42, { action: "restart" });
$client->cloud->cloudServersPowerManagementCreate(42, ['action' => 'restart']);
_, err := client.CloudAPI.CloudServersPowerManagementCreate(ctx, 42).PowerAction(pidginhost.PowerAction{Action: "restart"}).Execute()
client.cloud_api().cloud_servers_power_management_create(42).action("restart").await?;
phctl compute server restart 42
curl -X POST https://www.pidginhost.com/api/v1/cloud/servers/42/power-management/ \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"restart"}'

Kubernetes Clusters

Provision managed Kubernetes clusters, retrieve kubeconfig files, and manage cluster lifecycle.

List clusters - GET /api/kubernetes/clusters/

clusters = client.kubernetes.clusters_list()
for cluster in clusters:
    print(cluster.name, cluster.status)
const { data: clusters } = await client.kubernetes.clustersList();
clusters.forEach(c => console.log(c.name, c.status));
$clusters = $client->kubernetes->clustersList();
foreach ($clusters as $cluster) {
    echo $cluster->getName() . "\n";
}
clusters, _, err := client.KubernetesAPI.ClustersList(ctx).Execute()
for _, c := range clusters {
    fmt.Println(c.GetName(), c.GetStatus())
}
let clusters = client.kubernetes_api().clusters_list().await?;
for c in &clusters {
    println!("{} {}", c.name, c.status);
}
phctl kubernetes cluster list
curl -X GET https://www.pidginhost.com/api/kubernetes/clusters/ \
  -H "Authorization: Token YOUR_API_TOKEN"

Create cluster - POST /api/kubernetes/clusters/

cluster = client.kubernetes.clusters_create(
    name="prod-cluster",
    version="1.30",
    node_count=3,
    node_package="k8s-4",
)
print(f"Created: {cluster.name}")
const { data: cluster } = await client.kubernetes.clustersCreate({
  name: "prod-cluster",
  version: "1.30",
  nodeCount: 3,
  nodePackage: "k8s-4",
});
console.log("Created:", cluster.name);
$cluster = $client->kubernetes->clustersCreate([
    'name' => 'prod-cluster',
    'version' => '1.30',
    'node_count' => 3,
    'node_package' => 'k8s-4',
]);
echo "Created: " . $cluster->getName();
req := client.KubernetesAPI.ClustersCreate(ctx)
req = req.ClusterCreateRequest(pidginhost.ClusterCreateRequest{
    Name: "prod-cluster",
    Version: "1.30",
    NodeCount: 3,
    NodePackage: "k8s-4",
})
cluster, _, err := req.Execute()
let cluster = client.kubernetes_api()
    .clusters_create()
    .name("prod-cluster")
    .version("1.30")
    .node_count(3)
    .node_package("k8s-4")
    .await?;
phctl kubernetes cluster create \
  --name prod-cluster \
  --type prod \
  --package cloudv-4 \
  --pool-size 3
curl -X POST https://www.pidginhost.com/api/kubernetes/clusters/ \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"prod-cluster","cluster_type":"prod","resource_pool_package":"cloudv-4","resource_pool_size":3}'

Get kubeconfig - GET /api/kubernetes/clusters/{id}/kubeconfig/

kubeconfig = client.kubernetes.clusters_kubeconfig(cluster_id=5)
with open("kubeconfig.yaml", "w") as f:
    f.write(kubeconfig)
const { data } = await client.kubernetes.clustersKubeconfig({ id: 5 });
fs.writeFileSync("kubeconfig.yaml", data);
$kubeconfig = $client->kubernetes->clustersKubeconfig(5);
file_put_contents('kubeconfig.yaml', $kubeconfig);
kubeconfig, _, err := client.KubernetesAPI.ClustersKubeconfig(ctx, 5).Execute()
os.WriteFile("kubeconfig.yaml", []byte(kubeconfig), 0600)
let kubeconfig = client.kubernetes_api().clusters_kubeconfig(5).await?;
std::fs::write("kubeconfig.yaml", &kubeconfig)?;
phctl kubernetes cluster kubeconfig 5 > kubeconfig.yaml
curl -X GET https://www.pidginhost.com/api/kubernetes/clusters/5/kubeconfig/ \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -o kubeconfig.yaml

Delete cluster - DELETE /api/kubernetes/clusters/{id}/

client.kubernetes.clusters_destroy(cluster_id=5)
await client.kubernetes.clustersDestroy({ id: 5 });
$client->kubernetes->clustersDestroy(5);
_, err := client.KubernetesAPI.ClustersDestroy(ctx, 5).Execute()
client.kubernetes_api().clusters_destroy(5).await?;
phctl kubernetes cluster delete 5
curl -X DELETE https://www.pidginhost.com/api/kubernetes/clusters/5/ \
  -H "Authorization: Token YOUR_API_TOKEN"

FreeDNS

Manage DNS zones and records for your domains.

List active domains - GET /api/v1/freedns/dns/

domains = client.freedns.dns_list()
for domain in domains:
    print(domain.name)
const { data: domains } = await client.freedns.dnsList();
domains.forEach(d => console.log(d.name));
$domains = $client->freedns->dnsList();
foreach ($domains as $domain) {
    echo $domain->getName() . "\n";
}
domains, _, err := client.FreeDNSAPI.DnsList(ctx).Execute()
for _, d := range domains {
    fmt.Println(d.GetName())
}
let domains = client.freedns_api().dns_list().await?;
for d in &domains {
    println!("{}", d.name);
}
phctl dns domain list
curl -X GET https://www.pidginhost.com/api/v1/freedns/dns/ \
  -H "Authorization: Token YOUR_API_TOKEN"

List DNS records - GET /api/v1/freedns/dns/records/?domain=example.com&source=internal

records = client.freedns.dns_records(
    domain="example.com", source="internal"
)
for r in records:
    print(r.type, r.name, r.content)
const { data: records } = await client.freedns.dnsRecords({
  domain: "example.com", source: "internal",
});
records.forEach(r => console.log(r.type, r.name, r.content));
$records = $client->freedns->dnsRecords('example.com', 'internal');
foreach ($records as $r) {
    echo $r->getType() . " " . $r->getName() . "\n";
}
records, _, err := client.FreeDNSAPI.DnsRecords(ctx).Domain("example.com").Source("internal").Execute()
for _, r := range records {
    fmt.Println(r.GetType(), r.GetName(), r.GetContent())
}
let records = client.freedns_api()
    .dns_records("example.com", "internal")
    .await?;
for r in &records {
    println!("{} {} {}", r.record_type, r.name, r.content);
}
phctl dns record list --domain example.com --source internal
curl -X GET "https://www.pidginhost.com/api/v1/freedns/dns/records/?domain=example.com&source=internal" \
  -H "Authorization: Token YOUR_API_TOKEN"

Add or edit a record - POST /api/v1/freedns/dns/add-record/?domain=example.com&source=internal

client.freedns.dns_add_record(
    domain="example.com",
    source="internal",
    type="A",
    name="www",
    content="203.0.113.10",
    ttl=3600,
)
await client.freedns.dnsAddRecord({
  domain: "example.com",
  source: "internal",
  type: "A",
  name: "www",
  content: "203.0.113.10",
  ttl: 3600,
});
$client->freedns->dnsAddRecord('example.com', 'internal', [
    'type' => 'A',
    'name' => 'www',
    'content' => '203.0.113.10',
    'ttl' => 3600,
]);
_, err := client.FreeDNSAPI.DnsAddRecord(ctx).
    Domain("example.com").Source("internal").
    DnsRecord(pidginhost.DnsRecord{
        Type: "A", Name: "www",
        Content: "203.0.113.10", Ttl: 3600,
    }).Execute()
client.freedns_api()
    .dns_add_record("example.com", "internal")
    .record_type("A")
    .name("www")
    .content("203.0.113.10")
    .ttl(3600)
    .await?;
phctl dns record add \
  --domain example.com \
  --source internal \
  --type A \
  --name www \
  --content 203.0.113.10 \
  --ttl 3600
curl -X POST "https://www.pidginhost.com/api/v1/freedns/dns/add-record/?domain=example.com&source=internal" \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"A","name":"www","content":"203.0.113.10","ttl":3600}'

Delete a record - POST /api/v1/freedns/dns/delete-record/?domain=example.com&source=internal

client.freedns.dns_delete_record(
    domain="example.com",
    source="internal",
    type="A",
    name="www",
    content="203.0.113.10",
)
await client.freedns.dnsDeleteRecord({
  domain: "example.com",
  source: "internal",
  type: "A",
  name: "www",
  content: "203.0.113.10",
});
$client->freedns->dnsDeleteRecord('example.com', 'internal', [
    'type' => 'A',
    'name' => 'www',
    'content' => '203.0.113.10',
]);
_, err := client.FreeDNSAPI.DnsDeleteRecord(ctx).
    Domain("example.com").Source("internal").
    DnsRecord(pidginhost.DnsRecord{
        Type: "A", Name: "www",
        Content: "203.0.113.10",
    }).Execute()
client.freedns_api()
    .dns_delete_record("example.com", "internal")
    .record_type("A")
    .name("www")
    .content("203.0.113.10")
    .await?;
phctl dns record delete \
  --domain example.com \
  --source internal \
  --type A \
  --name www \
  --content 203.0.113.10
curl -X POST "https://www.pidginhost.com/api/v1/freedns/dns/delete-record/?domain=example.com&source=internal" \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"A","name":"www","content":"203.0.113.10"}'

Dedicated Servers

View and manage your dedicated servers, including power operations.

List servers - GET /api/v1/dedicated/servers/

servers = client.dedicated.servers_list()
for server in servers:
    print(server.hostname, server.status)
const { data: servers } = await client.dedicated.serversList();
servers.forEach(s => console.log(s.hostname, s.status));
$servers = $client->dedicated->serversList();
foreach ($servers as $server) {
    echo $server->getHostname() . "\n";
}
servers, _, err := client.DedicatedAPI.ServersList(ctx).Execute()
for _, s := range servers {
    fmt.Println(s.GetHostname(), s.GetStatus())
}
let servers = client.dedicated_api().servers_list().await?;
for s in &servers {
    println!("{} {}", s.hostname, s.status);
}
phctl dedicated server list
curl -X GET https://www.pidginhost.com/api/v1/dedicated/servers/ \
  -H "Authorization: Token YOUR_API_TOKEN"

Get server details - GET /api/v1/dedicated/servers/{id}/

server = client.dedicated.servers_retrieve(server_id=7)
print(server.hostname, server.cpu, server.ram)
const { data: server } = await client.dedicated.serversRetrieve({ id: 7 });
console.log(server.hostname, server.cpu, server.ram);
$server = $client->dedicated->serversRetrieve(7);
echo $server->getHostname() . " " . $server->getCpu();
server, _, err := client.DedicatedAPI.ServersRetrieve(ctx, 7).Execute()
fmt.Println(server.GetHostname(), server.GetCpu(), server.GetRam())
let server = client.dedicated_api().servers_retrieve(7).await?;
println!("{} {} {}", server.hostname, server.cpu, server.ram);
phctl dedicated server get 7
curl -X GET https://www.pidginhost.com/api/v1/dedicated/servers/7/ \
  -H "Authorization: Token YOUR_API_TOKEN"

Power management - POST /api/v1/dedicated/servers/{id}/power/

client.dedicated.servers_power(
    server_id=7, action="restart"
)
await client.dedicated.serversPower(7, { action: "restart" });
$client->dedicated->serversPower(7, ['action' => 'restart']);
_, err := client.DedicatedAPI.ServersPower(ctx, 7).PowerAction(pidginhost.PowerAction{Action: "restart"}).Execute()
client.dedicated_api().servers_power(7).action("restart").await?;
phctl dedicated server restart 7
curl -X POST https://www.pidginhost.com/api/v1/dedicated/servers/7/power/ \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"restart"}'

Full API Reference

Explore the complete interactive API documentation with request and response schemas for every endpoint.

Open Swagger UI