Autenticazione e API Key

Gestire le API key e autenticare le richieste

Ogni richiesta all'API di Tratto si autentica con un bearer token nell'header Authorization. Ce ne sono di due tipi:

  • una chiave API, per le tue integrazioni: server, script e SDK. Quasi tutta questa pagina parla delle chiavi;
  • una sessione della dashboard, il token di una persona che ha effettuato l'accesso, che la dashboard invia per te. Alcune rotte appartengono alla persona e non a un workspace (elencare, creare ed eliminare workspace, invitare e rimuovere membri) e accettano solo una sessione: con una chiave API rispondono 403. Vedi Header del Workspace.

Formato della API Key

Le API key di Tratto seguono questo formato:

tratto_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
tratto_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • Prefisso: tratto_live_ (consegna reale) o tratto_test_ (simulata, vedi Test mode)
  • Seguito da 32 caratteri casuali
  • Usato come bearer token nell'header Authorization

L'ambiente è scritto nella chiave: una chiave di test esegue l'intera pipeline senza inviare email reali, vede solo i dati di test, e viene rifiutata dagli endpoint che raggiungono destinatari reali.

Header di Autenticazione

Includi la tua API key in ogni richiesta:

Authorization: Bearer tratto_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Esempio:

curl https://api.tratto.email/v1/emails \
  -H "Authorization: Bearer tratto_live_..."

Senza l'header, riceverai 401 Unauthorized.

Permessi

Ogni chiave porta con sé un insieme di permessi, applicati ad ogni richiesta: una chiave a cui manca il permesso richiesto da un endpoint riceve 403 Forbidden, indipendentemente dal workspace a cui appartiene.

PermessoConsente
emails:sendInviare email, programmare, annullare, riprogrammare
emails:readLeggere il log email e gli eventi di consegna
contacts:read / contacts:writeLeggere / creare, aggiornare, importare contatti e audience
domains:read / domains:writeLeggere / aggiungere, verificare, rimuovere domini di invio
templates:read / templates:writeLeggere / creare e modificare template
campaigns:read / campaigns:writeLeggere / creare e inviare campagne e flow
webhooks:read / webhooks:writeLeggere / registrare, modificare, ruotare endpoint webhook
api-keys:read / api-keys:writeLeggere / creare, modificare, revocare API key
billing:read / billing:writeLeggere piano e consumi / avviare checkout e portale fatturazione
workspace:writeModificare le impostazioni del workspace, compresi il mittente generale e i mittenti per tipo
members:writeNessuna rotta lo usa: PATCH /v1/workspace/members/:userId, che cambiava il ruolo di un membro, non esiste più e risponde 404. Invitare e rimuovere membri si fa solo dalla dashboard: ogni chiave API riceve 403 (vedi Team)
*Accesso completo — tutti i permessi sopra, inclusi quelli futuri

Concedi l'insieme più ristretto che basta allo scopo. Una chiave che invia solo email transazionali ha bisogno di emails:send e nient'altro; se rispecchia anche le iscrizioni nella tua lista contatti serve pure contacts:write.

* viene memorizzato da solo: elencarlo insieme ai permessi granulari è ridondante, dato che li copre già.

Crea una API Key

Genera una nuova API key tramite l'API, oppure da Impostazioni → API key nella dashboard.

name, env e permissions sono tutti obbligatori. Ometterne permissions (o passare un array vuoto) restituisce 422.

cURL

curl -X POST https://api.tratto.email/v1/api-keys \
  -H "Authorization: Bearer tratto_live_CHIAVE_ESISTENTE" \
  -H "Content-Type: application/json" \
  -d '{"name":"production","env":"live","permissions":["emails:send"]}'

Node.js

const response = await fetch('https://api.tratto.email/v1/api-keys', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer tratto_live_CHIAVE_ESISTENTE',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ name: 'production', env: 'live', permissions: ['emails:send'] }),
});
const { data } = await response.json();
console.log('Nuova API Key:', data.key);

Python

import requests

