Přeskočit na obsah

SMS brána přes API za 10 minut v Node.js

2. 9. 2026 · 6 minut čtení · Aktualizováno 9. 10. 2026

Abys z Node.js poslal SMS, stačí jeden POST požadavek na https://volai.cz/v1/messages s hlavičkou Authorization: Bearer vk_... a JSON tělem {to, body}. Node 20 má fetch vestavěný, takže nepotřebuješ žádný balíček navíc. Funkční příklad je hned pod tímhle odstavcem, včetně ošetření chyb a idempotence, aby ti výpadek sítě neposlal stejnou zprávu dvakrát.

Co budeš potřebovat

  • Node.js 18 nebo novější - fetch je ve vestavěné knihovně už od téhle verze; doporučujeme aktuální podporovanou LTS řadu (24, stav k 2. 9. 2026).
  • Účet na volai a API klíč z portálu (Nastavení -> API a MCP), ve tvaru vk_....
  • Aspoň jedno dobití kreditu. SMS jdou až po prvním dobití, uvítací kredit na ně použít nejde - hovory si z něj vyzkoušíš, textovku ne.
  • Telefonní číslo příjemce v Česku nebo na Slovensku (+420 nebo +421) - do jiných zemí SMS neposíláme.
  • Vlastní telefonní číslo NEPOTŘEBUJEŠ. Zprávy odcházejí pod sdíleným jménem SMSinfo - operátoři pro české sítě povolují jen schválená jména, vlastní jméno odesílatele u sdíleného odesílatele nastavit nejde a na SMSinfo příjemce odpovědět nemůže. Svou identitu proto napiš přímo do textu zprávy, tak jako v příkladu níž. Chceš-li, aby šlo odpovědět, pošli zprávu z vlastního SMS čísla (doplněk, viz upozornění níž).

Pošli první SMS

Requestu stačí dvě pole: to (číslo příjemce) a body (text zprávy). Odpověď vrátí id zprávy, status, cenu v haléřích (priceHal) a počet segmentů (segments) - k oběma se za chvíli vrátíme.

javascript
const response = await fetch("https://volai.cz/v1/messages", {
  method: "POST",
  headers: {
    Authorization: "Bearer vk_TVUJ_KLIC",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "+420777123456",
    body: "Ahoj z volai! - Moje Appka",
  }),
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error("volai " + error.code + ": " + error.message);
}

const message = await response.json();
console.log(message);
// { id: "msg_7c1f9a2e", status: "sent", priceHal: 136, segments: 1 }

Kolik stojí SMS a co jsou segmenty?

Jedna SMS do jednoho segmentu stojí 1,36 Kč bez DPH (cena platná k 2. 9. 2026). Kolik segmentů zpráva zabere, závisí na tom, jestli obsahuje diakritiku - operátoři počítají SMS podle standardu GSM 03.38:

KódováníZnaků v 1. segmentuZnaků v dalších dílech
GSM-7 (bez diakritiky)160153
UCS-2 (s diakritikou)7067

Stačí JEDEN znak mimo základní GSM-7 abecedu - typicky česká diakritika (ě š č ř ž ý á í ů ď ť ň) - a celá zpráva přepne na UCS-2, i kdyby byl ve zprávě jen jeden háček. Tip: chceš-li se spolehlivě vejít do jednoho segmentu, piš bez diakritiky.

Příklad: zpráva s diakritikou nad 70 znaků

Tahle zpráva má 112 znaků a obsahuje diakritiku, takže se počítá jako UCS-2 (limit 67 znaků na segment u vícedílné zprávy) - vyjde na 2 segmenty, tedy 2,72 Kč, ne 1,36 Kč:

javascript
await fetch("https://volai.cz/v1/messages", {
  method: "POST",
  headers: {
    Authorization: "Bearer vk_TVUJ_KLIC",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "+420777123456",
    body: "Příliš žluťoučký kůň úpěl ďábelské ódy - a tahle věta má přes sedmdesát znaků, takže spadne do druhého segmentu.",
  }),
});
// -> { id: "msg_9a3f1c7d", status: "sent", priceHal: 272, segments: 2 }

