exbil/reselling-api-client

Official PHP client for the Exbil Reselling Portal API — Root Server, Cloud Services, Game Server, TeamSpeak, VPN, Mailcow, Domain and Accounting endpoints.

Maintainers

Package info

github.com/Exbil/reselling-api-client

pkg:composer/exbil/reselling-api-client

Statistics

Installs: 375

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.1.0 2026-06-25 01:24 UTC

This package is auto-updated.

Last update: 2026-06-25 01:24:49 UTC


README

PHP API Client for the Exbil Reselling Portal.

Installation

composer require exbil/reselling-api-client

Quick Start

<?php
require 'vendor/autoload.php';

use Exbil\ResellingAPI\Client;

$client = new Client('your-api-key', 'https://reselling-portal.de/api/');

// List servers
$servers = $client->rootServer()->getAll();

// Create VPN account
$account = $client->vpn()->account()->create('username', 'password');

// Register a domain
$domain = $client->domain()->register('example.com', [
    'owner_handle' => 1,
    'admin_handle' => 1,
    'tech_handle' => 1,
]);

// Create Mailcow domain
$mailcowDomain = $client->mailcow()->create('node-1', [
    'domain' => 'example.com',
    'mailboxes' => 10,
    'aliases' => 50,
    'quota_mb' => 5000,
]);

Authentication

All API requests require an API key transmitted as Bearer Token:

$client = new Client('your-api-key');

For the sandbox environment use the sandbox base URL:

$client = new Client('rs_sb_…', 'https://www.reselling-portal.de/api/sandbox/');

You can verify the configured key and inspect its scoped permissions at any time:

$info = $client->validateKey();
// → ['key' => [...], 'permissions' => [...], 'rate_limit' => [...]]

API Reference

Accounting

Billing, invoices, credit and usage.

Method Description
getUserData() Team/user billing information
getCreditStatus() Current credit status
getUsage() Current month usage summary
getUsageDetails(array $filters) Detailed usage records
getInvoices() All invoices
getInvoice(int $id) Single invoice
// Get credit status
$credit = $client->accounting()->getCreditStatus();

// Get usage with filters
$usage = $client->accounting()->getUsageDetails([
    'start' => '2024-01-01',
    'end' => '2024-01-31',
    'product_type' => 'rootserver',
    'limit' => 100,
]);

// Get invoice
$invoice = $client->accounting()->getInvoice(123);

Domain

Domain registration, transfer, DNS management and handles.

Main Methods

Method Description
getAll() All domains
get(string $domain) Single domain
checkAvailability(string $domain) Check availability
checkBulkAvailability(array $domains) Bulk availability check (max 50)
register(string $domain, array $handles, array $nameservers, int $period) Register domain
transfer(string $domain, string $authcode, array $handles, array $nameservers) Transfer domain
sync(string $domain) Sync from registrar
getAuthcode(string $domain) Get authcode
updateHandles(string $domain, array $handles) Update handles
requestDeletion(string $domain) Request deletion
cancelDeletion(string $domain) Cancel deletion
// Check availability
$check = $client->domain()->checkAvailability('example.com');

// Bulk availability check (up to 50 domains per request)
$bulk = $client->domain()->checkBulkAvailability([
    'example.com',
    'example.de',
    'example.net',
]);

// Register domain
$domain = $client->domain()->register('example.com', [
    'owner_handle' => 1,
    'admin_handle' => 1,
    'tech_handle' => 1,
], ['ns1.example.net', 'ns2.example.net'], 1);

// Transfer domain
$transfer = $client->domain()->transfer('example.com', 'AUTH-CODE-123', [
    'owner_handle' => 1,
    'admin_handle' => 1,
    'tech_handle' => 1,
]);

Pricing ($client->domain()->pricing())

Method Description
getAll() All domain prices
getTlds() Available TLDs
getByTld(string $tld) Pricing for specific TLD
$prices = $client->domain()->pricing()->getAll();
$tlds = $client->domain()->pricing()->getTlds();
$comPrice = $client->domain()->pricing()->getByTld('com');

