| name | Ploi PHP SDK Expert |
| description | Best practices for using the Ploi PHP SDK to interact with the Ploi.io server management API |
| compatible_agents | ["Claude Code","Cursor","Windsurf","GitHub Copilot"] |
| tags | ["php","laravel","ploi","server-management","api","sdk"] |
Ploi PHP SDK Expert
Context
This skill covers the Ploi PHP SDK (ploi/ploi-php-sdk), a PHP wrapper around the Ploi.io server management REST API. It uses Guzzle HTTP under the hood and provides a fluent, chainable interface for managing servers, sites, databases, deployments, and more.
Scope: Initializing the SDK client, chaining resources, performing CRUD operations on all Ploi API resources, handling pagination, error handling, and understanding the resource hierarchy.
Rules
Initialization
- Always instantiate the client with an API token:
$ploi = new \Ploi\Ploi($apiToken);
- The token can also be set after construction:
$ploi->setApiToken($token);
- The SDK auto-configures a Guzzle client pointing at
https://ploi.io/api/ with JSON content headers.
Fluent Resource Chaining
- Access resources using the fluent parent-child chain. Always start from the
$ploi instance and drill down:
$ploi->server($serverId) to target a server
$ploi->server($serverId)->sites($siteId) to target a site on a server
$ploi->server($serverId)->sites($siteId)->certificates() to access certificates on a site
- Pass the resource ID when you first access the resource in the chain, not as a separate call.
- Singular and plural method names are interchangeable on the entry point:
$ploi->server() and $ploi->servers() both return a Server resource.
Resource Hierarchy
- Server-level resources (accessed from
$ploi->server($id)->): sites(), databases(), cronjobs(), daemons(), sshKeys(), services(), networkRules(), systemUsers(), opcache(), insights(), loadBalancer()
- Site-level resources (accessed from
->sites($id)->): certificates(), repository(), queues(), deployment(), app(), environment(), alias(), redirects(), fastCgi(), authUser(), robots(), tenants(), monitors(), nginxConfiguration()
- Database-level resources (accessed from
->databases($id)->): backups(), users()
- Top-level resources (accessed from
$ploi->): project(), scripts(), statusPage(), user(), webserverTemplates(), fileBackup()
Fetching Data
- Use
->get() to list all resources or fetch a single one by ID.
->get() returns a Ploi\Http\Response object. Use ->getJson() for a stdClass, ->getData() for the data property, or ->toArray() for the full structure.
- When calling
->get($id), the ID parameter is optional if you already passed it during chaining.
Creating Resources
- Each resource has a
create() method with named parameters matching the API. Always check the method signature for required vs. optional parameters.
- Pass options as method arguments, not as raw arrays (unless the method signature accepts one).
Pagination
- Resources with
HasPagination support ->page($pageNumber, $perPage) and ->perPage($amount).
- Example:
$ploi->server($id)->sites()->page(2, 15);
Error Handling
- Wrap API calls in try/catch blocks. The SDK throws typed exceptions based on HTTP status codes:
Ploi\Exceptions\Http\Unauthenticated (401)
Ploi\Exceptions\Http\NotFound (404)
Ploi\Exceptions\Http\NotAllowed (405)
Ploi\Exceptions\Http\NotValid (422)
Ploi\Exceptions\Http\TooManyAttempts (429)
Ploi\Exceptions\Http\InternalServerError (500)
Ploi\Exceptions\Http\PerformingMaintenance (503)
- A
Ploi\Exceptions\Resource\RequiresId is thrown when a resource method needs an ID but none was provided.
Deployment
- Use the
deployment() resource on a site: $ploi->server($id)->sites($siteId)->deployment()->deploy();
- Access and update deploy scripts:
->deployment()->deployScript() and ->deployment()->updateDeployScript($script).
- Quick deploy toggle is on the repository resource:
->repository()->toggleQuickDeploy().
API Call Options
- When making raw API calls or extending the SDK, pass body data as
['body' => json_encode([...])] (Guzzle options format).
- Only
get, post, patch, and delete HTTP methods are supported.
Examples
Initialize the client
use Ploi\Ploi;
$ploi = new Ploi('your-api-token');
List all servers with pagination
$response = $ploi->servers()->page(1, 10);
$servers = $response->getData();
Get a single server
$server = $ploi->server(123)->get();
echo $server->getData()->name;
Create a site on a server
$response = $ploi->server(123)->sites()->create(
domain: 'example.com',
webDirectory: '/public',
projectRoot: '/',
systemUser: 'ploi'
);
Install a repository and deploy
$ploi->server(123)->sites(456)->repository()->install(
provider: 'github',
branch: 'main',
name: 'owner/repo'
);
$ploi->server(123)->sites(456)->deployment()->deploy();
Manage SSL certificates
$certs = $ploi->server(123)->sites(456)->certificates()->get();
$ploi->server(123)->sites(456)->certificates()->create(
certificate: 'example.com',
type: 'letsencrypt'
);
Database management
$ploi->server(123)->databases()->create(
name: 'my_app',
user: 'my_user',
password: 'secret'
);
$ploi->server(123)->databases(789)->backups()->create(
interval: 1440,
type: 'to_server'
);
Queue and worker management
$ploi->server(123)->sites(456)->queues()->create(
connection: 'redis',
queue: 'default',
maximumSeconds: 60,
sleep: 30,
processes: 3,
maximumTries: 3
);
Update environment variables
$ploi->server(123)->sites(456)->environment()->update(
content: "APP_ENV=production\nAPP_DEBUG=false\nAPP_KEY=base64:..."
);
Error handling
use Ploi\Exceptions\Http\NotFound;
use Ploi\Exceptions\Http\NotValid;
use Ploi\Exceptions\Http\Unauthenticated;
try {
$server = $ploi->server(999)->get();
} catch (Unauthenticated $e) {
} catch (NotFound $e) {
} catch (NotValid $e) {
}
Manage daemons
$ploi->server(123)->daemons()->create(
command: 'php artisan horizon',
systemUser: 'ploi',
processes: 1,
directory: '/home/ploi/example.com'
);
$ploi->server(123)->daemons(789)->restart();
Manage cron jobs
$ploi->server(123)->cronjobs()->create(
command: 'php /home/ploi/example.com/artisan schedule:run',
frequency: '* * * * *',
user: 'ploi'
);
Service management
$ploi->server(123)->services('nginx')->restart();
$ploi->server(123)->services('mysql')->restart();
Anti-patterns
Do not re-fetch the client for every call
$servers = (new Ploi($token))->servers()->get();
$sites = (new Ploi($token))->server(1)->sites()->get();
$ploi = new Ploi($token);
$servers = $ploi->servers()->get();
$sites = $ploi->server(1)->sites()->get();
Do not manually build API URLs
$ploi->makeAPICall('servers/123/sites/456/certificates', 'get');
$ploi->server(123)->sites(456)->certificates()->get();
Do not ignore typed exceptions
try {
$ploi->server(123)->get();
} catch (\Exception $e) {
echo "Something went wrong";
}
try {
$ploi->server(123)->get();
} catch (TooManyAttempts $e) {
sleep(60);
} catch (NotFound $e) {
} catch (Unauthenticated $e) {
}
Do not pass IDs twice
$ploi->server(123)->sites(456)->certificates()->get(789);
$ploi->server(123)->sites(456)->certificates(789)->get(789);
$ploi->server(123)->sites(456)->certificates(789)->get();
$ploi->server(123)->sites(456)->certificates()->get(789);
Do not access raw Guzzle responses when the SDK provides helpers
$response = $ploi->servers()->get();
$body = json_decode($response->getResponse()->getBody()->getContents());
$response = $ploi->servers()->get();
$data = $response->getData();
$json = $response->getJson();
$array = $response->toArray();
References