Skip to main content

Laravel SDK

The jetemail-laravel package is the official Laravel SDK for JetEmail transactional email (MIT license). Create a transactional API key in the dashboard: OutboundKeys → Add API key. Use that key with the SDK.

Installation

composer require jetemail/jetemail-laravel
The service provider and facade are auto-discovered by Laravel.

Configuration

Add your transactional API key to your .env file:
JETEMAIL_API_KEY=your-api-key
Optionally publish the config file:
php artisan vendor:publish --tag=jetemail-config

Laravel Mail Integration

Use JetEmail as a Laravel mail driver. Add the mailer to your config/mail.php:
'mailers' => [
    'jetemail' => [
        'transport' => 'jetemail',
    ],
],
Set it as your default mailer in .env:
MAIL_MAILER=jetemail
Then use Laravel’s standard Mail API:
use Illuminate\Support\Facades\Mail;

Mail::to('[email protected]')->send(new WelcomeEmail());
This works with all Laravel Mailables and Notifications out of the box.

Direct API Usage

Send a single email

use JetEmail\Laravel\Facades\JetEmail;
use JetEmail\Laravel\Data\SendEmailOptions;

$result = JetEmail::email->send(new SendEmailOptions(
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Hello!',
    html: '<h1>Welcome</h1><p>Thanks for signing up.</p>',
));

// $result['id'] - the message ID

Send with plain text

$result = JetEmail::email->send(new SendEmailOptions(
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Hello!',
    text: 'Thanks for signing up.',
));

Multiple recipients, CC, BCC, Reply-To

$result = JetEmail::email->send(new SendEmailOptions(
    from: '[email protected]',
    to: ['[email protected]', '[email protected]'],
    subject: 'Team Update',
    html: '<p>Here is the latest update.</p>',
    cc: '[email protected]',
    bcc: ['[email protected]'],
    replyTo: '[email protected]',
));

Attachments

use JetEmail\Laravel\Data\Attachment;

$result = JetEmail::email->send(new SendEmailOptions(
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Invoice',
    html: '<p>Please find your invoice attached.</p>',
    attachments: [
        Attachment::fromPath('/path/to/invoice.pdf'),
        Attachment::fromContent('Hello World', 'hello.txt'),
    ],
));

Custom headers

$result = JetEmail::email->send(new SendEmailOptions(
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Tracked Email',
    html: '<p>Hello</p>',
    headers: [
        'X-Campaign-Id' => 'welcome-2024',
    ],
));

Batch send (up to 100 emails)

$result = JetEmail::batch->send([
    new SendEmailOptions(
        from: '[email protected]',
        to: '[email protected]',
        subject: 'Hello Alice',
        html: '<p>Hi Alice!</p>',
    ),
    new SendEmailOptions(
        from: '[email protected]',
        to: '[email protected]',
        subject: 'Hello Bob',
        html: '<p>Hi Bob!</p>',
    ),
]);

// $result['summary']['successful'] - number of emails sent
// $result['results'] - per-email results

Without the facade

use JetEmail\Laravel\JetEmail;

$jetemail = app(JetEmail::class);
$jetemail->email->send(new SendEmailOptions(...));

Without Laravel

use JetEmail\Laravel\JetEmail;
use JetEmail\Laravel\Data\SendEmailOptions;

$jetemail = new JetEmail(apiKey: 'your-api-key');
$jetemail->email->send(new SendEmailOptions(
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Hello!',
    html: '<p>Hello World</p>',
));

Webhooks

JetEmail can send webhook events to your application for email delivery events.

Setup

Add your webhook secret to .env:
JETEMAIL_WEBHOOK_SECRET=your-webhook-secret
The webhook endpoint is automatically registered at POST /jetemail/webhook. Point your JetEmail dashboard webhook URL to https://yourdomain.com/jetemail/webhook.

Configuration

You can customise the webhook route prefix and domain:
JETEMAIL_PATH=jetemail
JETEMAIL_DOMAIN=webhooks.yourdomain.com

Listening for events

Listen for webhook events in your EventServiceProvider or using Event::listen():
use JetEmail\Laravel\Events\Outbound\MessageDelivered;
use JetEmail\Laravel\Events\Outbound\MessageBounced;
use JetEmail\Laravel\Events\Outbound\MessageOpened;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        MessageDelivered::class => [
            HandleDeliveredEmail::class,
        ],
        MessageBounced::class => [
            HandleBouncedEmail::class,
        ],
    ];
}
In your listener:
class HandleDeliveredEmail
{
    public function handle(MessageDelivered $event): void
    {
        $payload = $event->payload;

        // $payload['id']      - event ID
        // $payload['type']    - 'outbound.delivered'
        // $payload['from']    - sender
        // $payload['to']      - recipient
        // $payload['subject'] - subject line
        // $payload['mx']      - receiving mail server
    }
}

Available events

Event ClassWebhook Type
Events\Outbound\MessageQueuedoutbound.queued
Events\Outbound\MessageDeliveredoutbound.delivered
Events\Outbound\MessageDeferredoutbound.deferred
Events\Outbound\MessageBouncedoutbound.bounced
Events\Outbound\MessageRejectedoutbound.rejected
Events\Outbound\MessageSpamoutbound.spam
Events\Outbound\MessageVirusoutbound.virus
Events\Outbound\MessageDroppedoutbound.dropped
Events\Outbound\MessageOpenedoutbound.opened
Events\Outbound\MessageClickedoutbound.clicked
Events\Outbound\MessageComplaintoutbound.complaint

Signature verification

Webhook signatures are automatically verified when JETEMAIL_WEBHOOK_SECRET is set. The middleware validates:
  • HMAC-SHA256 signature via the X-Webhook-Signature header
  • Timestamp freshness via X-Webhook-Timestamp (default: 5-minute tolerance)
You can adjust the tolerance (in seconds):
JETEMAIL_WEBHOOK_TOLERANCE=300

Error Handling

use JetEmail\Laravel\Exceptions\JetEmailException;

try {
    JetEmail::email->send(new SendEmailOptions(...));
} catch (JetEmailException $e) {
    $e->getMessage();   // Error message
    $e->statusCode;     // HTTP status code
    $e->response;       // Full API error response
}

For the full source and additional examples, see the GitHub repository.