Handle ($client->domain()->handle())

Method Description
getTypes() Available handle types
getAll() All handles
get(string|int $id) Single handle
create(array $data) Create handle
update(string|int $id, array $data) Update handle
delete(string|int $id) Delete handle
setDefault(string|int $id) Set as default
// Create handle
$handle = $client->domain()->handle()->create([
    'type' => 'person',
    'firstname' => 'John',
    'lastname' => 'Doe',
    'street' => '123 Main St',
    'city' => 'Berlin',
    'zip' => '10115',
    'country' => 'DE',
    'phone' => '+49.301234567',
    'email' => 'john@example.com',
]);

// Set as default
$client->domain()->handle()->setDefault(1);

Nameserver ($client->domain()->nameserver())

Method Description
get(string $domain) Get nameservers
update(string $domain, array $nameservers) Update nameservers
$ns = $client->domain()->nameserver()->get('example.com');
$client->domain()->nameserver()->update('example.com', [
    'ns1.example.net',
    'ns2.example.net',
]);

DNS ($client->domain()->dns())

Method Description
get(string $domain) Get DNS records
create(string $domain, array $record) Create record
update(string $domain, string|int $recordId, array $data) Update record
bulkUpdate(string $domain, array $records) Bulk update records
delete(string $domain, string|int $recordId) Delete record
getZones(string $domain) Get DNS zones
createZone(string $domain, array $zone) Create zone
updateZone(string $domain, string|int $zoneId, array $data) Update zone
deleteZone(string $domain, string|int $zoneId) Delete zone
// Get DNS records
$records = $client->domain()->dns()->get('example.com');

// Create A record
$record = $client->domain()->dns()->create('example.com', [
    'type' => 'A',
    'name' => '@',
    'content' => '192.168.1.1',
    'ttl' => 3600,
]);

// Create MX record
$client->domain()->dns()->create('example.com', [
    'type' => 'MX',
    'name' => '@',
    'content' => 'mail.example.com',
    'priority' => 10,
]);

// Bulk update
$client->domain()->dns()->bulkUpdate('example.com', [
    ['type' => 'A', 'name' => '@', 'content' => '192.168.1.1'],
    ['type' => 'AAAA', 'name' => '@', 'content' => '2001:db8::1'],
]);

Root Server

Manage virtual servers.

Main Methods

Method Description
getAll(array $filters) All servers (filters: state, datacenter_id, cluster_id)
get(int $vmId) Single server
create(string $clusterSlug, array $config) Create server
update(int $vmId, array $config) Resize server
delete(int $vmId) Delete server
resetRootPassword(int $vmId, ?string $password) Reset root password
reinstall(int $vmId, array $config) Reinstall server
getStats(int $vmId) Live statistics (CPU, RAM, network)
getLogs(int $vmId, int $limit) Server logs
getTasks(int $vmId, int $limit) Running/completed tasks
// Create server
$server = $client->rootServer()->create('cluster-de-1', [
    'hostname' => 'web-server-01',
    'cores' => 4,
    'ram_mb' => 8192,
    'disk_gb' => 100,
    'operating_system_slug' => 'ubuntu-22.04',
    'root_password' => 'secure-password',
    'ipv4_addresses' => 1,
    'backup_slots' => 1,
]);

// Resize server (disk can only be increased)
$client->rootServer()->update(12345, [
    'cores' => 8,
    'ram_mb' => 16384,
]);

// Get stats
$stats = $client->rootServer()->getStats(12345);

Location ($client->rootServer()->location())

Method Description
getAll() All datacenters
getClusters(string $datacenterSlug) Clusters of a datacenter
$locations = $client->rootServer()->location()->getAll();
$clusters = $client->rootServer()->location()->getClusters('de-fra');

Cluster ($client->rootServer()->cluster())

