Gå til indholdet

Send SMS fra Node.js på 10 minutter

September 2, 2026 · 6 minutter læsetid · Opdateret October 9, 2026

For at sende en SMS fra Node.js er det nok med én POST-forespørgsel til https://volai.cz/v1/messages med headeren Authorization: Bearer vk_... og en JSON-body med {to, body}. Node 20 har fetch indbygget, så der skal ikke installeres nogen pakke. Et fungerende eksempel følger lige nedenfor, med fejlhåndtering og idempotens, så et netværkshak ikke sender den samme besked to gange.

Det skal der bruges

  • Node.js 18 eller nyere - fetch er indbygget siden denne version; vi anbefaler den aktuelt understøttede LTS-linje (24, pr. 2. september 2026).
  • En volai-konto og en API-nøgle fra portalen (Indstillinger -> API & MCP) i formen vk_....
  • Mindst én optankning af saldoen. SMS kræver en første optankning; velkomstsaldoen dækker dem ikke - den kan bruges til at prøve opkald, ikke SMS.
  • Et modtagernummer i Tjekkiet eller Slovakiet (+420 eller +421) - vi sender ikke SMS til andre lande.
  • Der skal IKKE bruges et eget telefonnummer. Beskederne sendes under det fælles afsendernavn SMSinfo - operatørerne tillader kun godkendte afsendernavne på tjekkiske net, så et eget afsendernavn er ikke muligt med den fælles afsender, og modtagere kan ikke svare til SMSinfo. Afsenderens identitet skrives derfor i selve beskedteksten, som i eksemplet nedenfor. Hvis der skal kunne svares, sendes i stedet fra et eget mobilnummer (et tilkøb, se bemærkningen nedenfor).

Send den første SMS

Forespørgslen kræver kun to felter: to (modtagernummer) og body (beskedtekst). Svaret indeholder beskedens id, dens status, prisen i hundrededele af en krone (priceHal) og antallet af segmenter (segments) - mere om begge dele nedenfor.

javascript
const response = await fetch("https://volai.cz/v1/messages", {
  method: "POST",
  headers: {
    Authorization: "Bearer vk_DIN_NOGLE",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "+420777123456",
    body: "Hej fra volai! - Min App",
  }),
});

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 }

Hvad koster en SMS, og hvad er segmenter?

En SMS med ét segment koster 1,36 kr uden moms (pris pr. 2. september 2026). Hvor mange segmenter en besked fylder, afhænger af, om den indeholder tegn uden for GSM-7-alfabetet - operatørerne tæller SMS efter standarden GSM 03.38:

KodningTegn i 1. segmentTegn i de følgende dele
GSM-7 (standardtegn, også æ ø å)160153
UCS-2 (tegn uden for GSM-7)7067

Ét enkelt tegn uden for det grundlæggende GSM-7-alfabet (typografiske anførselstegn “ ”, emoji, de fleste ikke-latinske skrifttegn, tjekkiske tegn som ě š č ř ž) skifter HELE beskeden til UCS-2, selv om det er det eneste af slagsen i teksten. Æ, ø og å hører derimod til GSM-7. Tip: for pålideligt at holde sig inden for ét segment bruges kun almindelige GSM-7-tegn og lige anførselstegn.

Eksempel: en besked med specialtegn over 70 tegn

Denne besked er 121 tegn lang og indeholder typografiske anførselstegn, så den tælles som UCS-2 (67 tegn pr. segment ved en flerdelt besked) - det giver 2 segmenter, altså 2,72 kr i stedet for 1,36 kr:

javascript
await fetch("https://volai.cz/v1/messages", {
  method: "POST",
  headers: {
    Authorization: "Bearer vk_DIN_NOGLE",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "+420777123456",
    body: "Hej, vi minder om aftalen i morgen kl. 10:00. Passer tidspunktet ikke, så skriv blot “ny tid”, så finder vi en anden tid.",
  }),
});
// -> { id: "msg_9a3f1c7d", status: "sent", priceHal: 272, segments: 2 }

