Search by

unione-repo / unione-laravel

unione-repo

UniOne Web API for Laravel: API client, mail transport, webhooks

Package info

github.com/unione-repo/unione-laravel

pkg:composer/unione-repo/unione-laravel

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

2.1.2 2026-10-16 14:14 UTC

This package is auto-updated.

Last update: 2026-10-16 14:27:03 UTC


README

Web API 1.90

Quickstart guide: Send emails with PHP · Docs for LLMs: llms.txt · API reference

Laravel integration of the UniOne PHP SDK (unione-repo/unione-php-sdk): the API client in the service container (+ facade), a webhook endpoint with signature check, a Laravel Mail transport that sends through email/send.

Install

composer require unione-repo/unione-laravel
php artisan vendor:publish --tag=unione-config  # config/unione.php

Requirements: PHP 8.1+, Laravel 10–12.

Get an API key

  1. Sign up at cp.unione.io.
  2. Copy your API key from Settings → Security → API.
  3. Put it in an environment variable:
export UNIONE_API_KEY="your-api-key"

The package reads it from UNIONE_API_KEY (see the configuration below).

Server-side only. Use this package in backend code: API routes, server functions, workers. Never ship the API key to a browser or a mobile app, and never commit it.

Configuration

Settings are read from the environment by default:

# .env
UNIONE_API_KEY=YOUR_API_KEY
UNIONE_HOST=us1.unione.io
MAIL_MAILER=unione

# config/mail.php, 'mailers':
#   'unione' => ['transport' => 'unione'],
Option Environment variable Default Description
unione.api_key UNIONE_API_KEY User or project API key (required).
unione.host UNIONE_HOST api.unione.io API host (datacenter: api.unione.io, eu1.unione.io, us1.unione.io) or a full base URL.
unione.timeout UNIONE_TIMEOUT 30 Request timeout, seconds.
unione.webhook.key UNIONE_WEBHOOK_KEY API key the webhooks are signed with (the key of the user/project of the webhook), if it differs from the API key.
unione.webhook.path UNIONE_WEBHOOK_PATH /unione/webhook Path of the webhook endpoint.

The route unione.webhook (/unione/webhook) is outside of the web middleware group, so it needs no CSRF exception; unione.webhook.enabled = false turns it off.

Send your first email — no DNS setup

Every account can send test email right away from a sandbox domain: pre-verified, DKIM and SPF already set. It delivers only to addresses you confirm.

The examples use the API client of the package (see the configuration above).

1. Get your sandbox domain (domain/get-sandbox-domain). The call creates the domain on first use and returns the same one afterwards.

use UniOne\UniOneClient;

$client = app(UniOneClient::class); // or injection, or the UniOne facade
$response = $client->domain()->getSandboxDomain();
echo $response->domain;

2. Confirm your inbox (confirmed-email/create). UniOne sends a confirmation email; click the link in it within 48 hours.

use UniOne\Api\ConfirmedEmail\Model\ConfirmedEmailRequest;
use UniOne\UniOneClient;

$client = app(UniOneClient::class); // or injection, or the UniOne facade
$response = $client->confirmedEmail()->create(new ConfirmedEmailRequest(
    email: 'you@example.com',
));
echo $response->status;

Check the status with confirmed-email/list — your address should show "status": "confirmed".

3. Send the email (email/send) from any address on the sandbox domain to the confirmed address.

use UniOne\Api\Email\Model\EmailSendRequest;
use UniOne\Api\Email\Model\MessageObject;
use UniOne\Api\Email\Model\MessageObjectRecipientsItem;
use UniOne\Common\Model\BodyObject;
use UniOne\UniOneClient;

$client = app(UniOneClient::class); // or injection, or the UniOne facade
$response = $client->email()->send(new EmailSendRequest(
    message: new MessageObject(
        recipients: [
            new MessageObjectRecipientsItem(
                email: 'you@example.com',
            ),
        ],
        body: new BodyObject(
            html: '<p>It works!</p>',
        ),
        subject: 'Hello from UniOne',
        fromEmail: 'noreply@<sandbox-domain>',
    ),
));
echo $response->jobId;

What you get back

{
  "status": "success",
  "job_id": "1ZymBc-00041N-9X",
  "emails": [
    "you@example.com"
  ]
}

On error the API returns status: "error", a message and a numeric code:

{
  "status": "error",
  "code": 102,
  "message": "Error ID:83274F98-C7D5-11F1-B221-6214FF8707B1. Can not decode key wrongkey"
}

Same flow with curl

BASE=https://api.unione.io/en/transactional/api/v1

curl -s -X POST $BASE/domain/get-sandbox-domain.json \
  -H "Content-Type: application/json" -H "X-API-KEY: $UNIONE_API_KEY" -d '{}'

curl -s -X POST $BASE/confirmed-email/create.json \
  -H "Content-Type: application/json" -H "X-API-KEY: $UNIONE_API_KEY" \
  -d '{"email": "you@example.com"}'

