Different ways to send emails with the Lettr PHP SDK
The Lettr PHP SDK offers multiple ways to send emails, from quick one-liners to the full-featured email builder. This page covers every approach so you can choose the one that fits your use case.
For simple emails, use the shorthand methods. These are convenience wrappers around the email builder that handle the most common use cases with minimal code.
$response = $lettr->emails()->sendHtml( from: 'sender@yourdomain.com', to: 'recipient@example.com', subject: 'Hello', html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',);
The sendHtml() method is perfect for simple HTML emails where you don’t need advanced features like tracking configuration or metadata.
When providing HTML content, the SDK automatically enables CSS inlining and template substitutions by default. You can disable these behaviors using the email builder for more control.
use Lettr\ValueObjects\EmailAddress;$response = $lettr->emails()->sendText( from: new EmailAddress('sender@yourdomain.com', 'Your App'), to: ['user1@example.com', 'user2@example.com'], subject: 'Plain text update', text: 'This is a plain text email.',);
The from parameter accepts either a string email address or an EmailAddress value object to set a display name. The to parameter accepts a single email string or an array of recipients (maximum 50).
You can send both HTML and plain text in the same email by using the email builder with both ->html() and ->text() methods. Mail clients that don’t support HTML will display the plain text version.
The substitutionData array keys correspond to merge tags in your Lettr template (e.g., {{name}}). See Merge Tags & Template Language for the full syntax including conditionals and loops.For more advanced template usage including versioning, see the Templates page.
For complex emails that require multiple features, use the fluent email builder. This gives you access to every option the Lettr API supports in a chainable interface:
The builder is useful when you need to combine multiple features in a single email — for example, HTML content with a plain text fallback, tracking configuration, metadata for analytics, and attachments.
All builder methods return the builder instance, so you can chain as many method calls as needed. The order of method calls doesn’t matter — the builder collects all the data and sends it to the API when you call send().
$response = $lettr->emails()->send( $lettr->emails()->create() ->from('billing@yourdomain.com') ->to(['customer@example.com']) ->subject('Your Invoice') ->html('<p>Please find your invoice attached.</p>') // From file path ->attachFile('/path/to/invoice.pdf') // With custom name and MIME type ->attachFile('/path/to/file', 'custom-name.pdf', 'application/pdf') // From binary data ->attachData($binaryContent, 'report.csv', 'text/csv'));
When attaching files, the SDK automatically:
Base64-encodes the file content
Detects the MIME type if not provided
Validates file size and format
Large attachments increase email size and can affect deliverability. Keep attachments under 10 MB total per email. For larger files, consider uploading to cloud storage and including a download link in the email instead.
Fine-tune email behavior with tracking and processing options:
$email = $lettr->emails()->create() ->from('sender@yourdomain.com') ->to(['recipient@example.com']) ->subject('Newsletter') ->html($htmlContent) // Tracking ->withClickTracking(true) ->withOpenTracking(true) // Mark as transactional (bypasses unsubscribe lists) ->transactional() // CSS inlining ->withInlineCss(true) // Template variable substitution in HTML ->withSubstitutions(true);$response = $lettr->emails()->send($email);
Option
Default
Description
withClickTracking()
true
Track link clicks in the email
withOpenTracking()
true
Track when recipients open the email
transactional()
true
Mark as transactional (bypasses unsubscribe lists)
withInlineCss()
false
Automatically inline CSS styles for better email client compatibility
withSubstitutions()
true
Enable merge tag substitution in HTML/text content
Click and open tracking are enabled by default. For transactional emails like password resets or order confirmations, leave tracking enabled — the data helps you diagnose delivery issues. For privacy-sensitive communications, disable tracking explicitly.
Every send method returns a SendEmailResponse with details about the request:
$response = $lettr->emails()->send($email);$response->requestId; // Request ID for tracking$response->accepted; // Number of accepted recipients$response->rejected; // Number of rejected recipients$response->allAccepted(); // true if all recipients accepted$response->hasRejections(); // true if any were rejected$response->total(); // Total recipients (accepted + rejected)
The requestId is a unique identifier for this email transmission. Store it in your database to correlate emails with delivery events later. You can use it to query the Lettr API for detailed delivery information including opens, clicks, bounces, and complaints.
Save the requestId in your application’s database alongside the user record or transaction. This lets you look up delivery status, debug issues, and provide customer support without searching through logs.
The SDK throws typed exceptions for different API errors, making it easy to handle specific failure cases:
use Lettr\Exceptions\ValidationException;use Lettr\Exceptions\UnauthorizedException;use Lettr\Exceptions\NotFoundException;use Lettr\Exceptions\ConflictException;use Lettr\Exceptions\QuotaExceededException;use Lettr\Exceptions\RateLimitException;use Lettr\Exceptions\ApiException;use Lettr\Exceptions\TransporterException;try { $response = $lettr->emails()->send($email);} catch (ValidationException $e) { // Invalid request data (422) // e.g., unverified sender domain, invalid email format} catch (UnauthorizedException $e) { // Invalid API key (401)} catch (NotFoundException $e) { // Resource not found (404) // e.g., template slug doesn't exist} catch (ConflictException $e) { // Resource conflict (409)} catch (QuotaExceededException $e) { // Sending quota exceeded (429) — monthly or daily limit reached // $e->quota contains quota details (free tier only)} catch (RateLimitException $e) { // API rate limit exceeded (429) — too many requests per second // $e->rateLimit contains limit/remaining/reset info // $e->retryAfter contains seconds to wait} catch (ApiException $e) { // Other API errors (server errors, etc.)} catch (TransporterException $e) { // Network/transport errors (connection failures, timeouts)}
Exception
HTTP Status
Common Causes
ValidationException
422
Invalid email format, unverified sending domain, missing required fields
UnauthorizedException
401
Invalid or expired API key
NotFoundException
404
Template slug doesn’t exist, invalid project ID
ConflictException
409
Resource already exists or conflicts with current state
QuotaExceededException
429
Monthly or daily sending limit reached (free tier)
RateLimitException
429
Too many requests per second (3 req/s per team)
ApiException
4xx/5xx
Server errors, other API errors
TransporterException
N/A
Network failures, timeouts, DNS errors
Always wrap email sending in a try-catch block in production. Network failures and API errors can happen at any time, and unhandled exceptions will crash your application.