Idempotens: gentag forespørgsler sikkert

Tilføj en Idempotency-Key-header med en vilkårlig selvvalgt streng, for eksempel et ordre-id. Hvis forespørgslen fejler på grund af en netværksfejl, og arbejdsgangen gentager den med SAMME header, returnerer volai det gemte svar fra det første forsøg i stedet for at sende beskeden en gang til - nøglen gælder i 24 timer.

bash
curl -X POST https://volai.cz/v1/messages \
  -H "Authorization: Bearer vk_DIN_NOGLE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ordre-4471" \
  -d '{"to": "+420777123456", "body": "Ordre nr. 4471 er modtaget."}'

Opbevar API-nøglen sikkert

En API-nøgle (vk_...) vejer lige så tungt som en adgangskode - den, der har den, kan sende beskeder og bruge kontoens saldo i kontoindehaverens navn. Skriv den derfor aldrig ind i kildekode, der committes til et repository. Den hører hjemme i en miljøvariabel (process.env.VOLAI_API_KEY i Node.js) eller i hostingudbyderens secrets-manager.

javascript
// .env.local (må aldrig committes til repositoriet)
// VOLAI_API_KEY=vk_din_nogle

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 }),
});

Endpointet kaldes fra en server, aldrig fra browseren - enhver kan læse nøglen i browserens udviklerværktøjer og begynde at sende beskeder fra kontoen. Hvis en nøgle nogensinde lækker (for eksempel ved et uheldigt commit), oprettes en ny i portalen, og den gamle tilbagekaldes - den gamle værdi holder op med at virke med det samme.

Test først på en sikker måde

Før SMS-afsendelsen kobles på en rigtig proces (for eksempel ordrebekræftelser), sendes de første par beskeder til et eget telefonnummer. Så ses præcis det, kunden ser - hvordan afsendernavnet SMSinfo tager sig ud, hvor lang tid leveringen tager, og om segmenteringen opfører sig som forventet. Først derefter skiftes to til en rigtig kundes nummer.

Hvad gør man, når API'et returnerer en fejl?

Et fejlsvar har altid formen {error: {code, message}}, med en HTTP-status, der svarer til fejlens art:

StatusKodeHvad det betyder
400invalid_numberIkke et gyldigt tjekkisk eller slovakisk telefonnummer.
400invalid_bodyBeskedteksten skal være på 1 til 765 tegn.
400unsupported_countryNummer uden for CZ/SK - vi sender ikke SMS dertil.
400recipient_cannot_receive_smsEt fastnetnummer - SMS'en kunne ikke nå frem. Faktureres ikke.
402insufficient_creditSaldoen dækker ikke prisen for alle segmenter, eller kontoen er ikke fyldt op endnu - SMS kræver en første optankning, velkomstsaldoen dækker dem ikke.
429rate_limitedMere end 1 SMS pr. 2 sekunder eller over 100 SMS pr. konto pr. dag.
502send_failedOperatøren afviste forespørgslen. Saldoen blev ikke belastet, forsøg igen.
502send_unknownVi afleverede beskeden til operatøren, men fik ingen leveringsbekræftelse - den faktureres, så send den ikke blindt igen.

Hvilke tilstande kan en besked have?

Feltet status, både i listen (GET /v1/messages) og på en enkelt besked, har fem mulige værdier: pending (gemt, sendes stadig), sent (operatøren bekræftede afsendelsen), failed (operatøren afviste den - faktureres ikke), unknown (der kom ingen leveringsbekræftelse, typisk en timeout hos operatøren - den faktureres stadig fuldt ud, fordi beskeden kan være nået frem) og received (kun for indgående beskeder, det vil sige med direction=in; afsendte beskeder har den aldrig).

