laravel-plesk maintained by beziworld
Laravel Plesk
Laravel client for Plesk: the REST API (/api/v2) for customers, domains, DNS, databases,
API keys and the CLI gate, and the XML API (agent.php) for mail and server statistics, which REST does not cover.
Works with any number of servers.
Features
- Multiple named connections (like database connections) plus
Plesk::using([...])for servers stored in your DB - API key (
X-API-Keyfor REST,KEYfor XML) or login and password authentication - Resources:
clients,domains,dns,databases,mail,ssl,cli,keys,server - Let's Encrypt certificates and one-time login links through the REST CLI gate
- Typed, immutable DTOs (
Client,Domain,DnsRecord,Database,ServerInfo,CliResult,CreatedObject) and request objects (ClientRequest,DomainRequest); genericrest()andxml()for everything else - XML packets are built from PHP arrays and parsed back into arrays; packet-level and per-result errors become
exceptions with the Plesk
errcode - Requests are never retried automatically; configurable TLS verification for self-signed certificates
php artisan plesk:checkto test a connection- PHP 8.3+, Laravel 12 and 13, PHPStan level 8
Installation
composer require beziworld/laravel-plesk
php artisan vendor:publish --tag=plesk-config # optional
PLESK_HOST=plesk.example.com # port 8443 is added automatically
PLESK_API_KEY=78711059-23bb-cf6f-b07f-985e1995d2e2
# or PLESK_USERNAME=admin and PLESK_PASSWORD=...
PLESK_VERIFY=true # false or a CA bundle path for self-signed certificates
php artisan plesk:check
Create an API key bound to the IP address of your application with plesk bin secret_key -c -ip-address <IP> or
with Plesk::keys()->create(ip: '...') on a connection that uses a login and password.
Usage examples
Provision a customer and a subscription
use Beziworld\Plesk\Data\ClientRequest;
use Beziworld\Plesk\Data\DomainRequest;
use Beziworld\Plesk\Exceptions\ApiException;
use Beziworld\Plesk\Facades\Plesk;
try {
$customer = Plesk::clients()->create(new ClientRequest(
name: $order->customer_name,
login: $order->username,
password: Str::password(16),
email: $order->email,
company: $order->company,
externalId: 'order:' . $order->id,
));
$subscription = Plesk::domains()->create(new DomainRequest(
name: $order->domain,
ftpLogin: $order->username . '_ftp',
ftpPassword: Str::password(16),
plan: $order->plan->plesk_plan, // service plan name
ownerLogin: $order->username,
));
} catch (ApiException $e) {
// e.g. "Plesk POST /api/v2/clients failed: Validation failed [login: ...] (code 1)"
report($e);
}
Plesk::mail()->create($order->domain, 'office', Str::password(16), quotaBytes: 2 * 1024 ** 3);
Plesk::ssl()->issueLetsEncrypt($order->domain, $order->email, ['www.' . $order->domain]);
Customers and subscriptions
Plesk::clients()->list(); // list<Client>
Plesk::clients()->findByLogin('john')?->id;
Plesk::clients()->update(3, ['email' => 'new@example.com']);
Plesk::clients()->domains(3); // list<Domain>
$domain = Plesk::domains()->findByName('example.com');
Plesk::domains()->suspend($domain->id);
Plesk::domains()->activate($domain->id);
Plesk::domains()->delete($domain->id); // cannot be undone
DNS
Plesk::dns()->records('example.com'); // list<DnsRecord>: id, type, host, content, opt
Plesk::dns()->create('example.com', 'TXT', '_acme-challenge', 'token-value');
Plesk::dns()->create('example.com', 'MX', '', 'mail.example.com.', opt: '10');
foreach (Plesk::dns()->find('example.com', 'TXT', '_acme-challenge.example.com') as $record) {
Plesk::dns()->delete($record->id);
}
Databases
$database = Plesk::databases()->create('example.com', 'shop');
$user = Plesk::databases()->createUser($database->id, 'shop_app', $password);
Plesk::databases()->changeUserPassword($user->id, $rotated);
Plesk::databases()->list('example.com');
Mail (XML API)
$mail = Plesk::mail();
$mail->create('example.com', 'office', $password, quotaBytes: 1024 ** 3);
$mail->changePassword('example.com', 'office', $newPassword);
$mail->setQuota('example.com', 'office', -1); // unlimited
$mail->setForwarding('example.com', 'sales', ['anna@example.org']);
$mail->list('example.com'); // ['office', 'sales']
$mail->delete('example.com', 'office');
SSL, login links and the CLI gate
Plesk::ssl()->issueLetsEncrypt('example.com', 'admin@example.com', ['www.example.com'], secureMail: true);
return redirect()->away(Plesk::cli()->loginLink()); // one-time admin login
Plesk::cli()->loginLink('john'); // one-time login of a user
Plesk::cli()->commands(); // utilities exposed by the gate
$result = Plesk::cli()->call('domain', ['--info', 'example.com']); // CliResult: code, stdout, stderr
Plesk::cli()->run('subscription', ['--webspace-off', 'example.com']); // throws when the exit code is not 0
Many servers
// config/plesk.php
'connections' => [
'default' => ['host' => env('PLESK1_HOST'), 'api_key' => env('PLESK1_KEY')],
'eu2' => ['host' => env('PLESK2_HOST'), 'api_key' => env('PLESK2_KEY')],
],
Plesk::connection('eu2')->domains()->list();
Plesk::using([
'host' => $server->hostname,
'api_key' => decrypt($server->api_key),
'verify' => ! $server->self_signed,
])->server()->info();
Anything else
Plesk::rest('GET', 'ftpusers', ['domain' => 'example.com']); // any REST endpoint
Plesk::xml(['webspace' => ['get' => [ // any XML API packet
'filter' => ['name' => 'example.com'],
'dataset' => ['hosting' => null, 'limits' => null],
]]]);
Plesk::connection()->connection()->xmlResults([...]); // results with per-result error checks
Exceptions
All exceptions extend PleskException:
| Exception | When |
|---|---|
AuthenticationException |
REST HTTP 401, XML HTTP 401/403 or XML errcode 1001. Never retried. |
ApiException |
REST 4xx/5xx ({"code", "message"}), XML <status>error</status>, failed CLI call via run(), unexpected response or transport error (operation(), errorCode(), httpStatus(), payload()) |
InvalidConfiguration |
Unknown connection, missing host or credentials |
Notes
- REST endpoints beyond
/server,/clients,/domains,/cliand/auth/keysfollow the OpenAPI description served by each Plesk server athttps://<host>:8443/api/v2/openapi.yml; check it when a call fails on an older Plesk release. - Let's Encrypt uses
plesk bin extension --exec letsencrypt cli.php; theextensionutility must be exposed by the CLI gate (Plesk::cli()->commands()). - Customers and resellers can use the REST API since Plesk Obsidian 18.0.79 when the administrator allows it.
Development
composer install
composer ci # pint --test, phpstan (level 8), pest
Author
Created and maintained by Kamil Ćwiertnia, beziworld.eu.
- E-mail: biuro@beziworld.eu
- Issues and feature requests: GitHub issues
- Security vulnerabilities: see SECURITY.md
Need hosting automation, a client panel or automated provisioning? Get in touch through beziworld.eu.
Other packages by the author
- beziworld/laravel-cpanel: cPanel UAPI and WHM API 1
- beziworld/laravel-directadmin: DirectAdmin hosting panel
- beziworld/laravel-cloudflare: Cloudflare DNS, cache and Turnstile
License
MIT. See LICENSE.md. This package is not affiliated with or endorsed by WebPros International (Plesk). Plesk is a trademark of its owner.