Idempotence: bezpečné opakování požadavku

Pošli navíc hlavičku Idempotency-Key s libovolným vlastním řetězcem, třeba ID objednávky. Když požadavek selže na výpadku sítě a workflow ho zopakuje se STEJNOU hlavičkou, volai vrátí uloženou odpověď z prvního pokusu a zprávu neodešle podruhé - klíč platí 24 hodin.

bash
curl -X POST https://volai.cz/v1/messages \
  -H "Authorization: Bearer vk_TVUJ_KLIC" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: objednavka-4471" \
  -d '{"to": "+420777123456", "body": "Vaše objednávka č. 4471 byla přijata."}'

Bezpečně zacházej s API klíčem

API klíč (vk_...) má stejnou váhu jako heslo - kdokoli ho zná, může tvým jménem posílat zprávy a utrácet kredit z tvého účtu. Do zdrojového kódu, který commitneš do repozitáře, ho proto nepiš nikdy. Patří do proměnné prostředí (process.env.VOLAI_API_KEY v Node.js) nebo do správce tajemství, který používá tvůj hosting.

javascript
// .env.local (nikdy necommituj do repozitáře)
// VOLAI_API_KEY=vk_tvuj_klic

const response = await fetch("https://volai.cz/v1/messages", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.VOLAI_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ to, body }),
});

Endpoint volej vždy ze serveru, ne z prohlížeče - kdokoli by si klíč přečetl v developerských nástrojích prohlížeče a začal na tvůj účet posílat vlastní zprávy. Uniknul-li klíč (třeba omylem v commitu), vygeneruj si v portálu nový a ten starý zneplatni - stará hodnota přestane fungovat okamžitě.

Vyzkoušej to nejdřív bezpečně

Než napojíš odesílání SMS na skutečný proces (třeba potvrzení objednávky), pošli první pár zpráv na vlastní telefonní číslo. Uvidíš přesně to, co uvidí zákazník - jak vypadá odesílatel SMSinfo, jak dlouho doručení trvá a jestli se diakritika počítá tak, jak čekáš. Teprve pak přepni to na skutečné číslo zákazníka.

Co dělat, když API vrátí chybu?

Neúspěšná odpověď má vždy tvar {error: {code, message}} a HTTP status podle typu chyby:

StavKódCo to znamená
400invalid_numberTo není platné české nebo slovenské telefonní číslo.
400invalid_bodyText zprávy musí mít 1 až 765 znaků.
400unsupported_countryČíslo mimo ČR/SR, SMS tam neposíláme.
400recipient_cannot_receive_smsPevná linka - SMS by nedorazila. Nic se neúčtuje.
402insufficient_creditKredit nepokryje cenu všech segmentů zprávy, nebo účet ještě nedobil kredit - SMS jdou až po prvním dobití, uvítací kredit na ně použít nejde.
429rate_limitedVíc než 1 SMS za 2 vteřiny, nebo přes 100 SMS na účet denně.
502send_failedTelefonní síť žádost odmítla. Kredit se nestrhl, zkus to znovu.
502send_unknownZprávu jsme předali síti, potvrzení nedorazilo - účtuje se, neposílej ji prosím znovu naslepo.

Jaké stavy může zpráva mít?

Pole status ve výpisu (GET /v1/messages) i v detailu jedné zprávy nabývá pěti hodnot: pending (uložená, ještě se odesílá), sent (operátor odeslání potvrdil), failed (operátor odmítl - neúčtuje se), unknown (potvrzení o doručení nedorazilo, typicky timeout na straně operátora - přesto se účtuje plnou cenou, protože zpráva mohla dojít) a received (jen u zpráv přijatých na SMS čísle, tedy s direction=in; odeslané zprávy ho nemají).

