Documentation

PHP SDK

PHP 8.1+
Composer

Send emails with PHP using the official Laneful PHP SDK. Built for modern PHP applications with comprehensive features and type safety.

Full Feature Support

Templates, attachments, tracking, webhooks

Type Safe

Built-in validation & error handling

Modern PHP

PHP 8.1+ with named parameters

Prerequisites

To get the most out of this guide, you'll need:

  • • PHP 8.1 or higher
  • • Composer 2.0+
  • • A Laneful account with API key
  • • Verified domain for sending emails

Installation

Composer

Add the following dependency to your composer.json:

composer require lanefulhq/laneful-php

Quick Start

Send your first email in minutes:

Send Simple Text Email

<?php

require_once 'vendor/autoload.php';

use Laneful\LanefulClient;
use Laneful\Models\Email;
use Laneful\Models\Address;
use Laneful\Exceptions\*;

// Create client
$client = new LanefulClient(
    baseUrl: 'https://your-endpoint.send.laneful.net',
    authToken: 'your-auth-token'
);

// Create email
$email = new Email(
    from: new Address('sender@example.com', 'Your Name'),
    to: [new Address('recipient@example.com', 'Recipient Name')],
    subject: 'Hello from Laneful!',
    textContent: 'This is a simple test email sent using the Laneful PHP SDK.'
);

// Send email
try {
    $response = $client->sendEmail($email);
    echo "✓ Email sent successfully!\n";
    echo "Response: " . json_encode($response) . "\n";
} catch (ValidationException $e) {
    echo "✗ Validation error: " . $e->getMessage() . "\n";
} catch (ApiException $e) {
    echo "✗ API error: " . $e->getMessage() . "\n";
    echo "Status code: " . $e->getCode() . "\n";
} catch (HttpException $e) {
    echo "✗ HTTP error: " . $e->getMessage() . "\n";
} catch (Exception $e) {
    echo "✗ Unexpected error: " . $e->getMessage() . "\n";
}

Examples

Common use cases and patterns:

HTML Email with Tracking

use Laneful\Models\TrackingSettings;

// Create tracking settings
$tracking = new TrackingSettings(opens: true, clicks: true, unsubscribes: true);

$email = new Email(
    from: new Address('sender@example.com', 'Your Name'),
    to: [new Address('recipient@example.com', 'Recipient Name')],
    subject: 'HTML Email with Tracking',
    htmlContent: '<h1>Welcome!</h1><p>This is an <strong>HTML email</strong> with tracking enabled.</p>',
    textContent: 'Welcome! This is an HTML email with tracking enabled.',
    tracking: $tracking,
    tag: 'welcome-email'
);

$response = $client->sendEmail($email);

Template Email

$email = new Email(
    from: new Address('sender@example.com'),
    to: [new Address('user@example.com')],
    subject: 'Welcome {{name}}!',
    templateId: 'welcome-template',
    templateData: [
        'name' => 'John Doe',
        'company' => 'Acme Corporation',
        'activation_link' => 'https://example.com/activate'
    ],
    tracking: new TrackingSettings(opens: true, clicks: true)
);

$response = $client->sendEmail($email);

Email with Attachments

use Laneful\Models\Attachment;

// Create attachment from file
$attachment = Attachment::fromFile('/path/to/document.pdf');

$email = new Email(
    from: new Address('sender@example.com'),
    to: [new Address('user@example.com')],
    subject: 'Document Attached',
    textContent: 'Please find the document attached.',
    attachments: [$attachment],
    tracking: new TrackingSettings(opens: true, clicks: true)
);

$response = $client->sendEmail($email);

Multiple Recipients with Reply-To

$email = new Email(
    from: new Address('sender@example.com', 'Your Name'),
    to: [
        new Address('user1@example.com', 'User One'),
        new Address('user2@example.com', 'User Two')
    ],
    cc: [new Address('cc@example.com', 'CC Recipient')],
    bcc: [new Address('bcc@example.com', 'BCC Recipient')],
    replyTo: new Address('reply@example.com', 'Reply To'),
    subject: 'Email to Multiple Recipients',
    textContent: 'This email is being sent to multiple recipients.',
    tracking: new TrackingSettings(opens: true, clicks: true)
);

$response = $client->sendEmail($email);

Scheduled Email

// Schedule for 24 hours from now
$sendTime = time() + (24 * 60 * 60);

$email = new Email(
    from: new Address('sender@example.com'),
    to: [new Address('user@example.com')],
    subject: 'Scheduled Email',
    textContent: 'This email was scheduled.',
    sendTime: $sendTime,
    tracking: new TrackingSettings(opens: true, clicks: true)
);

$response = $client->sendEmail($email);

Batch Email Sending