response = requests.post(
  'https://api.tratto.email/v1/api-keys',
  headers={
    'Authorization': 'Bearer tratto_live_CHIAVE_ESISTENTE',
    'Content-Type': 'application/json',
  },
  json={'name': 'production', 'env': 'live', 'permissions': ['emails:send']},
)
api_key = response.json()['data']['key']
print(f"Nuova API Key: {api_key}")

PHP

// tratto/email non ha metodi per le API key: HTTP con ext-curl
$ch = curl_init('https://api.tratto.email/v1/api-keys');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer tratto_live_CHIAVE_ESISTENTE',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['name' => 'production', 'env' => 'live', 'permissions' => ['emails:send']]),
    CURLOPT_RETURNTRANSFER => true,
]);
$apiKey = json_decode(curl_exec($ch), true)['data']['key'];
echo "Nuova API Key: {$apiKey}\n";

Risposta:

{
  "data": {
    "id": "key_abc123",
    "name": "production",
    "key": "tratto_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "prefix": "tratto_live_xxxx...xxxx",
    "env": "live",
    "permissions": ["emails:send"],
    "createdAt": "2025-06-30T12:00:00Z",
    "lastUsedAt": null,
    "revokedAt": null
  }
}

⚠️ Importante: Il campo key è mostrato una sola volta. Salvalo immediatamente: non verrà più visualizzato. Se lo perdi, revoca e crea una nuova chiave.

Elenca le API Key

Recupera tutte le API key per il tuo tenant.

cURL

curl https://api.tratto.email/v1/api-keys \
  -H "Authorization: Bearer tratto_live_..."

Node.js

const response = await fetch('https://api.tratto.email/v1/api-keys', {
  headers: {
    'Authorization': 'Bearer tratto_live_...',
  },
});
const { data } = await response.json();
console.log(data); // Array di oggetti API key

Python

import requests

response = requests.get(
  'https://api.tratto.email/v1/api-keys',
  headers={'Authorization': 'Bearer tratto_live_...'},
)
keys = response.json()['data']
for key in keys:
  print(f"Key: {key['prefix']} (Creata: {key['createdAt']})")

PHP

// tratto/email non ha metodi per le API key: HTTP con ext-curl
$ch = curl_init('https://api.tratto.email/v1/api-keys');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => ['Authorization: Bearer tratto_live_...'],
    CURLOPT_RETURNTRANSFER => true,
]);
$keys = json_decode(curl_exec($ch), true)['data'];
foreach ($keys as $key) {
    echo "Key: {$key['prefix']} (Creata: {$key['createdAt']})\n";
}

Risposta:

{
  "data": [
    {
      "id": "key_abc123",
      "name": "production",
      "prefix": "tratto_live_xxxx...xxxx",
      "createdAt": "2025-06-30T12:00:00Z",
      "lastUsedAt": "2025-06-30T13:45:00Z"
    },
    {
      "id": "key_def456",
      "name": "development",
      "prefix": "tratto_live_yyyy...yyyy",
      "createdAt": "2025-06-29T10:00:00Z",
      "lastUsedAt": "2025-06-29T15:20:00Z"
    }
  ]
}

Nota: La chiave completa non è mai restituita dopo la creazione, solo il prefisso (primi e ultimi 4 caratteri).

Modifica i permessi di una chiave

Cambia i permessi di una chiave esistente, senza revocarla né ruotare il segreto. Il valore della chiave non cambia, quindi nulla di ciò che la usa va rideployato — i permessi vengono letti ad ogni richiesta e il nuovo insieme vale già dalla successiva.

cURL

curl -X PATCH https://api.tratto.email/v1/api-keys/key_abc123 \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"permissions":["emails:send","contacts:write"]}'

Node.js

