Send email from Laravel
Five steps: nothing to install, create a test key, send to the sandbox from a route, read the log, then verify a domain from an Artisan command so you can send to anyone.
1. Install
No SDK needed. The API is plain JSON over HTTPS, and Laravel's Http facade already handles bearer tokens, JSON bodies and error checks.
2. Create an API key
Sign in, open API keys in the dashboard and create a test key. It starts with av_test_. Add it to .env and expose it through config/services.php, the usual place for third-party credentials:
# .envAVELTO_API_KEY=av_test_...// config/services.php'avelto' => [ 'key' => env('AVELTO_API_KEY'),],3. Send your first email
A POST route in routes/api.php. On Laravel 11 and later that file only exists once you have run php artisan install:api, and its routes are served under /api. A rejected send comes back as a non-2xx status with { "error": { "code", "message" } }; the route passes that through unchanged.
<?php// routes/api.phpuse Illuminate\Support\Facades\Http;use Illuminate\Support\Facades\Route;Route::post('/send', function () { $response = Http::withToken(config('services.avelto.key')) ->post('https://api.avelto.dev/v1/emails', [ 'from' => 'you@sandbox.avelto.dev', 'to' => 'delivered@sandbox.avelto.dev', 'subject' => 'Hello from Avelto', 'text' => 'It works.', ]); if ($response->failed()) { // { "error": { "code": "...", "message": "..." } } return response()->json($response->json(), $response->status()); } return response()->json(['id' => $response->json('id')], 201);});Run php artisan serve and call the route:
curl -X POST http://localhost:8000/api/sendThe API answers 201 Created with the email id, and the route returns the same:
HTTP/1.1 201 CreatedContent-Type: application/json{ "id": "9c1f4a52-6f6e-4b8f-9b8e-2e1a5c7d3f10" }4. Check the log
A GET route fetches the email by id. status moves from queued to sent to delivered, and events records each step: email.queued, email.sent, email.delivered.
Route::get('/emails/{id}', function (string $id) { $email = Http::withToken(config('services.avelto.key')) ->get("https://api.avelto.dev/v1/emails/{$id}") ->throw() ->json(); return response()->json([ 'status' => $email['status'], // "queued", then "sent", then "delivered" 'events' => array_column($email['events'], 'type'), ]);});curl http://localhost:8000/api/emails/9c1f4a52-6f6e-4b8f-9b8e-2e1a5c7d3f105. Verify a domain
Adding a domain is a one-off task, so it fits an Artisan command. Publish the DNS records it prints (an ownership TXT, an SPF TXT and a DMARC TXT, then three DKIM CNAMEs once the ownership record is found) at your DNS provider; the command polls GET /v1/domains/:id, which re-checks DNS on every call, until status is verified. Use a subdomain such as mail.acme.com.
<?php// routes/console.phpuse Illuminate\Support\Facades\Artisan;use Illuminate\Support\Facades\Http;Artisan::command('avelto:verify {name}', function (string $name) { $http = Http::withToken(config('services.avelto.key')); $domain = $http->post('https://api.avelto.dev/v1/domains', ['name' => $name]) ->throw() ->json(); $show = function (array $d) { foreach ($d['dns_records'] as $r) { $this->line("{$r['type']}\t{$r['name']}\t{$r['value']}\t({$r['purpose']})"); } }; $show($domain); // the ownership record first, with SPF and DMARC // Publish the records, then poll. GET re-checks DNS on every call. // Once the ownership record is found, the DKIM records appear: publish those too. while ($domain['status'] === 'pending') { sleep(30); $latest = $http ->get("https://api.avelto.dev/v1/domains/{$domain['id']}") ->json(); if ($latest['ownership_verified'] && !$domain['ownership_verified']) { $show($latest); } $domain = $latest; } $this->info($domain['status']); // "verified" or "failed"});php artisan avelto:verify mail.acme.comOnce the domain is verified, switch AVELTO_API_KEY in .env to a live key (av_live_) and change from in the route to an address on it, such as hello@mail.acme.com. Nothing else changes.
Next
- Send email: every field, attachments, tags, scheduling and idempotency.
- Webhooks: get events pushed to your app.
- Test mode: test keys, the sandbox sender and the simulator addresses.