Search by

beziworld / laravel-plesk

kamcw

Laravel client for the Plesk REST API and XML API: customers, domains, DNS, databases, mail, SSL, CLI gate and secret keys, for many servers.

Package info

github.com/Beziworld/laravel-plesk

Homepage

Issues

pkg:composer/beziworld/laravel-plesk

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.0 2026-11-04 16:51 UTC

This package is auto-updated.

Last update: 2026-11-04 16:52:40 UTC


README

tests Latest Version License

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.

Polska wersja dokumentacji

Features

  • Multiple named connections (like database connections) plus Plesk::using([...]) for servers stored in your DB
  • API key (X-API-Key for REST, KEY for 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); generic rest() and xml() 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:check to 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, /cli and /auth/keys follow the OpenAPI description served by each Plesk server at https://<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; the extension utility 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.

Need hosting automation, a client panel or automated provisioning? Get in touch through beziworld.eu.

Other packages by the author

License

MIT. See LICENSE.md. This package is not affiliated with or endorsed by WebPros International (Plesk). Plesk is a trademark of its owner.