Hver besked har også felterne direction (out for afsendte beskeder, in for modtagne), smsNumber (det nummer, beskeden blev sendt fra eller modtaget på; null for beskeder under det fælles afsendernavn SMSinfo) og deliveryStatus (delivered eller undelivered, en leveringsrapport fra operatøren; den findes kun for beskeder sendt fra et mobilnummer og er null for alle andre).

Hvordan sender man en række SMS?

Hastighedsgrænsen tillader højst én SMS hvert andet sekund pr. konto, op til 100 SMS om dagen. Når der sendes flere beskeder ad gangen (for eksempel aftalepåmindelser), er en kort pause mellem forespørgslerne enklere end at fange rate_limited og forsøge igen:

javascript
const message = "Påmindelse: aftalen er i morgen - Min App";

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

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

  // Grænsen er højst 1 SMS pr. 2 sekunder pr. konto - en kort pause
  // mellem kaldene er enklere end at fange rate_limited og forsøge igen.
  await new Promise((resolve) => setTimeout(resolve, 2100));
}

To slags numre modtager SMS

volais stemmenumre kan ikke modtage SMS, så et svar på en besked sendt under det fælles navn SMSinfo dukker ikke op nogen steder. For at modtage SMS og sende fra et eget nummer vælges tilkøbet mobilnummer (et tjekkisk nummer kun til SMS, uden opkald, 290 kr pr. 30 dage uden moms) eller et nummer til opkald og SMS (et fastnetnummer i Prag, 49 kr pr. 30 dage uden moms, som først trækkes efter aktivering). Modtagne SMS er inkluderet i begge. Modtagne beskeder læses via GET /v1/messages?direction=in, via MCP-værktøjet list_messages eller via webhooken message.received, og vi opbevarer dem i 90 dage; afsendelse fra et mobilnummer koster 1,90 kr pr. segment, fra et nummer til opkald og SMS 2,50 kr, og virker kun til tjekkiske numre. For at sende fra et nummer tilføjes feltet fromNumber til POST /v1/messages (nummeret hentes fra GET /v1/sms-numbers eller GET /v1/voice-sms-numbers). Den daglige grænse for afsendelse fra et nummer er 20 SMS pr. konto i de første 30 dage efter købet, derefter 50. En konto kan højst have to mobilnumre og kan først købe et efter den første optankning; det samme gælder numre til opkald og SMS. Vi garanterer ikke bekræftelseskoder fra tjenester (banker, Google og lignende) - numrene er beregnet til at skrive med mennesker, og modtagelsen er blevet verificeret i O2-nettet. Detaljerne står i afsnittene Mobilnumre og Numre til opkald og SMS i API-dokumentationen. Hvornår Twilio er det bedre valg til at modtage SMS, beskriver sammenligningen af volai og Twilio.

Kort sagt: ét kald til POST /v1/messages med to og body sender SMS'en, en Idempotency-Key-header beskytter en arbejdsgang, der forsøger igen, mod at sende den to gange, og prisen er pr. segment, afhængigt af om teksten indeholder tegn uden for GSM-7. Hold nøglen ude af repositoriet, og send den første besked til sig selv, før der sendes til andre.

Hvad er det næste

Den fulde REST API-reference (numre, opkald, webhooks, agenter) findes på /docs/api. Skal det være muligt at vide, i det øjeblik volai afleverer en besked til operatøren, uden at polle GET /v1/messages? Abonnér på hændelsen message.sent - den bekræfter kun, at operatøren har modtaget beskeden, ikke at den er nået frem til telefonen (det kan volai ikke se for beskeder under det fælles navn, se ovenfor; beskeder sendt fra et eget mobilnummer får en leveringsrapport) - se dokumentationen til webhooks. Og er der brug for et telefonnummer, der også tager imod opkald og ikke kun SMS, er AI receptionist et kig værd.

Prøv volai - de første 50 kr er på os

Du opretter en konto på et minut og får straks en startsaldo til opkald og nummer - SMS kan sendes efter den første opfyldning.

Prøv gratis