Každá zpráva nese i pole direction (out u odeslaných, in u přijatých), smsNumber (SMS číslo, ze kterého zpráva odešla nebo na které přišla; u zpráv pod sdíleným jménem SMSinfo je null) a deliveryStatus (delivered nebo undelivered, doručenka od operátora; vzniká jen u SMS odeslaných ze SMS čísla, u ostatních zpráv je null).

Jak rozeslat víc SMS najednou?

Rate limit dovoluje nejvýš jednu SMS za dvě vteřiny na účet a nejvýš 100 SMS denně. Pro rozeslání víc zpráv najednou (třeba připomínek schůzek) je jednodušší mezi požadavky krátce počkat, než chytat rate_limited a zkoušet znovu:

javascript
const messageText = "Připomínáme zítřejší termíny - Moje Appka";

for (const to of recipients) {
  const response = await fetch("https://volai.cz/v1/messages", {
    method: "POST",
    headers: {
      Authorization: "Bearer vk_TVUJ_KLIC",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ to, body: messageText }),
  });

  if (!response.ok) {
    const { error } = await response.json();
    console.error("volai " + to + ": " + error.code);
  }

  // Limit je nejvýš 1 SMS za 2 vteřiny na účet - krátká pauza mezi
  // požadavky je jednodušší než řešit rate_limited a zkoušet znovu.
  await new Promise((resolve) => setTimeout(resolve, 2100));
}

Příjem SMS umí jen doplněk SMS číslo

Hlasová čísla volai SMS nepřijímají, takže odpověď na zprávu odeslanou pod sdíleným jménem SMSinfo se nikde neobjeví. Chceš-li SMS přijímat a odesílat z vlastního čísla, přikup si doplněk SMS číslo: české mobilní číslo jen pro SMS, bez hovorů, 390 Kč na 30 dní bez DPH, přijaté SMS jsou v ceně. Přijaté zprávy přečteš v GET /v1/messages?direction=in, v MCP nástroji list_messages nebo ve webhooku message.received a uchováváme je 90 dní; odeslání z čísla stojí 1,90 Kč za segment a jde jen na česká čísla. Z čísla pošleš SMS polem fromNumber v POST /v1/messages (číslo vezmi z GET /v1/sms-numbers). Denní strop odeslání z čísla je 20 SMS na účet v prvních 30 dnech od koupě čísla, potom 50. Na účtu mohou být nejvýš dvě SMS čísla a koupit je jde až po prvním dobití kreditu. Ověřovací kódy od služeb (banky, Google a podobně) nezaručujeme, číslo je určené pro psaní s lidmi a příjem jsme ověřili na síti O2. Podrobnosti jsou v sekci SMS čísla v dokumentaci API. Kdy je pro příjem SMS lepší volba Twilio, popisuje srovnání Twilio a volai.

Shrnutí: jeden POST /v1/messages s poli to a body pošle SMS, hlavička Idempotency-Key ochrání workflow před dvojím odesláním při opakování požadavku a cena se počítá po segmentech podle toho, jestli text obsahuje diakritiku. Klíč drž mimo repozitář a nejdřív si pošli zprávu sám sobě.

Co dál

Plná reference REST API (čísla, hovory, webhooky, agenti) je na /docs/api. Chceš vědět hned, jakmile volai zprávu předá operátorovi, bez pollování GET /v1/messages? Přihlas se k odběru události message.sent - potvrzuje jen převzetí operátorem, ne doručení do telefonu (u zpráv pod sdíleným jménem to volai nevidí, viz výš; zprávy odeslané z vlastního SMS čísla mají doručenku) - viz dokumentace webhooků. A pokud kromě SMS potřebuješ i telefonní číslo, které samo zvedá hovory, mrkni na AI recepční.

Vyzkoušej volai - prvních 50 Kč na nás

Založíš účet za minutu a rovnou máš na kontě startovní kredit na hovory a číslo - SMS pošleš po prvním dobití.

Vyzkoušet zdarma