$emails = [
    new Email(
        from: new Address('sender@example.com'),
        to: [new Address('user1@example.com')],
        subject: 'Email 1',
        textContent: 'First email content.'
    ),
    new Email(
        from: new Address('sender@example.com'),
        to: [new Address('user2@example.com')],
        subject: 'Email 2',
        textContent: 'Second email content.'
    )
];

$response = $client->sendEmails($emails);

Webhook Handler Example

use Laneful\Webhooks\WebhookVerifier;

// Complete webhook handler implementation
class WebhookController {
    private string $webhookSecret;
    
    public function __construct(string $webhookSecret) {
        $this->webhookSecret = $webhookSecret;
    }
    
    public function handleWebhook(): void {
        try {
            // Extract signature from headers
            $signature = WebhookVerifier::extractSignatureFromHeaders($_SERVER);
            if (!$signature) {
                $this->respond(401, 'Missing signature');
                return;
            }
            
            // Get raw payload
            $payload = file_get_contents('php://input');
            
            // Verify signature
            if (!WebhookVerifier::verifySignature($this->webhookSecret, $payload, $signature)) {
                $this->respond(401, 'Invalid signature');
                return;
            }
            
            // Parse and validate payload
            $webhookData = WebhookVerifier::parseWebhookPayload($payload);
            
            // Process events
            foreach ($webhookData['events'] as $event) {
                $eventType = $event['event'];
                $email = $event['email'];
                
                switch ($eventType) {
                    case 'delivery':
                        error_log("Email delivered to: " . $email);
                        break;
                    case 'open':
                        error_log("Email opened by: " . $email);
                        break;
                    case 'click':
                        $url = $event['url'] ?? 'Unknown URL';
                        error_log("Link clicked by " . $email . ": " . $url);
                        break;
                    case 'bounce':
                        $isHard = $event['is_hard'] ?? false;
                        error_log("Email bounced (" . ($isHard ? "hard" : "soft") . ") for: " . $email);
                        break;
                }
            }
            
            $this->respond(200, 'Webhook processed successfully');
            
        } catch (\InvalidArgumentException $e) {
            $this->respond(400, 'Invalid payload: ' . $e->getMessage());
        } catch (Exception $e) {
            $this->respond(500, 'Processing error: ' . $e->getMessage());
        }
    }
    
    private function respond(int $status, string $message): void {
        http_response_code($status);
        header('Content-Type: application/json');
        echo json_encode(['status' => $status, 'message' => $message]);
    }
}

API Reference

LanefulClient

Constructors

  • LanefulClient(string $baseUrl, string $authToken) - Default timeout (30 seconds)
  • LanefulClient(string $baseUrl, string $authToken, int $timeout) - Custom timeout
  • LanefulClient(string $baseUrl, string $authToken, ?HttpClient $httpClient, int $timeout) - Custom HTTP client

Methods

  • array sendEmail(Email $email) - Send single email
  • array sendEmails(array $emails) - Send multiple emails

Email

Required Fields

  • Address $from - Sender address

Optional Fields

  • Address[] $to - Recipient addresses
  • Address[] $cc - CC recipient addresses
  • Address[] $bcc - BCC recipient addresses
  • Address|null $replyTo - Reply-to address
  • string|null $subject - Email subject
  • string|null $textContent - Plain text content
  • string|null $htmlContent - HTML content
  • string|null $templateId - Template ID
  • array|null $templateData - Template data
  • Attachment[] $attachments - File attachments
  • int|null $sendTime - Scheduled send time
  • TrackingSettings|null $tracking - Email tracking configuration
  • string|null $tag - Email tag for categorization

Utility Classes

  • Address(string $email, ?string $name) - Email address with optional name
  • Attachment::fromFile(string $filePath, ?string $fileName) - Create attachment from file
  • TrackingSettings(bool $opens, bool $clicks, bool $unsubscribes) - Email tracking configuration (opens, clicks, unsubscribes)

WebhookVerifier

Signature Verification

  • bool verifySignature(string $secret, string $payload, string $signature) - Verifies webhook signature (supports sha256= prefix)
  • string generateSignature(string $secret, string $payload) - Generates signature for payload
  • string generateSignature(string $secret, string $payload, bool $includePrefix) - Generates signature with optional prefix

Payload Processing

  • array parseWebhookPayload(string $payload) - Parse and validate webhook payload structure
  • string getSignatureHeaderName() - Get the correct header name for webhook signatures
  • ?string extractSignatureFromHeaders(array $headers) - Extract signature from HTTP headers

WebhookData

  • bool is_batch - Returns true if payload contains multiple events
  • array events - Returns array of parsed events

Error Handling

Comprehensive error handling with specific exception types:

use Laneful\Exceptions\*;

