> ## Documentation Index
> Fetch the complete documentation index at: https://jetemail.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# PHP

> Official JetEmail PHP SDK for transactional email

# PHP SDK

The **[jetemail/jetemail-php](https://github.com/jetemail/jetemail-php)** package is the official PHP SDK for JetEmail transactional email (MIT license, **PHP 8.1+**, Guzzle 7.5+).

Create a **transactional** API key in the dashboard: **Outbound** → **Keys** → Add API key. Use that key with the SDK.

<Note>For Laravel projects, use the dedicated [Laravel SDK](/docs/transactional-email/sdk/laravel) which includes Mail driver integration and webhook support.</Note>

## Installation

```bash theme={null}
composer require jetemail/jetemail-php
```

Package on Packagist: [jetemail/jetemail-php](https://packagist.org/packages/jetemail/jetemail-php). Source on GitHub: [jetemail/jetemail-php](https://github.com/jetemail/jetemail-php).

## Quick start

```php theme={null}
$jetemail = JetEmail::client('your-transactional-api-key');

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

echo $email->id;
```

Use a `from` address on a **verified domain** (see [Getting started](/docs/transactional-email/getting-started)).

<Note>`JetEmail` is a global (un-namespaced) class registered by Composer's autoloader, so no `use` statement is needed. Inside a namespaced file, call it as `\JetEmail::client(...)`.</Note>

## Creating a client

```php theme={null}
// API key only
$jetemail = JetEmail::client('your-transactional-api-key');

// With a custom base URL
$jetemail = JetEmail::client('your-transactional-api-key', 'https://api.jetemail.com');
```

`JetEmail::client()` returns a `JetEmail\Client` exposing two services:

| Service | Property | Description |
| - | - | - |
| Email | `$jetemail->emails` | Send a single email |
| Batch | `$jetemail->batch` | Send up to 100 emails in one request |

## `$jetemail->emails->send(array $parameters)`

Send a single email. Parameters are a plain associative array using the same field names as the REST API.

| Field | Required | Description |
| - | - | - |
| `from` | ✓ | Sender, e.g. `"Name <email@domain.com>"` |
| `to` | ✓ | Recipient(s), string or array (max 50) |
| `subject` | ✓ | Subject line |
| `html` | \* | HTML body (need `html` and/or `text`) |
| `text` | \* | Plain text (need `html` and/or `text`) |
| `cc`, `bcc`, `reply_to` | | Recipients, string or array (max 50 each) |
| `headers` | | Custom headers as `['X-Header' => 'value']` |
| `attachments` | | Array of `['filename' => ..., 'data' => base64]` (max 40MB total) |
| `eu` | | `true` to restrict delivery to the EU region |

```php theme={null}
$email = $jetemail->emails->send([
    'from' => 'App <noreply@example.com>',
    'to' => 'user@example.com',
    'subject' => 'Order Confirmation',
    'html' => '<p>Your order has been confirmed.</p>',
    'text' => 'Your order has been confirmed.',
    'cc' => ['manager@example.com'],
    'bcc' => ['archive@example.com'],
    'reply_to' => 'support@example.com',
    'headers' => [
        'X-Custom-Header' => 'value',
    ],
    'attachments' => [
        [
            'filename' => 'receipt.pdf',
            'data' => base64_encode(file_get_contents('/path/to/receipt.pdf')),
        ],
    ],
]);

echo $email->id;       // Message ID
echo $email->response; // Queue confirmation message
```

The array is sent to the API as-is, so any other field the [`POST /email`](/docs/api-reference/introduction) endpoint accepts can be included too.

### Responses

Every call returns a `JetEmail\Resource`. Read fields as properties or array keys, or call `toArray()` for the raw payload:

```php theme={null}
$email->id;         // property access
$email['id'];       // array access
$email->toArray();  // ['id' => '...', 'response' => '...']
```

Response shape for a single send: `['id' => string, 'response' => string]`.

## `$jetemail->batch->send(array $emails)`

Send up to **100** emails in one request. Each element uses the same fields as a single send. Returns `summary` (`total`, `successful`, `failed`) and `results` per message (success with `id`, or error with `error`).

```php theme={null}
$result = $jetemail->batch->send([
    [
        'from' => 'App <noreply@example.com>',
        'to' => 'user1@example.com',
        'subject' => 'Hello User 1',
        'html' => '<p>Welcome!</p>',
    ],
    [
        'from' => 'App <noreply@example.com>',
        'to' => 'user2@example.com',
        'subject' => 'Hello User 2',
        'html' => '<p>Welcome!</p>',
    ],
]);

echo $result->summary['successful'];

foreach ($result->results as $item) {
    if ($item['status'] === 'success') {
        echo $item['id'];
    } else {
        echo $item['error'];
    }
}
```

## Error handling

API errors throw `ErrorException`; network and transport failures throw `TransporterException`. A response that is not valid JSON throws `UnserializableResponse`.

```php theme={null}
use JetEmail\Exceptions\ErrorException;
use JetEmail\Exceptions\TransporterException;

try {
    $jetemail->emails->send([...]);
} catch (ErrorException $e) {
    // API returned an error
    echo $e->getMessage();
    echo $e->getCode();     // HTTP status code
    print_r($e->getBody()); // Full error response
} catch (TransporterException $e) {
    // Network/transport error
    echo $e->getMessage();
}
```

Common status codes: **400** invalid request, **401** bad/missing API key, **500** server error.

## Webhook signature verification

The SDK includes a helper for verifying [webhook signatures](/docs/webhooks/security). It checks the HMAC-SHA256 signature and rejects events whose timestamp is older than the tolerance (default 300 seconds).

```php theme={null}
use JetEmail\WebhookSignature;
use JetEmail\Exceptions\WebhookSignatureVerificationException;

try {
    WebhookSignature::verify(
        payload: file_get_contents('php://input'),
        signature: $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'],
        timestamp: $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'],
        secret: 'your-webhook-secret',
        // tolerance: 300, // optional, seconds
    );

    // Signature is valid
} catch (WebhookSignatureVerificationException $e) {
    // Invalid or expired signature
    http_response_code(401);
}
```

***

For additional examples, see the **[README on GitHub](https://github.com/jetemail/jetemail-php)**. REST details: [API reference](/docs/api-reference/introduction).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.