# PHP SDK

> The official vaartix/vaartix-php package for PHP, with a Laravel mail driver and a Symfony Mailer transport.

Source: https://vaartix.com/docs/sdks/php/

```sh
composer require vaartix/vaartix-php
```

The current version is 0.3.0. Works with PHP 8.1 and later, with the standard `curl` and `json` extensions and no other dependencies. The package is on [Packagist](https://packagist.org/packages/vaartix/vaartix-php) and the source is on GitHub at [vaartix-mail/vaartix-php](https://github.com/vaartix-mail/vaartix-php). Step-by-step guides: [Laravel](https://vaartix.com/docs/guides/laravel/) and [Symfony](https://vaartix.com/docs/guides/symfony/).

Using an AI assistant such as Claude or Cursor? The [MCP server](https://vaartix.com/docs/guides/mcp/) lets it send email and look up delivery status through the same API, without writing code.

## Send an email

```php
use Vaartix\Vaartix;

$vaartix = new Vaartix(); // reads the VAARTIX_API_KEY environment variable

$email = $vaartix->emails->send([
    'from' => 'Acme <onboarding@acme.in>',
    'to' => ['priya@example.com'],
    'subject' => 'Hello from Vaartix',
    'html' => '<strong>It works!</strong>',
]);

echo $email['id']; // em_...
```

Keys can be `snake_case` (`reply_to`, `scheduled_at`) or the API’s camelCase (`replyTo`, `scheduledAt`). Responses are the API’s JSON as arrays, exactly as in the [API reference](https://vaartix.com/docs/api/).

## Methods

```php
// Retry safely: the same key within 24 hours returns the first email instead of sending twice.
$vaartix->emails->send($email, idempotencyKey: "welcome-{$user->id}");

// Send later (up to 30 days ahead), and cancel if plans change.
$scheduled = $vaartix->emails->send([...$email, 'scheduled_at' => '2026-11-01T09:00:00+05:30']);
$vaartix->emails->cancel($scheduled['id']);

// Up to 100 emails in one request.
$vaartix->batch->send([$email1, $email2]);

// Look up an email and its delivery events, or list recent bounces.
$vaartix->emails->get('em_...');
$vaartix->emails->list(['status' => 'bounced', 'days' => 7]);
```

No domain yet? Send from any address at `test.vaartix.com` to `delivered@`, `bounced@` or `complained@test.vaartix.com` to [simulate what happens](https://vaartix.com/docs/sending/test-mode/):

```php
$vaartix->emails->send([
    'from' => 'onboarding@test.vaartix.com',
    'to' => 'delivered@test.vaartix.com',
    'subject' => 'Hello',
    'text' => 'It works!',
]);
```

## Errors

API and network errors throw `Vaartix\Exceptions\VaartixException`:

```php
use Vaartix\Exceptions\VaartixException;

try {
    $vaartix->emails->send($email);
} catch (VaartixException $e) {
    echo $e->type, ' ', $e->statusCode, ' ', $e->getMessage(); // e.g. rate_limited 429 Too many requests...
}
```

`$e->type` is a stable code; the full list is in [Errors](https://vaartix.com/docs/concepts/errors/). Network failures have `type === 'network_error'` and `statusCode === null`.

## Laravel

Since vaartix/vaartix-php 0.2.0, the package registers a `vaartix` mail driver automatically (Laravel 12 and 13), so Laravel’s `Mail` facade, Mailables, notifications and queued mail all go through Vaartix.

.env

```sh
MAIL_MAILER=vaartix
MAIL_FROM_ADDRESS=noreply@acme.in   # on a domain verified in Vaartix
VAARTIX_API_KEY=vx_live_...
VAARTIX_WEBHOOK_SECRET=whsec_...
```

config/services.php

```php
'vaartix' => [
    'key' => env('VAARTIX_API_KEY'),
    'webhook_secret' => env('VAARTIX_WEBHOOK_SECRET'),
],
```

config/mail.php, inside 'mailers'

```php
'vaartix' => [
    'transport' => 'vaartix',
],
```

That’s all. Existing code keeps working:

```php
Mail::to($user)->send(new OrderConfirmed($order));
```

Laravel’s `->tag('welcome')` and `->metadata('order_id', 42)` become Vaartix tags, so you can filter by them in the dashboard. `SentMessage::getMessageId()` returns the Vaartix email ID.

To call the API directly, inject the client; it’s registered as a singleton from `services.vaartix.key`:

```php
public function store(Request $request, \Vaartix\Vaartix $vaartix)
{
    $vaartix->emails->send([
        'from' => 'Acme <orders@acme.in>',
        'to' => $request->user()->email,
        'subject' => 'Your order is confirmed',
        'html' => view('emails.order-confirmed', ['order' => $order])->render(),
    ], idempotencyKey: "order-confirmed-{$order->id}");
}
```

### Symfony Mailer

Since vaartix/vaartix-php 0.3.0, Symfony Mailer can use Vaartix through a `MAILER_DSN`. Set the DSN and register the transport factory once:

.env.local

```dotenv
MAILER_DSN=vaartix+api://vx_your_api_key@default
```

config/services.yaml

```yaml
services:
    Vaartix\Symfony\VaartixTransportFactory:
        parent: mailer.transport_factory.abstract
        tags: ['mailer.transport_factory']
```

`default` means `https://api.vaartix.com`. Add `?timeout=10` to change the request timeout (seconds). If your key contains `+` or `/`, URL-encode them in the DSN. Step by step: [Symfony guide](https://vaartix.com/docs/guides/symfony/).

## Webhooks

Check that a webhook request really came from Vaartix before trusting it. `Webhook::verify()` checks the signature and timestamp ([Standard Webhooks](https://www.standardwebhooks.com)) and returns the parsed event. Pass the **raw** body, not parsed JSON.

### Plain PHP

```php
use Vaartix\Webhook;
use Vaartix\Exceptions\WebhookVerificationException;

try {
    $event = Webhook::verify(file_get_contents('php://input'), getallheaders(), getenv('VAARTIX_WEBHOOK_SECRET'));
} catch (WebhookVerificationException $e) {
    http_response_code(400);
    exit;
}
```

### Laravel

Exclude this route from CSRF protection; Vaartix signs the request instead.

```php
use Illuminate\Http\Request;
use Vaartix\Webhook;
use Vaartix\Exceptions\WebhookVerificationException;

Route::post('/webhooks/vaartix', function (Request $request) {
    try {
        $event = Webhook::verify($request->getContent(), $request->headers->all(), config('services.vaartix.webhook_secret'));
    } catch (WebhookVerificationException $e) {
        abort(400);
    }
    if ($event['type'] === 'email.bounced') {
        // stop emailing $event['data']['recipient']
    }
    return response()->noContent();
});
```

Manage endpoints with `$vaartix->webhooks->create()`, `list()`, `get()`, `update()`, `remove()`, `rotateSecret()`, `deliveries()` and `retryDelivery()`. See [Webhooks](https://vaartix.com/docs/webhooks/setup/) for the events and how retries work.