Method Description
getAll() All clusters
get(string $clusterSlug) Single cluster
getOsList(string $clusterSlug) Available operating systems
getPrices(string $clusterSlug) Price list of a cluster
calculatePrice(string $clusterSlug, array $config) Calculate price
// Get OS list for cluster
$osList = $client->rootServer()->cluster()->getOsList('cluster-de-1');

// Calculate price
$price = $client->rootServer()->cluster()->calculatePrice('cluster-de-1', [
    'cores' => 4,
    'ram_mb' => 8192,
    'disk_gb' => 100,
    'backup_slots' => 1,
    'ipv4_addresses' => 1,
    'ipv6_addresses' => 1,
]);

Power ($client->rootServer()->power())

Method Description
start(int $vmId) Start server
stop(int $vmId) Shutdown server (graceful)
reboot(int $vmId) Reboot server
forceStop(int $vmId) Power off server (force)
$client->rootServer()->power()->start(12345);
$client->rootServer()->power()->stop(12345);
$client->rootServer()->power()->reboot(12345);
$client->rootServer()->power()->forceStop(12345);

VPN

VPN accounts and configurations.

Main Methods

Method Description
getServers() All VPN servers
getPorts() Available ports
getPricing() Pricing
getGeoIP() GeoIP info of current request
checkUsername(string $username) Check username availability
$servers = $client->vpn()->getServers();
$pricing = $client->vpn()->getPricing();
$available = $client->vpn()->checkUsername('new-user');

Account ($client->vpn()->account())

Method Description
getAll() All VPN accounts
get(int $id) Single account
create(string $username, ?string $password) Create account
delete(int $id) Delete account
sync(int $id) Sync account
changePassword(int $id, string $password) Change password
enable(int $id) Enable account
disable(int $id) Disable account
// Create account
$account = $client->vpn()->account()->create('new-user', 'secure-password');

// Change password
$client->vpn()->account()->changePassword(123, 'new-password');

// Sync account
$client->vpn()->account()->sync(123);

// Enable account (must be disabled, pending, or error state)
$client->vpn()->account()->enable(123);

// Disable account (must be active state)
$client->vpn()->account()->disable(123);

Note: Suspended accounts cannot be enabled via API. Contact support to lift the suspension.

Config ($client->vpn()->config())

Method Description
getOpenVpn(int $accountId, int $serverId, int $portId) OpenVPN config (JSON)
downloadOpenVpn(int $accountId, int $serverId, int $portId) OpenVPN .ovpn download
getWireGuard(int $accountId, int $serverId) WireGuard config (JSON)
downloadWireGuard(int $accountId, int $serverId) WireGuard .conf download
// OpenVPN configuration
$ovpnConfig = $client->vpn()->config()->getOpenVpn(123, 1, 443);

// WireGuard configuration
$wgConfig = $client->vpn()->config()->getWireGuard(123, 1);

Mailcow

Email domains, mailboxes and aliases.

Main Methods

Method Description
getNodes(?string $datacenter) All Mailcow nodes
getLoadBalancerStats(?string $datacenter) Load balancer statistics
calculatePrice(string $nodeOrDc, int $mailboxes, int $aliases, int $quotaMb) Calculate price
getAll(?string $id) All or single domain
get(string|int $id) Single domain
create(string $nodeOrDc, array $config) Create domain
update(string|int $id, array $config) Update domain
delete(string|int $id) Delete domain
// Get nodes
$nodes = $client->mailcow()->getNodes();

// Create domain
$domain = $client->mailcow()->create('node-1', [
    'domain' => 'example.com',
    'mailboxes' => 10,
    'aliases' => 50,
    'quota_mb' => 5000,
    'defquota_mb' => 500,
    'maxquota_mb' => 1000,
    'admin_username' => 'admin',
    'admin_password' => 'secure-password',
]);

// Update domain
$client->mailcow()->update('example.com', [
    'mailboxes' => 20,
    'quota_mb' => 10000,
]);

Mailbox ($client->mailcow()->mailbox())