const response = await fetch('https://api.tratto.email/v1/api-keys/key_abc123', {
  method: 'PATCH',
  headers: {
    'Authorization': 'Bearer tratto_live_...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ permissions: ['emails:send', 'contacts:write'] }),
});
const { data } = await response.json();
console.log('Permessi:', data.permissions);

Python

import requests

response = requests.patch(
  'https://api.tratto.email/v1/api-keys/key_abc123',
  headers={
    'Authorization': 'Bearer tratto_live_...',
    'Content-Type': 'application/json',
  },
  json={'permissions': ['emails:send', 'contacts:write']},
)
print(f"Permessi: {response.json()['data']['permissions']}")

PHP

// tratto/email non ha metodi per le API key: HTTP con ext-curl
$ch = curl_init('https://api.tratto.email/v1/api-keys/key_abc123');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer tratto_live_...',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['permissions' => ['emails:send', 'contacts:write']]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
echo 'Permessi: ', implode(', ', $data['permissions']), "\n";

permissions sostituisce l'insieme precedente, non ci si fonde. Invia la lista completa che vuoi ottenere.

Richiede api-keys:write. Altre risposte:

StatoQuando
409La chiave è revocata — i permessi non sono più modificabili. Creane una nuova.
422Permesso sconosciuto, lista vuota, o tentativo di rimuovere api-keys:write dalla chiave che sta facendo la richiesta.

Non puoi togliere api-keys:write dalla chiave con cui ti stai autenticando: nessun altro endpoint potrebbe restituirtelo, quindi resteresti chiuso fuori dalla gestione delle chiavi. Rimuoverlo da una chiave diversa è consentito — quello è recuperabile.

Revoca una API Key

Revoca immediatamente una API key per impedirne l'uso.

cURL

curl -X DELETE https://api.tratto.email/v1/api-keys/key_abc123 \
  -H "Authorization: Bearer tratto_live_..."

Node.js

const response = await fetch('https://api.tratto.email/v1/api-keys/key_abc123', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer tratto_live_...',
  },
});
const { data } = await response.json();
console.log('Revocata:', data.id);

Python

import requests

response = requests.delete(
  'https://api.tratto.email/v1/api-keys/key_abc123',
  headers={'Authorization': 'Bearer tratto_live_...'},
)
print(f"Revocata: {response.json()['data']['id']}")

PHP

// tratto/email non ha metodi per le API key: HTTP con ext-curl
$ch = curl_init('https://api.tratto.email/v1/api-keys/key_abc123');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => ['Authorization: Bearer tratto_live_...'],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
echo "Revocata: {$data['id']}\n";

Una volta revocata, le richieste con quella chiave restituiranno 401 Unauthorized.

Best Practice di Sicurezza delle Chiavi

1. Non Esporre le Chiavi Client-Side

Le API key concedono accesso completo al workspace. Non includerle mai in:

  • JavaScript frontend (React, Vue, Angular)
  • App mobile (iOS, Android)
  • Codice sorgente pubblicato su GitHub

Invece, chiama la tua API backend, che tiene la chiave al sicuro.

Per indicazioni specifiche per framework vedi React (Server Component e Server Action) e Angular (SSR e AnalogJS/Nitro).

Sbagliato:

// ❌ NON FARE QUESTO MAI
const trattoKey = 'tratto_live_...';
const response = await fetch('https://api.tratto.email/v1/emails', {
  headers: { 'Authorization': `Bearer ${trattoKey}` },
});

Corretto:

// ✅ Chiama il tuo backend
const response = await fetch('/api/send-email', {
  method: 'POST',
  body: JSON.stringify({ to, subject, text }),
});

2. Tratta le Chiavi come Password

  • Memorizza in variabili di ambiente (.env.local, non .env)
  • Usa la gestione dei segreti per la produzione (GitHub Secrets, HashiCorp Vault, ecc.)
  • Non commitarle su Git

3. Ruota le Chiavi Regolarmente

  • Crea una nuova chiave
  • Aggiorna i tuoi servizi per usare la nuova chiave
  • Aspetta che tutti i deployment finiscano
  • Revoca la vecchia chiave

4. Usa Chiavi Specifiche per gli Ambienti

Crea chiavi separate per:

  • production: Usata solo in produzione
  • staging: Usata solo in staging
  • development: Usata solo nello sviluppo locale

In questo modo, una chiave di sviluppo compromessa non compromette la produzione.

5. Monitora l'Uso delle Chiavi

Controlla lastUsedAt nelle risposte di elenco per identificare chiavi non utilizzate. Revoca periodicamente le chiavi vecchie non utilizzate.

Risposte di Errore

401 Unauthorized