curl -s -X POST $BASE/email/send.json \
  -H "Content-Type: application/json" -H "X-API-KEY: $UNIONE_API_KEY" \
  -d '{
        "message": {
          "recipients": [
            {
              "email": "you@example.com"
            }
          ],
          "from_email": "noreply@<sandbox-domain>",
          "subject": "Hello from UniOne",
          "body": {
            "html": "<p>It works!</p>"
          }
        }
      }'

Sending mail

Messages of the Laravel Mail go through email/send: from, to/cc/bcc, reply-to, subject, HTML and text parts, attachments, inline images and X-* headers.

use Illuminate\Support\Facades\Mail;

Mail::to('user@example.com')->send(new OrderShipped($order));

// Mailable: tags and metadata go to the API as well
public function envelope(): Envelope
{
    return new Envelope(subject: 'Order shipped', tags: ['orders'], metadata: ['order_id' => '42']);
}

Webhooks

The endpoint answers GET with 200 OK (the URL check of webhook/set) and accepts the POST notifications: it checks the auth field (MD5 of the request body in which the auth value is replaced by the API key), answers 401 to a wrong signature, and passes the notification and each of its events to the application.

use Illuminate\Support\Facades\Event;
use UniOne\Wrapper\Events\WebhookEventReceived;

// AppServiceProvider::boot() or a listener class
Event::listen(function (WebhookEventReceived $e) {
    $data = $e->event->eventData;
    logger()->info('UniOne event', ['name' => ($n = $e->event->eventName) instanceof \BackedEnum ? $n->value : $n, 'email' => $data?->email,
        'status' => ($s = $data?->status) instanceof \BackedEnum ? $s->value : $s]);
});

Register the URL of the endpoint with webhook/set, e.g. https://example.com/unione/webhook.

API client

Every section of the API is available on the client, see the SDK docs.

use UniOne\UniOneClient;
use UniOne\Wrapper\Facades\UniOne;

// facade
$info = UniOne::system()->info();

// or injection
public function usage(UniOneClient $client): array
{
    return ['emails_sent' => $client->system()->info()->accounting?->emailsSent];
}

Sending through the API

Besides the Laravel Mail, mail can be sent through the API client directly. The client calls email/send with all of its options: templates, substitutions, metadata, tracking. Fields of the request and other examples: PHP SDK docs.

use UniOne\Api\Email\Model\EmailSendRequest;
use UniOne\Api\Email\Model\MessageObject;
use UniOne\Api\Email\Model\MessageObjectRecipientsItem;
use UniOne\Common\Model\BodyObject;
use UniOne\UniOneClient;

$client = app(UniOneClient::class); // or injection, or the UniOne facade
$response = $client->email()->send(new EmailSendRequest(
    message: new MessageObject(
        recipients: [
            new MessageObjectRecipientsItem(
                email: 'user@example.com',
                substitutions: [
                    'CustomerId' => '12452',
                    'to_name' => 'John Smith',
                ],
            ),
        ],
        body: new BodyObject(
            html: '<b>Hello, {{to_name}}</b>',
            plaintext: 'Hello, {{to_name}}',
        ),
        subject: 'UniOne test email ',
        fromEmail: 'email@example.com',
        fromName: 'John Smith',
    ),
));
echo $response->jobId;

Sandbox limits

  • Sends only to confirmed addresses: up to 10 per account.
  • Up to 100 emails a day.
  • The sandbox domain expires after 31 days; call domain/get-sandbox-domain again to get a new one.
  • Works only with the account API key, not a project key.
  • Not listed in domain/list; the other domain/* methods do not apply to it.

The sandbox is for development and testing. Do not use a sandbox domain as the sender in production.

Before production: verify your domain

  1. domain/get-dns-records — returns the values for your DNS records.

  2. Add these records at your DNS provider (the domain page of the control panel shows them too):

    Type Host Value
    TXT @ the verification-record value (unione-validate-hash=…)
    TXT <selector>._domainkey (the selector is on the domain page of the control panel, e.g. us) v=DKIM1; k=rsa; p=<dkim>
    TXT @ v=spf1 include:spf.unione.io ~all
    CNAME _dmarc the dmarc value
  3. domain/validate-verification-record, then domain/validate-dkim.

  4. domain/list — wait until the verification record is confirmed and DKIM is active.

  5. Read from_email from your config, so switching from the sandbox needs no code change.

Sending reliably

  • Retries: set message.idempotence_key on sends that may be retried, so a repeated request does not send twice.
  • Limits: up to 500 recipients and 10 MB per request; each attachment up to 7 MB.
  • Body: always include body.html or body.plaintext.
  • Delivery status: register a webhook (webhook/set) with a fast, idempotent HTTPS handler instead of polling.
  • Defaults: keep the default unsubscribe footer and tracking; changing them requires UniOne support.
  • Errors: retry 429 and 5xx with backoff; do not retry other errors unchanged.

Links

For package contributors

See AGENTS.md: how this package is generated from the OpenAPI spec, built, tested and released.