siberfx / mail-sdk-kerioconnect
Kerio Connect SDK for Laravel: SMTP-AUTH mail sending and mailbox provisioning through the Kerio Connect JSON-RPC admin API.
Requires
- php: ^8.3|^8.4|^8.5
- ext-ftp: *
- guzzlehttp/guzzle: ^7.5
- illuminate/contracts: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- symfony/mailer: ^7.0|^8.0
- symfony/mime: ^7.0|^8.0
Requires (Dev)
- illuminate/cache: ^12.0|^13.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A native Kerio Connect SDK for Laravel applications. It does two things:
- Mail sending — SMTP-AUTH straight to your Kerio Connect server on its own Symfony Mailer
instance, separate from
config/mail.phpand theMailfacade. Your existing mailers and Mailables stay untouched; sending through Kerio is an explicit call. - Mailbox provisioning — create, look up, enable/disable mailboxes and manage aliases on a
Kerio domain through the JSON-RPC admin API. Built for SaaS onboarding (every tenant gets its
own mailbox) without hand-rolled
curlcalls against:4040/admin/api/jsonrpc/.
The scope is deliberately the user level inside an already existing domain. Creating the domain itself (DNS records, the Kerio domain object) is an infrastructure step and stays out of the application.
Requirements
- PHP 8.3, 8.4 or 8.5
- The
ftpPHP extension (ext-ftp) - Laravel 12 or 13
- A Kerio Connect server with the administration API reachable (port 4040 by default)
On most distributions ext-ftp ships with PHP itself; if it is missing, install it with
sudo apt install php8.4-ftp (Debian/Ubuntu), sudo dnf install php-ftp (RHEL/Fedora), or by
enabling extension=ftp in php.ini (Windows). Verify with php -m | grep ftp.
Installation
composer require siberfx/mail-sdk-kerioconnect
That is all. The service provider is registered through Laravel's package auto-discovery, so
there is nothing to add to bootstrap/providers.php or config/app.php, and the KerioConnect
facade alias is registered for you.
Publish the config file only if you want to change the defaults:
php artisan vendor:publish --tag=kerioconnect-config
Configuration
Everything is driven by environment variables; the merged package config works without publishing.
# Admin JSON-RPC (mailbox provisioning) — an account with Users/Domains rights. # The built-in "administrator" works; it has no mailbox, so it cannot send mail. KERIOCONNECT_BASE_URL=https://mail.example.com:4040 KERIOCONNECT_ADMIN_USERNAME=administrator KERIOCONNECT_ADMIN_PASSWORD= # SMTP submission (mail sending) — a real mailbox belonging to this application. KERIOCONNECT_SMTP_HOST=mail.example.com KERIOCONNECT_SMTP_PORT=587 KERIOCONNECT_SMTP_ENCRYPTION=tls KERIOCONNECT_SMTP_USERNAME=operations@example.com KERIOCONNECT_SMTP_PASSWORD= KERIOCONNECT_SMTP_FROM_ADDRESS=noreply@example.com KERIOCONNECT_SMTP_FROM_NAME="${APP_NAME}" # Optional KERIOCONNECT_DEFAULT_DOMAIN=example.com KERIOCONNECT_TOKEN_TTL=600 KERIOCONNECT_CACHE_STORE=redis KERIOCONNECT_VERIFY_TLS=true KERIOCONNECT_TIMEOUT=10
From address: the SMTP mailbox must own the From address — as its login address or as an extra email address (alias). Otherwise Kerio rejects the message with
553 5.1.8(sender does not match the authenticated user). SeeaddEmailAddress()below.
Run php artisan about to see the resolved Kerio Connect settings at a glance.
Usage
Dependency injection (recommended)
Every component is bound against a contract, so type-hint what you need and Laravel injects it — in controllers, jobs, commands, listeners, anywhere the container resolves:
use Siberfx\MailSdkKerioConnect\Contracts\MailboxProvisioner; use Siberfx\MailSdkKerioConnect\Contracts\MailTransport; final class OnboardTenant { public function __construct( private readonly MailboxProvisioner $mailboxes, private readonly MailTransport $mail, ) {} public function handle(Tenant $tenant): void { if (! $this->mailboxes->exists($tenant->domain, 'support')) { $this->mailboxes->create( domain: $tenant->domain, login: 'support', password: Str::password(20), fullName: "{$tenant->name} Support", ); } $this->mail->send( to: $tenant->owner_email, subject: 'Your mailbox is ready', htmlBody: view('mail.mailbox-ready', ['tenant' => $tenant])->render(), ); } }
Available contracts, all in Siberfx\MailSdkKerioConnect\Contracts:
| Contract | Implementation | Purpose |
|---|---|---|
MailTransport |
MailSender |
Sending mail over Kerio's SMTP submission |
MailboxProvisioner |
MailboxManager |
Mailbox and alias management |
RpcClient |
JsonRpcClient |
Raw admin JSON-RPC calls |
Facade
For quick call sites, the KerioConnect facade exposes the same three components:
use Siberfx\MailSdkKerioConnect\Facades\KerioConnect; KerioConnect::mail()->send( to: 'customer@example.com', subject: 'Welcome', htmlBody: '<p>Welcome aboard.</p>', ); KerioConnect::mailboxes()->create('tenant.test', 'support', $password, 'Tenant Support');
Components are resolved from the container on first use, so an application that only sends mail never opens an admin API session.
Sending mail
$mail->send( to: ['a@example.com', 'b@example.com'], subject: 'Invoice #123', htmlBody: $html, options: [ 'from' => 'billing@example.com', // must be an address of the SMTP mailbox 'fromName' => 'Billing', 'replyTo' => 'support@example.com', 'cc' => 'archive@example.com', 'bcc' => ['audit@example.com'], ], );
For attachments, text parts or custom headers, compose a Symfony Email yourself:
use Symfony\Component\Mime\Email; $mail->sendRaw( (new Email) ->from('noreply@example.com') ->to('customer@example.com') ->subject('Your invoice') ->html($html) ->text($plain) ->attachFromPath(storage_path('app/invoices/123.pdf')) );
Delivery failures throw Siberfx\MailSdkKerioConnect\Exceptions\KerioConnectException.
Mailbox provisioning
$mailboxes->list('example.com'); // all mailboxes on the domain $mailboxes->find('example.com', 'support'); // one mailbox, or null $mailboxes->exists('example.com', 'support'); // bool $mailboxes->create( domain: 'example.com', login: 'support', password: Str::password(20), fullName: 'Support Desk', isEnabled: true, ); $mailboxes->setEnabled('example.com', 'support', false); $mailboxes->setPassword('example.com', 'support', $newPassword);
create() is deliberately not an upsert: it throws when the login already exists, so
provisioning code can never silently reset a live mailbox's password. Check exists() first.
Aliases
// Makes noreply@example.com a valid From address on the operations@ mailbox. $mailboxes->addEmailAddress('example.com', 'operations', 'noreply');
Two Kerio quirks the SDK handles for you: emailAddresses is a plain array of local parts (not
objects), and Users.set replaces the field wholesale — so the current list is read back and the
alias appended. The call is idempotent.
Raw JSON-RPC
For admin API methods that have no wrapper yet:
$result = $rpc->call('Domains.get', [ 'query' => ['fields' => ['id', 'name']], ]);
Session handling
Every call() reuses a cached session — the X-Token header and the cookie together, because
Kerio treats them as one pair — stored for KERIOCONNECT_TOKEN_TTL seconds (default 600) in the
configured cache store. On a session error the client logs in again exactly once and replays
the call, never more, so a bad credential can never turn into a login loop against the mail server.
Use a shared cache store (redis, file, database) in production so queue workers reuse one
session instead of each opening their own. Domain ids are cached for 5 minutes as well.
Error handling
| Exception | When |
|---|---|
KerioConnectAuthException |
Login failed, or a session was rejected |
KerioConnectException |
Everything else: RPC errors, HTTP errors, delivery failures |
KerioConnectAuthException extends KerioConnectException, so catching the latter catches both.
use Siberfx\MailSdkKerioConnect\Exceptions\KerioConnectException; try { $mailboxes->create('example.com', 'support', $password, 'Support'); } catch (KerioConnectException $e) { report($e); }
Testing
composer install
composer test
Unit tests mock the HTTP layer (Guzzle's MockHandler) and the feature tests boot a real Laravel
container through Testbench — the suite never talks to a mail server.
Code style is enforced with Laravel Pint:
composer format
Security
If you discover a security issue, please email info@siberfx.com instead of opening a public issue.
License
The MIT License (MIT). See LICENSE.