PHP SDK
SDK ufficiale PHP per Tratto Email API
SDK ufficiale per PHP: tratto/email, su Packagist. Tutto quello che c'è
in questa pagina richiede la 1.0.0 o successiva, salvo dove una sezione indica una versione più alta.
Requisiti
- PHP 8.1 o successivo
- l'estensione
curl(ext-curl)
Nessun'altra dipendenza a runtime.
Installazione
composer require tratto/emailInizializzazione
use Tratto\Client;
$tratto = new Client((string) getenv('TRATTO_API_KEY'));Tieni la chiave fuori dal codice: leggila dall'ambiente o dal tuo secret store. Le API key si creano in dashboard, alla voce API Keys.
Il secondo argomento accetta le opzioni:
$tratto = new Tratto\Client($apiKey, [
'baseUrl' => 'https://api.tratto.email', // default
'timeout' => 30, // secondi, default 30 (connessione: al massimo 10)
]);| Opzione | Tipo | Descrizione |
|---|---|---|
baseUrl | string | URL http:// o https://, senza /v1 (lo aggiunge l'SDK). Con http:// la chiave viaggia in chiaro: usalo solo per un server locale. |
timeout | int|float | Tempo totale per richiesta, in secondi. |
transport | Tratto\Http\Transport | Sostituisce il transport curl integrato, per esempio con un fake nei test. |
Una chiave vuota, un'opzione sconosciuta o un valore non valido sollevano
\InvalidArgumentException. Non esiste un'opzione per passare impostazioni a
curl: la verifica TLS è sempre attiva e i redirect non vengono mai seguiti.
Risposte
I metodi restituiscono il JSON decodificato come array associativi, quindi un campo aggiunto dall'API è disponibile senza aggiornare l'SDK.
- I singoli oggetti arrivano senza l'involucro
data:$tratto->emails->get($id)['status']. - Le liste paginate restituiscono la pagina intera,
['data' => [...], 'pagination' => ['hasMore' => bool, 'nextCursor' => ?string]]: passanextCursorcomeafterper leggere la pagina successiva. - I metodi che rispondono 204 (
webhooks->delete,templates->delete) non restituiscono nulla. - Un
\DateTimeInterfacepassato all'SDK viene inviato in UTC con i millisecondi; una stringa viene inviata così com'è.
Invia Email
$email = $tratto->emails->send([
'from' => 'Acme <hello@tuodominio.it>',
'to' => 'user@example.com',
'subject' => 'Benvenuto!',
'html' => '<h1>Benvenuto!</h1>',
'text' => 'Benvenuto su Tratto',
]);
echo $email['id'];Invia con Markdown
Invia Markdown e lascia al server il rendering in
HTML email responsive. markdown e html si escludono a vicenda.
$tratto->emails->send([
'from' => 'Acme <hello@tuodominio.it>',
'to' => 'user@example.com',
'subject' => 'Benvenuto!',
'markdown' => "# Benvenuto, {{firstName}}\n\nSiamo felici di averti a bordo.",
'variables' => ['firstName' => 'Alice'],
]);Invia con Template, Programmazione e Chiave di Idempotenza
Il secondo argomento di emails->send è la
chiave di idempotenza: entro 24 ore, ripetere la chiamata
con la stessa chiave restituisce la risposta originale invece di inviare di
nuovo (best-effort). emails->send e emails->sendBatch la accettano, e una
chiave che contiene un a capo viene rifiutata prima dell'invio.
$tratto->emails->send([
'from' => 'hello@tuodominio.it',
'to' => ['alice@example.com', 'bob@example.com'],
'subject' => 'La tua ricevuta',
'templateId' => 'tmpl_abc123',
'variables' => ['name' => 'Alice'],
'scheduledAt' => new DateTimeImmutable('+1 hour'),
], 'receipt-order-1234');In PHP [] è sia una lista vuota sia un oggetto vuoto, e un array annidato
vuoto parte come array JSON. Per i campi che sono oggetti, come variables o
headers, ometti il campo oppure passa new \stdClass().
L'array parte così com'è, quindi le chiavi devono corrispondere ai nomi dei
campi dell'API, in camelCase. Con l'API 0.24.0 o successiva, una chiave che
l'API non conosce, come reply_to o attachments, fallisce con
422 VALIDATION_ERROR invece di essere scartata. Vedi
Campi sconosciuti.
Invia un Batch
emails->sendBatch() invia da 1 a 100 email in una sola richiesta e richiede
la 1.1.0 o successiva. Ogni email accetta i campi di send() tranne
scheduledAt; il secondo argomento è la chiave di
idempotenza, che vale per tutto il batch.
$emails = [
['from' => 'hello@yourdomain.com', 'to' => ['alice@example.com'], 'subject' => 'La tua ricevuta', 'text' => 'Grazie, Alice.'],
['from' => 'hello@yourdomain.com', 'to' => ['bob@example.com'], 'subject' => 'La tua ricevuta', 'text' => 'Grazie, Bob.'],
];
$batch = $tratto->emails->sendBatch($emails, 'receipts-2026-10-21');
// Reinvia solo le email non partite, in un nuovo batch con una nuova chiave.
$failed = [];
foreach ($batch['emails'] as $i => $result) {
if ($result['status'] === 'failed') {
$failed[] = $emails[$i];
}
}
if ($failed !== []) {
$tratto->emails->sendBatch($failed, 'receipts-2026-10-21-retry-1');
}La risposta è sempre il 202, anche quando alcune email non sono partite:
controlla failed. I risultati sono nell'ordine della richiesta, e un'email
non partita non ha id ma un error con code e message. Dopo un 503 o un
500 non è partito niente: ripeti lo stesso batch con la stessa chiave. Quota,
limiti ed errori sono in Invio in Batch.
Stato e Timeline di un'Email
$email = $tratto->emails->get('email_abc123');
echo $email['status']; // 'delivered'
$events = $tratto->emails->listEvents('email_abc123');Elenca Email
$page = $tratto->emails->list(['status' => 'delivered', 'limit' => 20]);
foreach ($page['data'] as $email) {
echo "{$email['id']}: {$email['status']}\n";
}
if ($page['pagination']['hasMore']) {
$next = $tratto->emails->list(['limit' => 20, 'after' => $page['pagination']['nextCursor']]);
}Altre Risorse
Il client espone dieci risorse: emails, contacts, audiences,
campaigns, templates, webhooks, domains, analytics, flows e
workspace. Il
README mostra ogni
metodo, e examples/
ha un file eseguibile per risorsa più le integrazioni Laravel e Symfony.
I flow richiedono una API key con il permesso *: non
esistono scope flows:read o flows:write.
Gestione Errori
Ogni chiamata API fallita solleva Tratto\TrattoException: errori HTTP,
risposte che non sono JSON ed errori di rete. Gli errori di configurazione
sollevano \InvalidArgumentException.
use Tratto\TrattoException;
try {
$tratto->emails->send($params);
} catch (TrattoException $e) {
error_log(sprintf('[%d] %s: %s', $e->statusCode, $e->errorCode, $e->getMessage()));
// $e->suggestion: cosa fare, quando l'API lo dice
// $e->docs: link alla documentazione dell'errore, quando disponibile
}| Proprietà | Tipo | |
|---|---|---|
getMessage() | string | Il messaggio dell'API, oppure HTTP <status> se la risposta non ne ha |
errorCode | string | Il codice d'errore dell'API, oppure unknown_error, invalid_response, invalid_header, connection_error |
statusCode | int | Lo status HTTP (anche getCode()), 0 se non è arrivata alcuna risposta |
suggestion | ?string | Cosa fare, nelle parole dell'API; null se la risposta non lo contiene |
docs | ?string | Link alla pagina che spiega il codice; null se la risposta non lo contiene |
details | ?array | I details dell'API così come sono, per esempio la lista di un 422 ['path' => '/emails/3/to', 'message' => ...]; null se assenti. Richiede la 1.1.0 o successiva |
errorCode è lo stesso codice elencato in Codici di Errore.
L'SDK non ripete le chiamate. Per ripetere un invio in sicurezza, passa la
stessa chiave di idempotenza.
PHP 8.1: Non Loggare il Trace Completo delle Eccezioni
Da PHP 8.2 in poi la API key e il secret dei webhook sono marcati
#[\SensitiveParameter] e non compaiono mai nei trace dell'SDK. PHP 8.1 ignora
quell'attributo: l'SDK li toglie dai propri parametri prima di fare qualsiasi
altra cosa, ma un argomento di tipo sbagliato (per esempio un array dove è
attesa una stringa) solleva un TypeError prima di quel punto, e il trace di
quell'errore contiene gli argomenti.
Su PHP 8.1 logga il messaggio, errorCode e statusCode delle eccezioni
dell'SDK, non getTraceAsString() né un dump dell'eccezione, oppure imposta
zend.exception_ignore_args=On.
Verifica dei Webhook
Tratto\Webhook::verify() controlla l'header X-Tratto-Signature di una
consegna: l'HMAC-SHA256 di {t}.{raw body} con il tuo secret (whsec_...,
usato così com'è). Accetta più valori v1 (rotazione del secret) e rifiuta le
consegne più vecchie o più nuove della tolleranza, 5 minuti di default.
Restituisce un bool e non solleva mai eccezioni per una firma non valida.
Lo schema è descritto in Verifica Webhook.
Passa sempre il body grezzo: un JSON decodificato e ricodificato non corrisponde.
use Tratto\Webhook;
$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_TRATTO_SIGNATURE'] ?? null;
if (!Webhook::verify($payload, $header, (string) getenv('TRATTO_WEBHOOK_SECRET'))) {
http_response_code(400);
exit;
}
$event = json_decode($payload, true);
// Le consegne possono ripetersi: usa $event['id'] per elaborare ogni evento una volta sola.La tolleranza è il quarto argomento, in secondi:
Webhook::verify($payload, $header, $secret, 600).
Cosa Non È Incluso
I membri e il workspace stesso si gestiscono dalla dashboard: l'SDK non ha
workspace->delete(), inviteMember(), removeMember() né updateMember().
Links
- Packagist: https://packagist.org/packages/tratto/email
- GitHub: https://github.com/tratto-email/tratto-php
- API Docs: API Reference
Modifica questa pagina su GitHub
Ultimo aggiornamento