mailkite / laravel
MailKite for Laravel — a native mail transport so MAIL_MAILER=mailkite just works. Send over your verified domain, receive inbound email as a webhook.
Requires
- php: ^8.2
- illuminate/mail: ^11.0 || ^12.0
- illuminate/support: ^11.0 || ^12.0
- mailkite/mailkite: >=0.11 <2.0
- symfony/mailer: ^7.0
Requires (Dev)
- orchestra/testbench: ^9.0 || ^10.0
- phpunit/phpunit: ^10.5 || ^11.0 || ^12.0
README
MailKite for Laravel
Email for every product you ship — send over a verified domain, receive email as a webhook, give an AI agent its own inbox.
The official MailKite mail transport for Laravel: MAIL_MAILER=mailkite just works.
Docs · Library guide · mailkite.dev · AI agents
Read-only mirror. This repo is a generated, release-time mirror of the MailKite monorepo (the private source of truth) — development doesn't happen here. Install from Packagist and open issues against the MailKite docs.
Install
composer require mailkite/laravel
Requires PHP 8.2+ and Laravel 11 or 12. The service provider is auto-discovered — no manual registration.
Configure
Add your API key to .env (create one in the dashboard):
MAIL_MAILER=mailkite MAILKITE_API_KEY=mk_live_… MAIL_FROM_ADDRESS=hello@yourdomain.com MAIL_FROM_NAME="Your App"
Register the mailer in config/mail.php:
'mailers' => [ // … 'mailkite' => [ 'transport' => 'mailkite', ], ],
And the credentials in config/services.php (the standard Laravel home for provider keys):
'mailkite' => [ 'key' => env('MAILKITE_API_KEY'), ],
That's it — every Mail::send() in your app now delivers through MailKite. The from domain must be verified (SPF + DKIM) on your MailKite account.
Send
Any Laravel Mailable works unchanged:
use App\Mail\InvoicePaid; use Illuminate\Support\Facades\Mail; Mail::to('ada@example.com')->send(new InvoicePaid($invoice));
namespace App\Mail; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Attachment; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; class InvoicePaid extends Mailable { public function __construct(public \App\Models\Invoice $invoice) {} public function envelope(): Envelope { return new Envelope( subject: "Your invoice #{$this->invoice->number}", replyTo: ['support@yourdomain.com'], ); } public function content(): Content { return new Content(markdown: 'mail.invoice-paid'); } public function attachments(): array { return [ Attachment::fromData(fn () => $this->invoice->pdf(), 'invoice.pdf') ->withMime('application/pdf'), ]; } }
What maps where
| Laravel / Symfony Email | MailKite send |
|---|---|
| from, to, cc, bcc | from, to, cc, bcc (display names preserved) |
| subject | subject |
| HTML + text bodies | html, text |
| reply-to (single) | replyTo |
In-Reply-To header |
inReplyTo (threading) |
| attachments | attachments — base64 content + filename + contentType |
Honest failures, not silent drops. The MailKite send API carries a fixed envelope, so the transport throws a TransportException (with the API's own error message) instead of pretending:
- API errors (unverified domain, suppressed recipient, rate limit, …) surface verbatim with their HTTP status.
- Custom headers (e.g.
X-Campaign, Laravel's->tag()/->metadata()) are refused — the API can't deliver them. - Inline (
cid:-embedded) images are refused — host the image at a URL instead. - Multiple reply-to addresses are refused — the API takes one.
Receive inbound email (webhook)
MailKite delivers inbound mail to your app as a signed webhook. Verify the signature with the bundled PHP SDK — this package binds \MailKite\Client into the container:
// routes/api.php Route::post('/inbound-email', InboundEmailController::class);
namespace App\Http\Controllers; use Illuminate\Http\Request; use MailKite\Client; class InboundEmailController { public function __invoke(Request $request, Client $mailkite) { $verified = $mailkite->verifyWebhook( $request->header('x-mailkite-signature', ''), $request->getContent(), // raw, unparsed body config('services.mailkite.webhook_secret'), ); abort_unless($verified, 401); $email = $request->json()->all(); // $email['from'], $email['subject'], $email['text'], … return response($mailkite->replyOk(), 200)->header('Content-Type', 'application/json'); } }
Add the webhook secret (shown when you set the domain's webhook) to config/services.php as services.mailkite.webhook_secret, and remember to exempt the route from CSRF if you register it in web.php. Full inbound guide: https://mailkite.dev/docs.
All MailKite libraries
Same contract, every language (full list: https://mailkite.dev/docs/libraries):
| Library | Repo | Distribution |
|---|---|---|
| MailKite for Laravel (this repo) | laravel |
Packagist |
| MailKite for PHP | mailkite-php |
Packagist |
| MailKite for Node.js | mailkite-node |
npm |
| MailKite for Python | mailkite-python |
PyPI |
| MailKite for Ruby | mailkite-ruby |
RubyGems |
| MailKite for Java | mailkite-java |
Maven Central |
| MailKite for Go | mailkite-go |
Go modules |
| @mailkite/cli | mailkite-cli |
npm |
| @mailkite/mcp | mailkite-mcp |
npm |
Docs & links
- 📚 Documentation: https://mailkite.dev/docs
- 📦 This library's guide: https://mailkite.dev/docs/libraries
- 🤖 AI agents (MCP + inbox agents): https://mailkite.dev/docs/ai-agents
- 🌐 Website: https://mailkite.dev
MIT licensed. © MailKite.