Method Description
getAll(string $domain, ?int $id) Get mailboxes
get(string $domain, int $mailboxId) Single mailbox
create(string $domain, string $address, array $config) Create mailbox
update(string $domain, string $address, array $config) Update mailbox
delete(string $domain, string $localPart) Delete mailbox
// Create mailbox
$mailbox = $client->mailcow()->mailbox()->create('example.com', 'info', [
    'password' => 'secure-password',
    'name' => 'Info Mailbox',
    'quota_mb' => 500,
]);

// Update mailbox
$client->mailcow()->mailbox()->update('example.com', 'info', [
    'quota_mb' => 1000,
    'active' => true,
]);

// Delete mailbox
$client->mailcow()->mailbox()->delete('example.com', 'info');

Alias ($client->mailcow()->alias())

Method Description
getAll(string $domain, ?int $id) Get aliases
get(string $domain, int $aliasId) Single alias
create(string $domain, string $address, array $goto) Create alias
update(string $domain, string $address, array $goto, ?bool $active) Update alias
delete(string $domain, string $localPart) Delete alias
// Create alias
$alias = $client->mailcow()->alias()->create('example.com', 'support', [
    'info@example.com',
    'admin@example.com',
]);

// Update alias
$client->mailcow()->alias()->update('example.com', 'support', [
    'info@example.com',
], true);

// Delete alias
$client->mailcow()->alias()->delete('example.com', 'support');

Domain Admin ($client->mailcow()->domainAdmin())

Method Description
getAll(string $domain, ?int $id) Get domain admins
get(string $domain, int $adminId) Single admin
create(string $domain, string $username, ?string $password) Create admin
update(string $domain, string $username, array $config) Update admin
delete(string $domain, string $username) Delete admin
// Create admin
$admin = $client->mailcow()->domainAdmin()->create('example.com', 'admin', 'secure-password');

// Update admin
$client->mailcow()->domainAdmin()->update('example.com', 'admin', [
    'password' => 'new-password',
    'active' => true,
]);

Cloud Services

Container based services — game servers (Minecraft, TeamSpeak, CS2, Rust, Palworld, …), databases (MySQL, MariaDB, PostgreSQL, MongoDB, Redis, …), web stacks (Caddy, Apache+PHP, Node.js, Python, Bun, …), discord bots, and ~250 other templates. Services and backups are addressed by their UUID.

Main Methods

Method Description
templates() Order catalogue — every active template + node bindings + field_schema
getAll(array $filters = []) All cloud services (filters: status, per_page, team_id)
get(string $uuid) Service details incl. live status
create(array $config) Create a service (node_id, template_slug, name, memory_limit, disk_limit, cpu_limit, …)
delete(string $uuid) Delete a service
reinstall(string $uuid, array $options = []) Reinstall a service — see options below
status(string $uuid) Live resource usage / status
sendCommand(string $uuid, string $command) Send a console command (one-shot REST)
consoleToken(string $uuid) Issue a scoped token + wss:// URLs for the live console + stats streams
allocations(string $uuid) List every port allocation with role + protocol + description
// === ORDER FLOW ============================================

// 1) Pull the order catalogue
$catalog = $client->cloudServices()->templates();
$mcPaper = collect($catalog['data']['templates'])
    ->firstWhere('slug', 'minecraft-paper');
$node    = $catalog['data']['nodes'][0];

// 2) Place the order
$service = $client->cloudServices()->create([
    'node_id'       => $node['id'],
    'template_slug' => $mcPaper['slug'],
    'name'          => 'My Survival World',
    'memory_limit'  => 4096,                    // MB
    'disk_limit'    => 10240,                   // MB
    'cpu_limit'     => 200,                     // % of one core
    'environment'   => [                        // override env defaults
        'MC_VERSION'    => '1.21.11',
        'RCON_ENABLED'  => 'true',
        'RCON_PASSWORD' => bin2hex(random_bytes(8)),
    ],
]);

// 3) Watch the install land
$uuid = $service['data']['uuid'];
while (true) {
    $s = $client->cloudServices()->get($uuid)['data'];
    if (in_array($s['status'], ['running', 'install_failed', 'offline'])) break;
    sleep(3);
}

