Search by

siberfx / mail-sdk-kerioconnect

siberfx

Kerio Connect SDK for Laravel: SMTP-AUTH mail sending and mailbox provisioning through the Kerio Connect JSON-RPC admin API.

Package info

github.com/siberfx/mail-sdk-kerioconnect

pkg:composer/siberfx/mail-sdk-kerioconnect

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.1.0 2026-09-24 18:17 UTC

This package is auto-updated.

Last update: 2026-09-24 18:18:51 UTC


README

Latest Version on Packagist Tests Total Downloads License

A native Kerio Connect SDK for Laravel applications. It does two things:

  1. Mail sending — SMTP-AUTH straight to your Kerio Connect server on its own Symfony Mailer instance, separate from config/mail.php and the Mail facade. Your existing mailers and Mailables stay untouched; sending through Kerio is an explicit call.
  2. 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 curl calls 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 ftp PHP 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). See addEmailAddress() 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.