New from emailrHealthCheck Email iconHealthCheck Email
emailr_

Send email from Laravel with emailr_

Send transactional email from Laravel with the emailr_ HTTP API: render Mailables, queue sends with jobs and verify signed delivery webhooks.

Laravel iconLaravelFramework

Laravel's HTTP client is all you need to talk to emailr_. This guide wraps the API in a small service, reuses your existing Mailable views for the HTML, sends from queued jobs and verifies webhooks.

There's no PHP SDK yet, so every example calls the REST API at https://api.emailr.dev directly.

Set up emailr_ with Laravel

  1. 01

    Create an API key

    Create a key under API keys in the dashboard. Keys start with et_live_ and are scoped to one domain workspace. Store it as EMAILR_API_KEY in your secret manager or environment, never in client-side code.

  2. 02

    Add and verify your sending domain

    In the dashboard, open Domains and add the domain you send from. A subdomain such as mail.yourcompany.com keeps transactional reputation separate from your corporate mailbox.

    Emailr generates the DNS records: a DKIM TXT record on the emailr._domainkey selector, an SPF TXT record, and an MX + TXT pair on the send subdomain used as the MAIL FROM / Return-Path. A DMARC record is optional but recommended.

    Emailr's DKIM selector and send subdomain don't collide with other providers' records, so you can add them before cutting over and send from both providers during the migration. A domain can only have one SPF record: if one already exists, merge Emailr's include: into it instead of adding a second record.

  3. 03

    Add the key to your config

    Add EMAILR_API_KEY and EMAILR_WEBHOOK_SECRET to .env, then expose them through config/services.php so they're cached with the rest of your config.

    config/services.phpphp
    'emailr' => [
        'key' => env('EMAILR_API_KEY'),
        'webhook_secret' => env('EMAILR_WEBHOOK_SECRET'),
    ],
  4. 04

    Create a small client

    POST /v1/emails/send takes JSON with from, to, subject, html and/or text, plus optional cc, bcc, reply_to_email, tags, attachments, template_id, template_data and scheduled_at. ->throw() turns 4xx/5xx responses into exceptions so failed jobs retry.

    app/Services/Emailr.phpphp
    <?php
    
    namespace App\Services;
    
    use Illuminate\Support\Facades\Http;
    
    class Emailr
    {
        public function send(array $payload): array
        {
            return Http::withToken(config('services.emailr.key'))
                ->acceptJson()
                ->post('https://api.emailr.dev/v1/emails/send', $payload)
                ->throw()
                ->json();
        }
    }
  5. 05

    Send a Mailable's HTML from a queued job

    Keep writing Blade Mailables and call render() to get the HTML string. Sending from a queued job keeps web requests fast and gives you retries for free.

    app/Jobs/SendWelcomeEmail.phpphp
    <?php
    
    namespace App\Jobs;
    
    use App\Mail\WelcomeMail;
    use App\Models\User;
    use App\Services\Emailr;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Foundation\Queue\Queueable;
    
    class SendWelcomeEmail implements ShouldQueue
    {
        use Queueable;
    
        public function __construct(public User $user) {}
    
        public function handle(Emailr $emailr): void
        {
            $emailr->send([
                'from' => 'Acme <[email protected]>',
                'to' => $this->user->email,
                'subject' => 'Welcome to Acme',
                'html' => (new WelcomeMail($this->user))->render(),
                'tags' => ['category' => 'welcome'],
            ]);
        }
    }
    
    // SendWelcomeEmail::dispatch($user);
  6. 06

    Verify delivery webhooks

    Register the route in routes/api.php so it skips CSRF protection. Compute the HMAC over the raw body from getContent() and compare with hash_equals.

    routes/api.phpphp
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Route;
    
    Route::post('/webhooks/emailr', function (Request $request) {
        $expected = hash_hmac('sha256', $request->getContent(), config('services.emailr.webhook_secret'));
    
        abort_unless(hash_equals($expected, (string) $request->header('X-Emailr-Signature')), 401);
    
        $event = $request->input('event');   // e.g. "email.bounced"
        $data = $request->input('data');
    
        return response()->noContent();
    });

Tips

  • On Laravel 11 and later, run php artisan install:api once if your app doesn't have routes/api.php yet.
  • Attachments are JSON objects: ['filename' => 'invoice.pdf', 'content' => base64_encode($pdf), 'contentType' => 'application/pdf'].
  • Use stored emailr_ templates with template_id and template_data if you'd rather edit copy in the dashboard than in Blade.

Frequently asked questions

Is there a Laravel mail driver for emailr_?

Not yet. Call the REST API with Laravel's HTTP client as shown here. You can keep using Mailables for rendering by calling render() and sending the HTML.

Can I queue emails in Laravel with emailr_?

Yes. Send from a job that implements ShouldQueue. Because the client calls throw(), failed API calls raise exceptions and the queue retries them.

How do I verify emailr_ webhooks in Laravel?

Compute hash_hmac('sha256', $request->getContent(), $secret) and compare it to the X-Emailr-Signature header with hash_equals. Register the route in routes/api.php to avoid CSRF checks.

Can I send attachments from Laravel?

Yes. Pass an attachments array of objects with filename, base64 content and an optional contentType.

Start sending in minutes

Create a free account, verify your domain and send your first email. No credit card required.