// === MANAGEMENT ============================================

$services = $client->cloudServices()->getAll(['status' => 'running']);
$service  = $client->cloudServices()->get('a1b2c3d4-...');
$client->cloudServices()->sendCommand('a1b2c3d4-...', 'say hello');
$client->cloudServices()->allocations('a1b2c3d4-...');

reinstall($uuid, $options) options array

Key Type Description
auto_start bool When true, the daemon starts the server the moment the wipe + image pull finishes — the customer sees the entrypoint's seed phase (Downloading vanilla jar..., npm run setup, ...) in the live console without a second click. Recommended for customer-facing flows.
cloudservice string Switch to a different service template at the same time (template-switch reinstall).
environment array Override environment variables for the (possibly new) template.
$client->cloudServices()->reinstall('a1b2c3d4-...', ['auto_start' => true]);

consoleToken($uuid) — Live WebSocket console + stats

Use this instead of polling REST sendCommand() when you need a live console view or want to stream output back to a client. The daemon accepts a short-lived (5 min) scoped token via Sec-WebSocket-Protocol, so the token never lands in URLs / proxy logs / browser history.

$ws = $client->cloudServices()->consoleToken('a1b2c3d4-...');
// $ws = [
//   'token'               => 'cst_...',
//   'subprotocols'        => ['cst', 'cst_...'],
//   'websocket_url'       => 'wss://node01.example.com:443/api/v1/servers/<uuid>/console',
//   'stats_websocket_url' => 'wss://node01.example.com:443/api/v1/servers/<uuid>/stats',
//   'expires_in_sec'      => 300,
// ]

Browser-side (vanilla JS):

const ws = new WebSocket(data.websocket_url, data.subprotocols);
ws.onmessage = e => console.log(e.data);
ws.send(JSON.stringify({event: 'command', command: 'list'}));

Token TTL is ~5 min; re-call consoleToken() before expiry to keep a long-running session alive. The same token authenticates BOTH the console and the stats WebSocket — connect to whichever URL you need.

Power ($client->cloudServices()->power())

Method Description
start(string $uuid) Start service
stop(string $uuid) Stop service (graceful)
restart(string $uuid) Restart service
kill(string $uuid) Kill service (force stop)
$client->cloudServices()->power()->start('a1b2c3d4-...');
$client->cloudServices()->power()->restart('a1b2c3d4-...');

Files ($client->cloudServices()->files())

Method Description
list(string $uuid, string $dir = '/') List a directory
read(string $uuid, string $file) Read file contents
write(string $uuid, string $file, string $content) Write a file
upload(string $uuid, string $filePath, string $dir = '/') Upload a local file
download(string $uuid, string $file) Get a download URL
delete(string $uuid, array $files) Delete files
compress(string $uuid, array $files, string $output) Compress into an archive
decompress(string $uuid, string $file, string $target) Extract an archive
$client->cloudServices()->files()->list('a1b2c3d4-...', '/');
$client->cloudServices()->files()->write('a1b2c3d4-...', 'server.properties', "max-players=20\n");

Backups ($client->cloudServices()->backups())

Each service has a slot quota (default 3) and a size cap (default 100 GB). Archives are encrypted at rest with AES-256-GCM and tagged with the team that created or uploaded them — restores across team boundaries are refused with 403 unless the caller passes force => true.

Method Description
getAll(string $uuid) All backups for a service (with team_id, cloudservice_id, origin, is_locked)
create(string $uuid, ?string $name = null, array $ignoredFiles = []) Create a backup of the current volume
upload(string $uuid, string $filePath) Upload a .tar.gz archive previously downloaded from this platform
delete(string $uuid, string $backupId) Delete a backup (sends a deletion-certificate mail to the owner)
restore(string $uuid, string $backupId, array $options = []) Restore a backup — stop, extract, chown, auto-start
toggleLock(string $uuid, string $backupId) Toggle the lock flag (locked backups skip retention sweeps)
download(string $uuid, string $backupId, string $targetPath) Stream the encrypted archive to a local path
// Snapshot
$client->cloudServices()->backups()->create('a1b2c3d4-...', 'pre-update');

