PHP SDK
Send email and manage contacts from PHP with the official Nexiom Connect SDK.
nexiom/connect-php is the official SDK for PHP. It works in Laravel, Symfony, WordPress, or plain PHP, and retries safely for you.
Install
PHP 8.2 or later is required. The SDK uses Guzzle 7.9 or 8.
composer require nexiom/connect-phpInitialize the client
Create one client on your server and reuse it. Load the API key from your environment; never ship it to the browser.
use Nexiom\Connect\NexiomConnect;
$apiKey = getenv('NEXIOM_API_KEY');
if (!$apiKey) {
throw new RuntimeException('NEXIOM_API_KEY is required');
}
$nexiom = new NexiomConnect($apiKey);apiKeyrequiredstringYour API key, starting with
nc_.timeoutoptionalint or floatTotal deadline per call in seconds, including retries. Default
30.maxRetriesoptionalintExtra attempts for reads, sends, and cancels. Default
2.baseUrloptionalstringAPI root. Default
https://api-connect.nxiom.com/api.httpClientoptionalGuzzleHttp\ClientInterfaceA Guzzle client, for proxies, instrumentation, or tests.
Pass options by name, for example new NexiomConnect(apiKey: $apiKey, timeout: 10).
Send an email
Parameters are arrays with camelCase keys. Methods return the response data as an array.
$email = $nexiom->emails->send(
[
'from' => 'hello@mail.example.com',
'fromName' => 'Acme',
'to' => 'reader@example.com',
'subject' => 'Your receipt',
'html' => '<p>Thanks for your order.</p>',
'text' => 'Thanks for your order.',
],
['idempotencyKey' => 'receipt-' . $order->id],
);
echo $email['messageId'];Pass an idempotencyKey that identifies the email in your system. If you omit it, the SDK generates one per call, which protects its own retries but not a retry of your whole operation. See Sending email.
To send a template, pass templateId and templateVariables instead of subject and content.
Schedule an email
Pass scheduledAt as a DateTimeInterface or an ISO 8601 timestamp with an offset, up to 30 days ahead. Keep the returned messageId to move or cancel the send before it goes out.
$email = $nexiom->emails->send([
'from' => 'hello@mail.example.com',
'to' => 'reader@example.com',
'subject' => 'Your trial ends soon',
'text' => 'Your trial ends soon.',
'scheduledAt' => new DateTimeImmutable('+1 day'),
]);
// Move it one day later, or cancel it before it is sent.
$nexiom->emails->reschedule($email['messageId'], [
'scheduledAt' => new DateTimeImmutable('+2 days'),
]);
$nexiom->emails->cancel($email['messageId']);See Reschedule and Cancel for the rules.
Read email logs
emails->list and emails->get need a full-access key. list pages with a cursor; get takes a delivery ID, not a message ID.
$page = $nexiom->emails->list(['status' => 'bounced', 'limit' => 50]);
$first = $page['items'][0] ?? null;
if ($first !== null) {
$delivery = $nexiom->emails->get($first['id']);
echo $delivery['recipient'], ' ', $delivery['delivery_reason'];
}
// nextCursor is null on the last page.
if ($page['nextCursor'] !== null) {
$nexiom->emails->list(['status' => 'bounced', 'cursor' => $page['nextCursor']]);
}See List emails and Retrieve an email for every field.
Manage contacts
Define a contact property before you store a value for it.
$nexiom->contacts->properties->create(['name' => 'plan', 'type' => 'string']);
$contact = $nexiom->contacts->create([
'email' => 'reader@example.com',
'firstName' => 'Sam',
'properties' => ['plan' => 'pro'],
]);
$nexiom->contacts->update($contact['id'], [
'email' => 'reader@example.com',
'emailStatus' => 'unsubscribed',
]);
$page = $nexiom->contacts->list(['search' => 'example.com', 'limit' => 50]);contacts->get, contacts->delete, and contacts->properties are available too. Responses use snake_case fields such as first_name. See the Contacts API for every field.
Send a WhatsApp message
messaging->send sends on every messaging channel; channel selects the parameters. For WhatsApp, send an approved template from your connected number, named by its phone number in from. Requires 0.4.0 or later and a key with the whatsapp.send permission.
$message = $nexiom->messaging->send(
[
'channel' => 'whatsapp',
'from' => '+213555000111',
'to' => '+213555123456',
'template' => [
'id' => 'wat_example',
'variables' => ['body' => [1 => 'Amina', 2 => 'A-1234']],
],
],
['idempotencyKey' => 'order-A-1234-shipped'],
);
$current = $nexiom->messaging->get($message['id']);
echo $current['status'];See Sending WhatsApp messages.
Verify webhooks
webhooks->constructEvent (0.4.0 or later) checks a webhook request's signature and timestamp, and returns the event as an array. Pass the raw body, not decoded JSON. In a Laravel controller or route:
$event = $nexiom->webhooks->constructEvent(
$request->getContent(),
$request->headers->all(),
config('services.nexiom.webhook_secret'),
);
if ($event['type'] === 'email.bounced') {
error_log($event['data']['recipient']);
}It throws NexiomWebhookVerificationException when the request is not genuine. Outside Laravel, pass file_get_contents('php://input') and getallheaders(). See Verify webhook requests for a complete Laravel route.
Handle errors
API and transport failures throw Nexiom\Connect\Exceptions\NexiomException.
use Nexiom\Connect\Exceptions\ErrorKind;
use Nexiom\Connect\Exceptions\NexiomException;
try {
$email = $nexiom->emails->send($message, ['idempotencyKey' => $idempotencyKey]);
} catch (NexiomException $error) {
if ($error->kind === ErrorKind::Api && $error->status === 429) {
// Rate limited: try again later with the same idempotency key.
}
error_log("{$error->status} {$error->errorCode} {$error->getMessage()} {$error->requestId}");
throw $error;
}| Property | Description |
|---|---|
kind | ErrorKind::Api for an error response; otherwise Network, Timeout, or Protocol. |
status | HTTP status, when the API responded. |
errorCode | Machine-readable error code, when present. |
requestId | The request's correlation ID. Include it when contacting support. |
details | Additional error details, such as validation failures. |
Invalid arguments — for example a missing from, or an unknown key such as from_name — throw a NexiomValidationException before any request is made.
Retries and timeouts
The SDK retries reads, sends and cancels on 408, 429, and 5xx responses and on network failures, honouring Retry-After. Sends are retried with the same idempotency key, so a retry never sends twice. Other writes, such as creating a contact or rescheduling an email, are not retried automatically. If Retry-After asks for a longer wait than the remaining timeout, the SDK throws that error straight away.
Override per call by passing ['timeout' => ..., 'maxRetries' => ...] as the method's last argument.