try {
    $response = $client->sendEmail($email);
    echo "✓ Email sent successfully!\n";
    echo "Response: " . json_encode($response) . "\n";
} catch (ValidationException $e) {
    // Invalid input data
    echo "✗ Validation error: " . $e->getMessage() . "\n";
    echo "Please check your email configuration\n";
} catch (ApiException $e) {
    // API returned an error
    echo "✗ API error: " . $e->getMessage() . "\n";
    echo "  Status code: " . $e->getCode() . "\n";
    echo "Please check your API credentials and endpoint\n";
} catch (HttpException $e) {
    // Network or HTTP-level error
    echo "✗ HTTP error: " . $e->getMessage() . "\n";
    echo "  Status code: " . $e->getCode() . "\n";
    echo "Please check your network connection and endpoint URL\n";
} catch (Exception $e) {
    // Other unexpected errors
    echo "✗ Unexpected error: " . $e->getMessage() . "\n";
    echo $e->getTraceAsString() . "\n";
}

Exception Types

  • ValidationException - Input validation fails
  • ApiException - API returns error response
    Methods: getCode(), getMessage()
  • HttpException - HTTP communication fails
    Methods: getCode()
  • LanefulException - Base exception class

Best Practices

  • • Always wrap API calls in try-catch
  • • Handle specific exception types first
  • • Log errors with context information
  • • Implement retry logic for transient failures

Webhook Handling

Comprehensive webhook handling with signature verification, payload parsing, and validation:

Basic Signature Verification

use Laneful\Webhooks\WebhookVerifier;

// In your webhook handler
$payload = file_get_contents('php://input'); // Get the raw request body
$signature = WebhookVerifier::extractSignatureFromHeaders($_SERVER);
$secret = 'your-webhook-secret';

if ($signature && WebhookVerifier::verifySignature($secret, $payload, $signature)) {
    // Process webhook data
    $webhookData = WebhookVerifier::parseWebhookPayload($payload);
    // Handle webhook events
} else {
    // Invalid signature
    http_response_code(401);
    echo "Invalid webhook signature";
}

Advanced Webhook Processing

use Laneful\Webhooks\WebhookVerifier;

// Complete webhook verification and processing workflow
try {
    // Step 1: Get raw payload
    $payload = file_get_contents('php://input');
    
    // Step 2: Extract signature from headers (supports multiple formats)
    $signature = WebhookVerifier::extractSignatureFromHeaders($_SERVER);
    
    // Step 3: Verify signature (supports sha256= prefix)
    if (!$signature || !WebhookVerifier::verifySignature($webhookSecret, $payload, $signature)) {
        throw new SecurityException("Invalid webhook signature");
    }
    
    // Step 4: Parse and validate payload structure
    $webhookData = WebhookVerifier::parseWebhookPayload($payload);
    
    // Step 5: Process events (handles both batch and single event formats)
    foreach ($webhookData['events'] as $event) {
        $eventType = $event['event'];
        $email = $event['email'];
        
        switch ($eventType) {
            case 'delivery':
                handleDeliveryEvent($event);
                break;
            case 'open':
                handleOpenEvent($event);
                break;
            case 'click':
                handleClickEvent($event);
                break;
            case 'bounce':
                handleBounceEvent($event);
                break;
            case 'drop':
                handleDropEvent($event);
                break;
            case 'spam_complaint':
                handleSpamComplaintEvent($event);
                break;
            case 'unsubscribe':
                handleUnsubscribeEvent($event);
                break;
        }
    }
    
} catch (\InvalidArgumentException $e) {
    // Payload validation error
    http_response_code(400);
    echo "Invalid payload: " . $e->getMessage();
} catch (Exception $e) {
    // Other errors
    http_response_code(401);
    echo "Processing error: " . $e->getMessage();
}

Batch Mode Support

$webhookData = WebhookVerifier::parseWebhookPayload($payload);

if ($webhookData['is_batch']) {
    // Processing multiple events in batch mode
    echo "Processing " . count($webhookData['events']) . " events in batch";
} else {
    // Processing single event
    echo "Processing single event";
}

// Process all events
foreach ($webhookData['events'] as $event) {
    processEvent($event);
}

Supported Webhook Events

Delivery Events

  • delivery - Email delivered successfully
  • bounce - Email bounced (hard or soft)
  • drop - Email dropped (spam, invalid, etc.)
  • spam_complaint - Recipient marked email as spam

Engagement Events

  • open - Email opened by recipient
  • click - Link clicked in email
  • unsubscribe - Recipient unsubscribed

Webhook Features

Security

  • • HMAC-SHA256 signature verification
  • • Support for sha256= prefix
  • • Constant-time comparison (timing attack protection)
  • • Multiple header format support

Validation

  • • JSON payload structure validation
  • • Required field validation
  • • Event type validation
  • • Email format validation
  • • UUID format validation for lane_id

Ready to Get Started?

Start sending emails with PHP in minutes. Check out the examples and integrate with your application.