// Download locally (encrypted .tar.gz — keep as-is for re-upload)
$client->cloudServices()->backups()->download(
    'a1b2c3d4-...',
    'backup-uuid-here',
    '/tmp/my-backup.tar.gz',
);

// Re-upload a backup you saved earlier
$client->cloudServices()->backups()->upload(
    'a1b2c3d4-...',
    '/tmp/my-backup.tar.gz',
);

// Restore. switch_template defaults to true — when the archive was
// taken on a different template (TS3 backup on a TS6 server) the
// daemon flips the server's cloudservice_id + docker_image to match
// the backup before extracting. Pass force=true for operator
// recovery (skips both the cross-team and cross-template guards).
$client->cloudServices()->backups()->restore(
    'a1b2c3d4-...',
    'backup-uuid-here',
    ['switch_template' => true],
);

// Lock an archive against the retention sweep
$client->cloudServices()->backups()->toggleLock('a1b2c3d4-...', 'backup-uuid-here');

Network ($client->cloudServices()->network())

Per-server IPv6 lifecycle. Up to four addresses out of the operator's routed prefix.

Method Description
status(string $uuid) Node prefix + upstream healthcheck snapshot
listIpv6(string $uuid) Addresses bound to the server (primary first)
orderIpv6(string $uuid) Allocate one more (HTTP 503 if transit down)
releaseIpv6(string $uuid, int $addressId) Release one by daemon-side id
$status = $client->cloudServices()->network()->status('a1b2c3d4-...');
if ($status['enabled'] && ($status['healthcheck']['healthy'] ?? false)) {
    $addr = $client->cloudServices()->network()->orderIpv6('a1b2c3d4-...');
    echo $addr['data']['ipv6'];     // e.g. 2a0e:97c0:440:105::abcd
}

A 503 response carries a detail.last_checked_at + detail.last_error explaining why the upstream is down; surface that to the operator rather than retrying blindly.

Container Registries ($client->cloudServices()->registries())

Customer-owned private Docker registries hosted on the same nodes. Each registry gets a <namespace>.<node-domain> URL and a generated admin password the customer uses to docker login + push images. Packaged (fixed slug → quotas) or custom (storage / repo / robot sliders).

Method Description
getAll() Calling team's registries
get(string $uuid) Registry details + admin creds (MANAGE-only)
create(array $config) Order a new registry (package or custom mode)
delete(string $uuid) Tear down — secure-wipes blobs before releasing the slot
packages() Catalogue of available packages with prices
calculatePrice(array $payload) Live price preview for the order form
checkNamespace(string $namespace) Returns {available: bool} for the typed namespace
// === ORDER FLOW ============================================

// 1) Show catalogue
$packages = $client->cloudServices()->registries()->packages();

// 2) Price-preview
$preview = $client->cloudServices()->registries()->calculatePrice([
    'mode'         => 'package',
    'package_slug' => 'small',
]);

// 3) Reserve the namespace (live as customer types)
$check = $client->cloudServices()->registries()->checkNamespace('myco-prod');
if (!$check['data']['available']) {
    throw new RuntimeException('Namespace taken');
}

// 4) Order
$registry = $client->cloudServices()->registries()->create([
    'mode'         => 'package',
    'package_slug' => 'small',
    'namespace'    => 'myco-prod',
    'auto_upgrade' => true,
]);

// 5) Customer pushes images
//    docker login myco-prod.registry.example.com -u admin -p <admin_password>
//    docker push myco-prod.registry.example.com/myapp:v1.0

packages() returns slug, included storage / repos / robots, hourly cents and a marketing blurb so the order form can render the cards without separate copy decks. Custom mode bypasses the package step:

$client->cloudServices()->registries()->create([
    'mode'        => 'custom',
    'storage_gb'  => 50,
    'repo_limit'  => 20,
    'robot_limit' => 10,
    'namespace'   => 'myco-staging',
]);