Causa: Credenziali mancanti o non valide: una chiave API errata o revocata, oppure una sessione della dashboard scaduta.

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing or invalid Authorization header",
    "docs": "https://docs.tratto.email/it/authentication"
  }
}

Fix:

  • Assicurati che l'header Authorization: Bearer tratto_live_... sia presente
  • Controlla per errori di digitazione nella API key
  • Verifica che la chiave non sia stata revocata
  • Nella dashboard, accedi di nuovo

403 Forbidden

Causa: La API key è valida ma non ha permesso per questa operazione.

{
  "error": {
    "code": "FORBIDDEN",
    "message": "Your API key does not have permission to perform this action",
    "docs": "https://docs.tratto.email/it/authentication"
  }
}

La risposta nomina il permesso mancante, così sai quale aggiungere:

{
  "error": {
    "code": "FORBIDDEN",
    "message": "Missing required permission: contacts:write",
    "suggestion": "Create a new API key with the 'contacts:write' permission."
  }
}

Si risolve concedendo quel permesso alla chiave — vedi Modifica i permessi di una chiave — oppure usando una chiave che lo ha già. Controlla i permessi correnti con GET /v1/api-keys.

Nella dashboard, un membro del workspace che non è il proprietario riceve 403 con Only the workspace owner can do this. su chiavi API, webhook, billing, modifiche ai domini e impostazioni del workspace, qualunque permesso abbia. Vedi Team.

Header del Workspace

Una richiesta autenticata con una sessione della dashboard (un ID token Firebase, come fa la dashboard quando chiama l'API) può aggiungere l'header facoltativo Tratto-Workspace per scegliere su quale workspace operare, fra quelli a cui l'account ha accesso: quelli di cui è proprietario e quelli in cui è membro attivo.

Tratto-Workspace: ws_xxxxxxxxxxxx

Esempio:

curl https://api.tratto.email/v1/workspace \
  -H "Authorization: Bearer $FIREBASE_ID_TOKEN" \
  -H "Tratto-Workspace: ws_..."
  • Senza l'header, la richiesta opera sul workspace predefinito dell'account.
  • Con una chiave API l'header è ignorato: una chiave appartiene a un solo workspace e opera sempre su quello.
  • Non c'è ripiego. Un header che indica un workspace a cui l'account non ha accesso viene rifiutato, non reindirizzato su un altro workspace: 403 NOT_A_MEMBER se l'account non ne è proprietario né membro attivo, 403 TENANT_CLOSED se il workspace è stato eliminato. Un valore che non è un id di workspace riceve 422 VALIDATION_ERROR. Vedi Codici di errore.
  • Un id di workspace è lungo da 1 a 128 caratteri fra A-Z, a-z, 0-9, _ e -, e non inizia mai con __.

Le chiavi API restano il modo consigliato per integrare da un server. L'header serve agli strumenti che usano una sessione della dashboard, come la dashboard stessa e gli strumenti interni. Gli SDK non lo impostano.

Header di Idempotenza

L'header Idempotency-Key previene richieste duplicate su determinati endpoint (operazioni POST che modificano lo stato).

Uso:

POST /v1/emails
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

Regole:

  • Il valore dovrebbe essere un UUID v4 (RFC 4122)
  • Tratto mette in cache la risposta per 24 ore
  • Se riprovi con la stessa chiave, ottieni la stessa risposta senza un'azione duplicata
  • La risposta in cache viene restituita per ogni retry con quella chiave; il corpo non viene confrontato, quindi riusa una chiave solo per l'operazione che l'ha creata

Esempio:

import { v4 as uuidv4 } from 'uuid';

const idempotencyKey = uuidv4();
const response = await fetch('https://api.tratto.email/v1/emails', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer tratto_live_...',
    'Idempotency-Key': idempotencyKey,
  },
  body: JSON.stringify({
    from: 'hello@tuodominio.it',
    to: 'user@example.com',
    subject: 'Ciao',
    text: 'Mondo',
  }),
});

Vedi Idempotenza per ulteriori dettagli.


Prossimi Passi


Modifica questa pagina su GitHub

Ultimo aggiornamento