Laravel
Laravel gives you a choice, and it is a real one.
Repoint Mail at our SMTP if you already have Mailables and want them to keep working.
Nothing in your code changes.
Call the API when you want tags, metadata, scheduling, idempotency or the delivery timeline. Those have no SMTP equivalent.
Most applications end up doing both.
Repointing Mail
# .env
MAIL_MAILER=smtp
MAIL_HOST=smtp.epostix.com
MAIL_PORT=587
MAIL_USERNAME=your-smtp-user
MAIL_PASSWORD=your-smtp-password
MAIL_ENCRYPTION=tls
Every existing Mail::to(...)->send(...) now goes through us. Credentials come from
API Keys & SMTP in the dashboard, and are separate from API keys.
Calling the API
use Illuminate\Support\Facades\Http;
$response = Http::withToken(config('services.epostix.key'))
->withHeaders(['Idempotency-Key' => "order-{$order->id}-confirmation"])
->post('https://api.epostix.com/v1/emails', [
'to' => [$order->customer_email],
'subject' => "Order {$order->reference} confirmed",
'html' => view('mail.order-confirmed', compact('order'))->render(),
'tags' => ['order-confirmation'],
'metadata' => ['order_id' => (string) $order->id],
]);
if ($response->failed()) {
Log::error('epostix send failed', [
'type' => $response->json('type'),
'request_id' => $response->json('request_id'),
]);
}
Add the key to config/services.php rather than reading env() at call time, so config
caching does not leave you with a null token in production.
Queued jobs need idempotency keys
This is the part Laravel applications get wrong. A queued job can run more than once: a worker restart, a timeout, or a failed release all redeliver it. Without a key, each attempt sends another email.
class SendOrderConfirmation implements ShouldQueue
{
public function handle(): void
{
Http::withToken(config('services.epostix.key'))
->withHeaders(['Idempotency-Key' => "order-{$this->order->id}-confirmation"])
->post('https://api.epostix.com/v1/emails', [...]);
}
}
The key is derived from the order, so every attempt of every retry resolves to one email. See Idempotency.
Local development
Point the queue at a sandbox address or use a tix_test_ key, and nothing
leaves the platform while you are working.
Next
- SMTP credentials for the mailer route
- Webhooks to record deliveries and bounces against the order