TeamSpeak

Order and manage TeamSpeak servers — power, slots, settings, channels, connected clients, privilege tokens, bans and snapshot backups.

// Pricing + available locations (datacenters with free capacity)
$pricing = $client->teamSpeak()->getPricing();

// Order a server (optionally in a specific datacenter)
$server = $client->teamSpeak()->order('My Server', 64);
$server = $client->teamSpeak()->order('My Server', 64, datacenterId: 2);

// List / show / delete
$servers = $client->teamSpeak()->getAll();
$server  = $client->teamSpeak()->get(1);
$client->teamSpeak()->delete(1);

// Live view (info + channels + connected clients) and advanced data
$view   = $client->teamSpeak()->view(1);
$extras = $client->teamSpeak()->extras(1); // groups, tokens, bans, complaints, log

// Power
$client->teamSpeak()->start(1);
$client->teamSpeak()->stop(1);

// Settings
$client->teamSpeak()->resize(1, 128); // subject to the host's free capacity
$client->teamSpeak()->updateSettings(1, ['name' => 'New name', 'password' => 'secret']);
$client->teamSpeak()->broadcast(1, 'Maintenance in 5 minutes');

Channels ($client->teamSpeak()->channels())

$client->teamSpeak()->channels()->create(1, 'Lobby');
$client->teamSpeak()->channels()->create(1, 'Team A', parentId: 5); // sub-channel
$client->teamSpeak()->channels()->delete(1, 5);

Clients ($client->teamSpeak()->clients())

$client->teamSpeak()->clients()->details(1, 12);
$client->teamSpeak()->clients()->move(1, 12, 5);
$client->teamSpeak()->clients()->kick(1, 12, 'Be nice');
$client->teamSpeak()->clients()->ban(1, 12, seconds: 3600, reason: 'Spam');

Security ($client->teamSpeak()->security())

$client->teamSpeak()->security()->createToken(1, serverGroupId: 6, description: 'Admin invite');
$client->teamSpeak()->security()->deleteToken(1, 'the-token-string');
$client->teamSpeak()->security()->addBan(1, ['ip' => '1.2.3.4', 'reason' => 'abuse']);
$client->teamSpeak()->security()->removeBan(1, 42);

Backups ($client->teamSpeak()->backups())

// Download a base64 snapshot (returned under data.snapshot)
$snapshot = $client->teamSpeak()->backups()->download(1)['data']['snapshot'];

// Restore from a snapshot (overwrites channels, groups and permissions)
$client->teamSpeak()->backups()->restore(1, $snapshot);

Error Handling

use Exbil\ResellingAPI\Exceptions\ApiException;
use Exbil\ResellingAPI\Exceptions\AuthenticationException;
use Exbil\ResellingAPI\Exceptions\ForbiddenException;
use Exbil\ResellingAPI\Exceptions\NotFoundException;
use Exbil\ResellingAPI\Exceptions\ValidationException;

try {
    $server = $client->rootServer()->get(99999);
} catch (AuthenticationException $e) {
    // 401 - Invalid API key
    echo "Authentication failed: " . $e->getMessage();
} catch (ForbiddenException $e) {
    // 403 - No permission
    echo "Access denied: " . $e->getMessage();
} catch (NotFoundException $e) {
    // 404 - Resource not found
    echo "Not found: " . $e->getMessage();
} catch (ValidationException $e) {
    // 422 - Validation error
    echo "Validation error: " . $e->getMessage();
    print_r($e->getValidationErrors());
} catch (ApiException $e) {
    // Other API errors
    echo "API error: " . $e->getMessage();
    echo "Status code: " . $e->getCode();
}

Asynchronous Operations

Many operations are executed asynchronously and return a 202 status:

  • Server create/delete/resize
  • Power operations (start, stop, reboot)
  • Mailcow domain/mailbox/alias create/update/delete
  • VPN account create/delete/enable/disable

The response typically contains a job ID or task information for tracking.

License

BSD-2-Clause