beziworld / laravel-plesk
Laravel client for the Plesk REST API and XML API: customers, domains, DNS, databases, mail, SSL, CLI gate and secret keys, for many servers.
Requires
- php: ^8.3
- ext-dom: *
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- illuminate/console: ^12.0|^13.0
- illuminate/contracts: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.7|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
- phpstan/phpstan-strict-rules: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-04 16:52:40 UTC
README
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.