API Reference
Twilio-style REST API. Una sola chiamata per inviare notifiche su WhatsApp, Telegram, Email, Push, Discord e Slack.
Quick start
- Crea il workspace su notify.trovido.com/register
- Genera una API key da /sources
- Fai la prima chiamata
- Verifica il delivery in /deliveries
Piano Free: 500 msg/mese gratis, 3 canali, nessuna carta richiesta.
curl -X POST https://notify.trovido.com/api/notifyhub/v1/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipient": {
"name": "Mario Rossi",
"email": "[email protected]"
},
"template": "hello_world",
"channel": "email",
"vars": { "name": "Mario" }
}'
$response = Http::withToken('YOUR_API_KEY')
->post('https://notify.trovido.com/api/notifyhub/v1/send', [
'recipient' => ['name' => 'Mario', 'email' => '[email protected]'],
'template' => 'hello_world',
'channel' => 'email',
'vars' => ['name' => 'Mario'],
]);
const res = await fetch('https://notify.trovido.com/api/notifyhub/v1/send', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { name: 'Mario', email: '[email protected]' },
template: 'hello_world',
channel: 'email',
vars: { name: 'Mario' },
}),
});
import requests
r = requests.post('https://notify.trovido.com/api/notifyhub/v1/send',
headers={'Authorization': 'Bearer YOUR_API_KEY'},
json={
'recipient': {'name': 'Mario', 'email': '[email protected]'},
'template': 'hello_world',
'channel': 'email',
'vars': {'name': 'Mario'},
})
{
"status": "queued",
"delivery_id": 12345,
"channel": "email",
"recipient": { "id": 1, "name": "Mario Rossi" },
"template": "hello_world"
}
/api/notifyhub/v1/send
Invia una notifica su qualsiasi canale. Il destinatario viene creato automaticamente se non esiste.
Headers
| Authorization | required | Bearer YOUR_API_KEY |
| Content-Type | required | application/json |
Body
| recipient | object req — per ID (recipient.id) oppure per contatti (name + email/phone) |
| template | string req — slug del template |
| channel | string — email | whatsapp | telegram (default: auto) |
| vars | object — sostituisce {{ "{{key}}" }} nel template |
| priority | string — low | normal | high |
| scheduled_for | ISO 8601 — schedula invio futuro |
| idempotency_key | string (max 128) — stessa chiave entro 1h ritorna stesso delivery |
| actions[] | array — bottoni interattivi (max 5) con slug, label, callback_url |
| media | object — file multimediale (vedi Media & allegati) |
| attachments[] | array (max 5) — allegati email (vedi Media & allegati) |
{
"recipient": { "name": "Mario", "email": "[email protected]" },
"template": "order_confirmed",
"channel": "email",
"vars": { "order_id": "ORD-2024-1234" }
}
{
"recipient": { "whatsapp_phone": "+393331234567" },
"template": "review_request",
"channel": "whatsapp",
"actions": [
{ "slug": "approve", "label": "Pubblica",
"callback_url": "https://sito.it/api/review/456" },
{ "slug": "reject", "label": "Rifiuta",
"callback_url": "https://sito.it/api/review/456" }
]
}
{
"recipient": { "telegram_chat_id": "624419126" },
"template": "alert_critical",
"channel": "telegram",
"priority": "high",
"vars": { "service": "Mailer", "error": "SMTP timeout" }
}
/api/notifyhub/v1/send/batch
Invia fino a 100 messaggi in una sola chiamata. Ogni messaggio usa lo stesso formato di /send.
Body
| messages[] | array (1-100) req — ogni elemento ha recipient, template, vars, channel, ecc. |
Risposta
Ritorna il conteggio totale + risultato per ogni messaggio. I messaggi vengono processati indipendentemente: un fallimento non blocca gli altri.
{
"messages": [
{ "recipient": { "id": 1 }, "template": "weekly_digest", "channel": "email" },
{ "recipient": { "id": 2 }, "template": "weekly_digest", "channel": "telegram" },
{ "recipient": { "id": 3 }, "template": "weekly_digest", "channel": "whatsapp" }
]
}
{
"total": 3,
"queued": 3,
"failed": 0,
"results": [
{ "index": 0, "status": "queued", "delivery_id": 101 },
{ "index": 1, "status": "queued", "delivery_id": 102 },
{ "index": 2, "status": "queued", "delivery_id": 103 }
]
}
Media & allegati
Invia immagini, video, documenti e allegati insieme alle notifiche.
media (object, opzionale)
| media.url | string req — URL pubblica del file |
| media.type | string — image | video | document (default: image) |
| media.caption | string — Didascalia opzionale (max 1000 char) |
| media.filename | string — Nome file (per document) |
attachments[] (array, max 5)
Allegati per il canale email. Fornire tramite URL o contenuto base64.
| attachments[].url | string — URL del file (alternativo a content) |
| attachments[].content | string — Contenuto base64 (alternativo a url) |
| attachments[].filename | string req — Nome del file |
| attachments[].mime_type | string — MIME type (opzionale) |
Comportamento per canale
- WhatsApp — Il media appare come header image del template (richiede template con header IMAGE approvato su Meta).
- Telegram — Inviato come foto separata prima del messaggio di testo.
- Email — Aggiunto automaticamente come allegato al messaggio.
{
"recipient": { "whatsapp_phone": "+393331234567" },
"template": "review_approval",
"channel": "whatsapp",
"vars": { "1": "Mario Rossi", "2": "5 stelle", "3": "Ottimo servizio!" },
"media": {
"url": "https://example.com/photo.jpg",
"type": "image"
}
}
{
"recipient": { "email": "[email protected]" },
"template": "report_monthly",
"channel": "email",
"attachments": [
{
"url": "https://sito.it/reports/apr-2026.pdf",
"filename": "report-aprile.pdf",
"mime_type": "application/pdf"
}
]
}
/api/notifyhub/v1/usage
Ritorna i consumi del workspace nel periodo di fatturazione corrente. Auth via Bearer API key.
Campi risposta
| messages.used | Messaggi inviati nel periodo |
| messages.limit | Limite del piano (null = illimitato) |
| messages.remaining | Messaggi residui |
| whatsapp.used | WhatsApp consumati (bundle separato) |
| period_start/end | Date del periodo corrente |
{
"plan_slug": "nh-pro",
"plan_name": "Pro",
"messages": { "used": 1234, "limit": 10000, "remaining": 8766, "percentage": 12 },
"whatsapp": { "used": 45, "limit": null },
"period_start": "2026-04-01",
"period_end": "2026-04-30"
}
/api/notifyhub/v1/health
Endpoint pubblico (no auth). Per monitoring uptime.
Campi risposta
| status | healthy | degraded (coda > 100) |
| worker | active | stale (fermo con coda piena) |
| queue_depth | Messaggi in attesa |
| last_hour | Contatori sent/delivered/failed ultima ora |
{
"status": "healthy",
"worker": "active",
"queue_depth": 0,
"last_hour": { "sent": 12, "delivered": 11, "failed": 1 },
"timestamp": "2026-04-29T20:38:52+00:00"
}
Webhook subscriptions
Registra endpoint HTTP per ricevere callback in tempo reale quando lo stato di una delivery cambia.
Eventi disponibili
| delivery.queued | Messaggio accodato |
| delivery.sent | Inviato al provider |
| delivery.delivered | Consegnato al destinatario |
| delivery.read | Letto (WhatsApp blue ticks) |
| delivery.failed | Invio fallito definitivamente |
| delivery.action_taken | Utente ha cliccato un bottone |
Verifica firma
Ogni POST contiene l'header X-NotifyHub-Signature con HMAC-SHA256 del body firmato con il tuo whsec_* secret. Rifiuta richieste con firma non valida.
Auto-disable
Dopo 10 fallimenti consecutivi (timeout o HTTP non-2xx) l'endpoint viene disattivato automaticamente. Riattivabile dal portale.
POST https://tuo-sito.it/webhook/notifyhub
X-NotifyHub-Signature: a1b2c3d4e5...
X-NotifyHub-Event: delivery.delivered
Content-Type: application/json
{
"event": "delivery.delivered",
"timestamp": "2026-04-29T15:30:00+00:00",
"delivery": {
"id": 12345,
"status": "delivered",
"recipient_id": 1,
"channel": "whatsapp",
"template_slug": "review_request",
"external_id": "wamid.abc123",
"error_message": null,
"created_at": "2026-04-29T15:29:55+00:00"
}
}
$body = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_NOTIFYHUB_SIGNATURE'];
$expected = hash_hmac('sha256', $body, $secret);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
exit('Invalid signature');
}
Action callbacks
Quando l'utente clicca un bottone interattivo (WhatsApp/Telegram), NotifyHub fa POST al tuo callback_url con firma HMAC.
Flusso
- Invii messaggio con
actions[] - NotifyHub genera URL firmati per ogni bottone
- Utente clicca → NotifyHub POST al tuo endpoint
- Verifica firma e processa l'azione
Replay protection
Controlla X-Notifyhub-Timestamp — rifiuta richieste oltre i 5 minuti dal tempo corrente.
POST https://tuo-sito.it/api/reviews/456/moderate
X-Notifyhub-Signature: sha256=abc123...
X-Notifyhub-Timestamp: 1714000000
Content-Type: application/json
{
"delivery_id": 12345,
"action_slug": "approve",
"payload": { "review_id": 456 },
"actor": { "name": "Mario", "channel": "whatsapp" }
}
Verifica firma (PHP)
$body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_NOTIFYHUB_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_NOTIFYHUB_TIMESTAMP'] ?? '';
// Rifiuta richieste oltre 5 minuti
if (abs(time() - (int) $timestamp) > 300) {
abort(403, 'Request too old');
}
$expected = 'sha256=' . hash_hmac('sha256', $body, $yourApiKey);
if (!hash_equals($expected, $signature)) {
abort(403, 'Invalid signature');
}
$data = json_decode($body, true);
// Processa: $data['action_slug'], $data['payload'], etc.
Note per canale
- Template Meta obbligatorio fuori dalla finestra 24h.
- I bottoni URL richiedono template approvato.
- Le immagini funzionano solo con template che hanno header IMAGE.
- Numero destinatario in formato E.164 (
+39...).
Telegram
- Il destinatario deve aver avviato il bot con
/start. - I bottoni usano inline keyboard.
- Le immagini vengono inviate come foto prima del testo.
- Usa
telegram_chat_idnel recipient.
- Allegati: max 5 per messaggio (URL o base64).
- Header
List-Unsubscribeaggiunto automaticamente. - Soggetto dal template o dal campo
subject. - HTML generato automaticamente dal body testuale.
Messenger
- Richiede Facebook Page connessa con permesso
pages_messaging. - Finestra 24h standard messaging policy di Meta.
- I bottoni usano quick-reply o URL button template.
- Immagini e video supportati come allegati.
Discord
- Usa webhook URL del canale Discord.
- Supporta embed con colore, titolo e campi.
- Max 2000 caratteri per messaggio.
- Nessuna interattività (solo invio, non bottoni).
Slack
- Webhook URL o Bot Token con
chat:write. - Supporta Block Kit per layout ricchi.
- Canale specificato nel recipient o nella source.
- Nessuna interattività via action callback.
Codici di stato
| 202 | Notifica accettata e in coda |
| 200 | Idempotency replay (stessa delivery) |
| 401 | API key mancante o non valida |
| 403 | API key revocata |
| 404 | Template non trovato |
| 422 | Validazione fallita (vedi details) |
| 429 | Rate limit o quota mensile esaurita |
{
"status": "validation_failed",
"message": "The given data was invalid.",
"details": {
"recipient": ["The recipient field is required."],
"template": ["The template field is required."]
}
}
{
"error": "quota_exceeded",
"message": "Monthly quota reached (500/500). Buy credits or upgrade.",
"used": 500,
"limit": 500,
"hint": "credits_empty"
}
{
"status": "queued",
"delivery_id": 1234,
"channel": "whatsapp",
"overage": {
"credits_deducted": 3,
"channel_cost": 3
}
}
Se la quota mensile è esaurita ma hai crediti, il messaggio viene inviato e i crediti scalati. Costi: Telegram/Email 1 cr, WhatsApp 3 cr, SMS 10 cr.
Rate limiting
- Per API key: default 100 req/ora, configurabile da /sources
- Quota mensile: Free 500, Pro 10K, Agency 50K. WhatsApp ha bundle separato.
- Idempotenza: campo
idempotency_key— stessa chiave entro 1h ritorna lo stesso delivery conreplayed: true - Retry automatici: 3 tentativi con backoff 30s → 2min → 10min
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
Retry-After: 3600
Hai bisogno di aiuto?