resend/resend-php
Resend PHP is an official PHP 8.1+ client for the Resend email API. Install via Composer and send transactional emails with a clean, simple interface (e.g., $resend->emails->send) in PHP or Laravel.
Installation
composer require resend/resend-php
Add to composer.json if using Laravel:
"require": {
"resend/resend-php": "^1.3"
}
First Use Case: Sending an Email Initialize the client in a service or controller:
use Resend\ResendClient;
$resend = new ResendClient('re_your_api_key_here');
$response = $resend->emails->send([
'from' => 'acme@yourdomain.com',
'to' => ['user@example.com'],
'subject' => 'Welcome!',
'html' => '<p>Hello, welcome to our platform!</p>',
]);
Where to Look First
automations, events).Transactional Emails
$resend->emails->send([
'from' => 'notifications@yourdomain.com',
'to' => ['user@example.com'],
'subject' => 'Your order is confirmed',
'text' => 'Order #12345 is processing...',
'headers' => ['X-Custom-ID' => 'order_123'],
]);
Templated Emails
$templateId = 'your-template-id';
$response = $resend->emails->send([
'from' => 'marketing@yourdomain.com',
'to' => ['user@example.com'],
'template_id' => $templateId,
'template_data' => ['name' => 'John'],
]);
Batch Emails
$batch = $resend->batches->create([
'emails' => [
['to' => 'user1@example.com', 'subject' => 'Batch Email 1'],
['to' => 'user2@example.com', 'subject' => 'Batch Email 2'],
],
'options' => ['batch_id' => 'unique_batch_123'],
]);
Contacts Management
// Create/update a contact
$contact = $resend->contacts->upsert([
'email' => 'user@example.com',
'first_name' => 'John',
'last_name' => 'Doe',
]);
// Add to a segment
$resend->contacts->segments->upsert($contact->id, ['segment_id' => 'premium_users']);
Webhooks (Laravel Integration)
// In a Laravel route (e.g., `webhook/resend`)
$payload = json_decode(request()->getContent(), true);
$event = $resend->webhooks->verify($payload, request()->header('X-Resend-Signature'));
Service Provider Binding
// app/Providers/ResendServiceProvider.php
public function register()
{
$this->app->singleton(ResendClient::class, function ($app) {
return new ResendClient(config('services.resend.api_key'));
});
}
Mailable Integration
Extend Laravel’s Mailable to use Resend:
use Resend\ResendClient;
class WelcomeMail extends Mailable
{
public function build()
{
$resend = app(ResendClient::class);
return $this->withSwiftMessage(function ($message) use ($resend) {
$message->setFrom('no-reply@yourdomain.com');
$message->setTo('user@example.com');
$message->setSubject('Welcome!');
// Use Resend’s API for advanced features (e.g., templates)
});
}
}
Config File
// config/services.php
'resend' => [
'api_key' => env('RESEND_API_KEY'),
'api_url' => env('RESEND_API_URL', 'https://api.resend.com'), // Custom URL support
];
Event Listeners Listen to Resend webhook events:
// app/Listeners/HandleResendWebhook.php
public function handle($event)
{
$payload = $event->payload;
if ($payload->event === 'email.bounce') {
// Handle bounce logic
}
}
API Key Exposure
.env:
RESEND_API_KEY=re_your_api_key_here
Idempotency Keys
idempotency_key for batch emails to avoid duplicates:
$resend->batches->create([
'emails' => [...],
'options' => ['idempotency_key' => uniqid()],
]);
Rate Limits
429 Too Many Requests:
try {
$resend->emails->send(...);
} catch (ResendException $e) {
if ($e->getCode() === 429) {
sleep(60); // Retry after 60 seconds
retry();
}
}
Webhook Verification
$event = $resend->webhooks->verify($payload, $signature);
if (!$event) {
abort(401, 'Invalid webhook signature');
}
Empty Email Bodies
text or html is provided; empty bodies may fail silently.Enable Debug Mode
$resend = new ResendClient('api_key', [
'debug' => true, // Logs requests/responses to stderr
]);
Inspect Responses
$response = $resend->emails->send([...]);
if ($response->error) {
logger()->error('Resend error:', ['error' => $response->error]);
}
Common Errors
invalid_parameter: Validate required fields (from, to, subject).invalid_to_address: Check email format (use filter_var($email, FILTER_VALIDATE_EMAIL)).rate_limit_exceeded: Implement exponential backoff.Custom HTTP Client Override the default Guzzle client for retries/timeouts:
$client = new ResendClient('api_key', [
'http_client' => new \GuzzleHttp\Client([
'timeout' => 30,
'connect_timeout' => 5,
]),
]);
Extend Resources Add custom methods to the client:
$resend->extend('custom', function ($client) {
return new class($client) {
public function sendSMS($to, $message) {
return $client->post('/sms', ['body' => compact('to', 'message')]);
}
};
});
Laravel Facade Create a facade for cleaner syntax:
// app/Facades/Resend.php
class Resend extends \Illuminate\Support\Facades\Facade {
protected static function getFacadeAccessor() {
return 'resend';
}
}
Usage:
Resend::emails()->send([...]);
Testing Use mocks in PHPUnit:
$mock = Mockery::mock(ResendClient::class);
$mock->shouldReceive('emails->send')->once()->andReturn(new \stdClass());
$this->app->instance(ResendClient::class, $mock);
API URL Override Useful for staging environments:
$resend = new ResendClient('api_key', [
'api_url' => 'https://staging.api.resend.com',
]);
Default Headers Add headers globally:
$res
How can I help you explore Laravel packages today?