PHP SDK

The official PHP SDK for Veil Mail with idiomatic PHP patterns.

  • Exception-based error handling
  • PSR-4 autoloading via Composer
  • Constant-time HMAC webhook verification
  • Zero external dependencies (cURL only)
  • PHP 8.1+ with readonly properties and named arguments

Installation

composer require veilmail/veilmail-php

Quick Start

send.php
<?php

require_once 'vendor/autoload.php';

$client = new VeilMail\VeilMail('veil_live_xxxxx');

$email = $client->emails->send([
    'from' => 'hello@yourdomain.com',
    'to' => 'user@example.com',
    'subject' => 'Hello from PHP!',
    'html' => '<h1>Welcome!</h1>',
]);

echo $email['id'];     // email_xxxxx
echo $email['status']; // queued

Resources

The client exposes the same resources as the Node.js SDK:

ResourceDescription
$client->emailsSend, batch send, list, get, cancel, update emails
$client->domainsCreate, verify, update, list, delete domains
$client->templatesCreate, update, preview, list, delete templates
$client->audiencesManage audiences and subscribers
$client->campaignsCreate, schedule, send, pause, resume, cancel campaigns
$client->webhooksManage webhook endpoints, test, rotate secrets
$client->topicsManage subscription topics and preferences
$client->propertiesManage contact property definitions and values

Sending Emails

emails.php
// Send with template
$email = $client->emails->send([
    'from' => 'hello@yourdomain.com',
    'to' => 'user@example.com',
    'templateId' => 'tmpl_xxx',
    'templateData' => ['name' => 'Alice'],
]);

// Send with attachments
$email = $client->emails->send([
    'from' => 'hello@yourdomain.com',
    'to' => 'user@example.com',
    'subject' => 'Invoice',
    'html' => '<p>Attached is your invoice.</p>',
    'attachments' => [
        [
            'filename' => 'invoice.pdf',
            'content' => $base64Content,
            'contentType' => 'application/pdf',
        ],
    ],
]);

// Batch send (up to 100)
$result = $client->emails->sendBatch([
    ['from' => 'hi@yourdomain.com', 'to' => ['u1@ex.com'], 'subject' => 'Hi', 'html' => '<p>Hi!</p>'],
    ['from' => 'hi@yourdomain.com', 'to' => ['u2@ex.com'], 'subject' => 'Hi', 'html' => '<p>Hi!</p>'],
]);

Subscriber Management

subscribers.php
$subs = $client->audiences->subscribers('audience_xxxxx');

// Add a subscriber
$subscriber = $subs->add([
    'email' => 'user@example.com',
    'firstName' => 'Alice',
    'lastName' => 'Smith',
    'consentType' => 'express',
]);

// List subscribers
$result = $subs->list(['status' => 'active', 'limit' => 50]);
foreach ($result['data'] as $sub) {
    echo $sub['email'] . "\n";
}

// Import from CSV
$result = $subs->import(['csvData' => "email,firstName\nuser@example.com,Bob"]);
echo "Created: {$result['created']}, Skipped: {$result['skipped']}";

// Export as CSV
$csv = $subs->export(['status' => 'active']);

Error Handling

errors.php
use VeilMail\Exceptions\RateLimitException;
use VeilMail\Exceptions\PiiDetectedException;
use VeilMail\Exceptions\ValidationException;
use VeilMail\Exceptions\AuthenticationException;
use VeilMail\Exceptions\VeilMailException;

try {
    $client->emails->send([
        'from' => 'hello@yourdomain.com',
        'to' => 'user@example.com',
        'subject' => 'Hello',
        'html' => '<p>Hi!</p>',
    ]);
} catch (RateLimitException $e) {
    echo "Rate limited. Retry after {$e->getRetryAfter()}s";
} catch (PiiDetectedException $e) {
    echo "PII detected: " . implode(', ', $e->getPiiTypes());
} catch (ValidationException $e) {
    echo "Validation error: {$e->getMessage()}";
} catch (AuthenticationException $e) {
    echo "Invalid API key";
} catch (VeilMailException $e) {
    echo "API error: {$e->getMessage()} (code: {$e->getErrorCode()})";
}

Webhook Verification

Use the built-in utility to verify webhook signatures:

webhook.php
$body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SIGNATURE_HASH'] ?? '';

if (!VeilMail\Webhook::verifySignature($body, $signature, $webhookSecret)) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);
switch ($event['type']) {
    case 'email.delivered':
        // Handle delivery
        break;
    case 'email.bounced':
        // Handle bounce
        break;
}

http_response_code(200);

Laravel Integration

Example webhook controller for Laravel:

WebhookController.php
use Illuminate\Http\Request;

class WebhookController extends Controller
{
    public function handle(Request $request)
    {
        $body = $request->getContent();
        $signature = $request->header('X-Signature-Hash', '');
        $secret = config('services.veilmail.webhook_secret');

        if (!\VeilMail\Webhook::verifySignature($body, $signature, $secret)) {
            return response('Unauthorized', 401);
        }

        $event = $request->json()->all();

        match ($event['type']) {
            'email.delivered' => $this->handleDelivered($event),
            'email.bounced' => $this->handleBounced($event),
            default => null,
        };

        return response('OK', 200);
    }
}

Required Scopes

The PHP SDK uses the same API scopes as the Authentication system. See the Node.js SDK documentation for the full scope reference.