SMS z n8n: odeslání, doručenky a opakování krok za krokem
Jak poslat SMS z n8n přes TopSMS API jedním HTTP Request uzlem, bezpečně uložit klíč, přijímat doručenky Webhook uzlem s ověřením podpisu a neposlat SMS dvakrát při opakování workflow.
n8n je open‑source automatizace, kterou si spousta českých firem provozuje sama (Docker, Hetzner, nebo n8n Cloud). Pro TopSMS zatím není hotový uzel, ale nic nepotřebujete: celé napojení je jeden HTTP Request uzel pro odeslání a jeden Webhook uzel pro doručenky. Tento návod prochází obojí včetně věcí, na kterých integrace obvykle padají (zdvojené SMS při opakování, formát čísel, ověření podpisu).
Co budete potřebovat
- Účet TopSMS (10 SMS zdarma na vyzkoušení) a v dashboardu API klíč se scopes send a read. Pro ladění si vytvořte klíč v testovacím režimu: workflow běží stejně, ale nic se neodesílá ani neúčtuje.
- n8n 1.x (self‑hosted nebo Cloud).
1. Uložte klíč jako credential, ne do uzlu
V n8n otevřete Credentials → Add → Header Auth a vyplňte:
- Name:
Authorization - Value:
Bearer CLIENT_ID:SECRET(obě hodnoty z dashboardu TopSMS, oddělené dvojtečkou)
Klíč pak není vidět v exportu workflow ani v logu provedení, a výměna klíče je změna na jednom místě.
2. HTTP Request uzel: odeslání SMS
Přidejte uzel HTTP Request a nastavte:
| Pole | Hodnota |
|---|---|
| Method | POST |
| URL | https://www.topsms.cz/api/sms/send |
| Authentication | Generic Credential Type → Header Auth → vaše credential |
| Send Body | ano, Body Content Type JSON |
| Send Headers | ano, Idempotency-Key (viz krok 4) |
Tělo požadavku (Specify Body: Using JSON):
{
"to": "{{ $json.phone }}",
"text": "Dobry den, vase objednavka {{ $json.orderNumber }} je pripravena k vyzvednuti.",
"from": "MojeFirma"
}
Odpověď, se kterou můžete dál pracovat v dalších uzlech:
{ "ok": true, "id": "cmv…", "to": "+420601234567", "from": "MojeFirma", "smsCount": 1, "price": 0.98, "status": "sent" }
id si uložte (do databáze, do Google Sheets, do poznámky objednávky): podle něj dohledáte stav zprávy a spárujete doručenku. smsCount a price jsou skutečný počet částí a účtovaná cena.
Dvě praktické poznámky:
- Číslo posílejte v mezinárodním formátu (
+420…). Pokud máte v datech601 234 567, přidejte před HTTP uzel uzel Code nebo Set s výrazem{{ '+420' + $json.phone.replace(/\D/g, '').slice(-9) }}. - Diakritika zkracuje SMS ze 160 na 70 znaků a zdvojnásobuje cenu. Pro notifikace pište bez diakritiky, nebo text před odesláním normalizujte:
{{ $json.text.normalize('NFD').replace(/[̀-ͯ]/g, '') }}.
Pole from vynechte, dokud nemáte schválené vlastní Sender ID; zpráva pak odejde pod odesílatelem TopSMS.
3. Chyby, na které má workflow reagovat
HTTP Request uzel při chybovém stavu zastaví workflow. Zapněte v uzlu Settings → On Error → Continue (using error output) a větvěte podle kódu:
402nedostatek kreditu: pošlete si upozornění (e‑mail/Slack), SMS se neodeslala a nic se neúčtovalo429rate limit klíče (výchozí 60 požadavků za minutu): počkejte uzlem Wait 2 s a zopakujte400neplatné číslo nebo prázdný text: zalogujte a pokračujte, opakování nepomůže5xxnebo timeout: zopakujte se stejným Idempotency‑Key (krok 4)
4. Opakování bez zdvojených SMS: Idempotency‑Key
n8n umí uzel automaticky opakovat (Settings → Retry On Fail). Bez ochrany by při timeoutu mohla SMS odejít dvakrát. TopSMS proto podporuje hlavičku Idempotency-Key: stejný klíč do 24 hodin vrátí původní odpověď a SMS znovu nepošle.
V HTTP Request uzlu přidejte hlavičku:
- Name:
Idempotency-Key - Value:
{{ 'order-' + $json.orderNumber + '-ready' }}
Klíč odvozujte od události, ne od času (order-123-ready, invoice-456-reminder-1). Pak můžete Retry On Fail zapnout s klidem: 3 pokusy, 5 s mezi nimi.
5. Doručenky: Webhook uzel s ověřením podpisu
V dashboardu TopSMS u API klíče nastavte Webhook URL na adresu vašeho n8n Webhook uzlu (Production URL, např. https://n8n.firma.cz/webhook/topsms-dlr). TopSMS na ni pošle POST při každé změně stavu:
{ "event": "sms.status", "id": "cmv…", "to": "+420601234567", "status": "delivered", "price": 0.98, "deliveredAt": "2026-10-16T09:12:00.000Z", "errorCode": null }
Stavy: delivered, failed (s errorCode undelivered, rejected, deleted, unknown), expired. Každý pokus o doručení webhooku vidíte v dashboardu u klíče v záložce Log → Webhooky; při chybě na vaší straně to zkusíme ještě po 30 s a po 2 minutách.
Ověření podpisu. Každý webhook nese hlavičku X-TopSMS-Signature: sha256=… (HMAC‑SHA256 těla zprávy tajným klíčem webhooku, který najdete u API klíče). V n8n to ověříte uzlem Code hned za Webhook uzlem (Webhook uzel nastavte na Raw Body):
const crypto = require('crypto');
const secret = $env.TOPSMS_WEBHOOK_SECRET; // uložte do env n8n, ne do kódu
const raw = $input.first().binary?.data
? Buffer.from($input.first().binary.data.data, 'base64').toString('utf8')
: JSON.stringify($input.first().json.body);
const sig = $input.first().json.headers['x-topsms-signature'] || '';
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(raw).digest('hex');
if (sig.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
throw new Error('Neplatný podpis webhooku');
}
return [{ json: JSON.parse(raw) }];
Bez ověření může kdokoli, kdo zná adresu webhooku, posílat falešné stavy. Webhook URL nenechávejte za HTTP Basic Auth ani za Cloudflare Access, TopSMS by se k ní nedostal.
6. Dohledání stavu bez webhooku
Pokud webhooky nechcete, stav zjistíte kdykoli: GET https://www.topsms.cz/api/sms/status/{id} se stejnou Header Auth credential. Hodí se i jako „pojistka“ po výpadku vašeho n8n: uzel Schedule jednou za hodinu projde zprávy bez konečného stavu a dotáhne je.
Hotová šablona
Celý workflow (odeslání s idempotencí, větvení chyb, webhook s ověřením podpisu) má 7 uzlů a postavíte ho za půl hodiny. Testujte s klíčem v testovacím režimu: číslo končící 0000 simuluje nedoručení, 0001 expiraci, ostatní doručení, a doručenky chodí stejně jako v ostrém provozu s příznakem test: true.
Podrobnosti k API (hromadné odeslání až 500 příjemců, plánované odeslání, Verify API pro ověřovací kódy) najdete v dokumentaci REST API. Pokud používáte Make místo n8n, máme pro něj oficiální app.