Automatické připomínky termínů přes API: naplánujte SMS 24 hodin předem a zrušte ji jedním voláním
Jak z rezervačního systému naplánovat SMS připomínku na konkrétní čas polem send_at (až 30 dní dopředu), dostat jobId, zrušit ji při odvolání termínu s vrácením kreditu a pojistit se Idempotency-Key. Kompletní curl, Node.js a PHP příklady.
Rezervační systém zná termín ve chvíli, kdy ho zákazník vytvoří. Nejjednodušší způsob, jak poslat připomínku 24 hodin předem, tedy není cron, který každou hodinu prochází databázi, ale naplánovat SMS hned při vytvoření rezervace a při jejím zrušení plán odvolat. Přesně to umí pole send_at v TopSMS API. Tento článek ukazuje celý postup včetně zrušení s vrácením kreditu, ochrany proti duplicitám a hotových příkladů.
Krátká odpověď
| Krok | Volání | Výsledek |
|---|---|---|
| Naplánovat připomínku | POST /api/sms/send s polem send_at | 202 se stavem scheduled a jobId |
| Zjistit stav | GET /api/sms/jobs/{jobId} | scheduled / queued / running / done / failed / cancelled |
| Zrušit při odvolání termínu | DELETE /api/sms/jobs/{jobId} | cancelled, cena se vrátí na kredit |
| Pojistit proti dvojímu odeslání | hlavička Idempotency-Key | opakovaný požadavek vrátí původní odpověď, SMS neodejde znovu |
send_at je ISO 8601 s časovou zónou (nebo Unix timestamp), nejvýš 30 dní dopředu. Kredit se strhne při naplánování a při zrušení se vrátí.
1. Naplánování SMS polem send_at
Do běžného odeslání přidáte jedno pole. Autentizace je Authorization: Bearer clientId:secret, klíč vytvoříte v dashboardu v sekci API přístup.
curl -X POST https://www.topsms.cz/api/sms/send \
-H "Authorization: Bearer CLIENT_ID:SECRET" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: rez-18342-24h" \
-d '{
"to": "+420600000000",
"text": "Dobry den, pripominame termin zitra 25. 10. v 9:30, Salon Iva, Brno. Zmena terminu: 541 123 456.",
"from": "SalonIva",
"send_at": "2026-10-24T09:30:00+02:00"
}'
Odpověď je 202 Accepted:
{
"ok": true,
"scheduled": true,
"id": "cmv...",
"jobId": "cmw...",
"status": "scheduled",
"sendAt": "2026-10-24T07:30:00.000Z",
"price": 0.98,
"statusUrl": "https://www.topsms.cz/api/sms/jobs/cmw..."
}
Pravidla pro send_at:
- Vždy s časovou zónou:
+02:00,+01:00neboZ. Bez zóny API vrátí400s kódeminvalid_send_at. Zónu nechte generovat knihovnu pro práci s časem, aby vám při přechodu letního a zimního času neodjela připomínka o hodinu jinam. - Nejvýš 30 dní dopředu. Pro rezervace dál v budoucnosti naplánujte SMS až ve chvíli, kdy se termín dostane do 30denního okna (např. nočním jobem).
- Čas do 1 minuty od teď se pošle hned, bez plánování. Čas o více než několik minut v minulosti vrátí
400. - Cena odpovídá vašemu tarifu a počtu segmentů (0,98 Kč za segment do ČR a SR v základním tarifu). Text bez diakritiky se vejde do 160 znaků, s diakritikou jen 70; připomínky proto pište bez háčků a čárek, nebo krátce.
Pole from přijme jen schválené Sender ID vašeho účtu nebo výchozí TopSMS. Dokud jméno není schválené, pole vynechte.
2. Zrušení připomínky s vrácením kreditu
Zákazník termín zrušil nebo přesunul. Uložený jobId pošlete na:
curl -X DELETE https://www.topsms.cz/api/sms/jobs/cmw... \
-H "Authorization: Bearer CLIENT_ID:SECRET"
Odpověď:
{ "ok": true, "id": "cmw...", "status": "cancelled", "cancelled": 1, "refunded": 0.98 }
Cena se vrátila na kredit. Dvě situace, se kterými musí váš kód počítat:
| Odpověď | Význam | Co udělat |
|---|---|---|
409 s code: "not_cancellable" | Zpráva se už odesílá nebo odešla. | Nic, připomínka už u zákazníka je. Zalogujte a pokračujte. |
404 | jobId neexistuje nebo nepatří k vašemu klíči. | Zkontrolujte uložené ID. |
Zrušení vyžaduje scope send, čtení stavu scope read. Přesun termínu řešte jako zrušení + nové naplánování s novým Idempotency-Key.
3. Idempotency-Key = ID rezervace
Když vašemu serveru vypadne spojení uprostřed požadavku, nevíte, zda SMS byla naplánována. Bez ochrany byste ji při opakování naplánovali dvakrát a zákazník by dostal dvě připomínky. Hlavička Idempotency-Key to řeší: opakovaný požadavek se stejným klíčem vrátí původní odpověď s hlavičkou Idempotent-Replayed: true a nic nového nevznikne.
Doporučený tvar klíče je ID rezervace plus označení připomínky, například rez-18342-24h a rez-18342-2h. Pravidla:
- 1 až 255 tisknutelných ASCII znaků bez mezer, platnost 24 hodin, vázaný na váš API klíč;
- stejný klíč s jiným tělem vrátí
422(idempotency_mismatch), proto po přesunu termínu použijte nový klíč, třeba s pořadovým číslem verze:rez-18342-24h-v2; - chyby
401,402,409,429a většina5xxse neukládají, takže po odstranění příčiny můžete stejný klíč použít znovu.
4. Vzor: připomínka 24 hodin a 2 hodiny předem
V praxi funguje nejlépe dvojice zpráv: den předem (zákazník si může termín ještě přesunout) a dvě hodiny předem (už nezapomene). Postup při vytvoření rezervace:
- Spočítejte
send_atpro oba časy z termínu rezervace. - Pošlete dva požadavky
POST /api/sms/send, každý s vlastnímIdempotency-Key. - Oba
jobIduložte k rezervaci. - Při zrušení zavolejte
DELETEna oba;409 not_cancellableberte jako „už odešlo". - Pokud je termín do 2 hodin od vytvoření, druhou zprávu vynechte; pokud je do 24 hodin, vynechte první.
Kdy připomínky posílat a jaké texty fungují, rozebírá článek SMS připomínky termínů.
Node.js
const BASE = 'https://www.topsms.cz/api/sms';
const AUTH = 'Bearer ' + process.env.TOPSMS_KEY; // "clientId:secret"
async function scheduleSms({ to, text, sendAt, key }) {
const res = await fetch(`${BASE}/send`, {
method: 'POST',
headers: { Authorization: AUTH, 'Content-Type': 'application/json', 'Idempotency-Key': key },
body: JSON.stringify({ to, text, send_at: sendAt.toISOString() }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
return data.jobId; // null, pokud byl cas do 1 minuty a SMS odesla hned
}
async function cancelSms(jobId) {
const res = await fetch(`${BASE}/jobs/${jobId}`, { method: 'DELETE', headers: { Authorization: AUTH } });
if (res.status === 409) return 'already_sent';
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return 'cancelled';
}
// pri vytvoreni rezervace
const start = new Date('2026-10-25T09:30:00+02:00');
const text = 'Dobry den, pripominame termin 25. 10. v 9:30, Salon Iva. Zmena: 541 123 456.';
const job24 = await scheduleSms({ to: '+420600000000', text, sendAt: new Date(start - 24 * 3600e3), key: 'rez-18342-24h' });
const job2 = await scheduleSms({ to: '+420600000000', text, sendAt: new Date(start - 2 * 3600e3), key: 'rez-18342-2h' });
// ulozte job24 a job2 k rezervaci; pri zruseni: await cancelSms(job24); await cancelSms(job2);
toISOString() vrací čas v UTC se sufixem Z, což API přijme; převod z místního času rezervace řeší Date sám.
PHP
<?php
function topsms(string $method, string $path, ?array $body = null, ?string $key = null): array {
$headers = ['Authorization: Bearer ' . getenv('TOPSMS_KEY'), 'Content-Type: application/json'];
if ($key) $headers[] = 'Idempotency-Key: ' . $key;
$ch = curl_init('https://www.topsms.cz/api/sms' . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
]);
$data = json_decode(curl_exec($ch), true) ?: [];
$data['httpStatus'] = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
return $data;
}
$start = new DateTimeImmutable('2026-10-25 09:30', new DateTimeZone('Europe/Prague'));
$text = 'Dobry den, pripominame termin 25. 10. v 9:30, Salon Iva. Zmena: 541 123 456.';
$r = topsms('POST', '/send', [
'to' => '+420600000000',
'text' => $text,
'send_at' => $start->modify('-24 hours')->format(DATE_ATOM), // vcetne +02:00
], 'rez-18342-24h');
$jobId = $r['jobId'] ?? null; // ulozit k rezervaci
// pri zruseni terminu
$c = topsms('DELETE', '/jobs/' . $jobId);
// $c['status'] === 'cancelled' nebo $c['httpStatus'] === 409 (uz odeslano)
DATE_ATOM vrací tvar 2026-10-24T09:30:00+02:00, tedy přesně to, co send_at očekává. Pro odesílání bez plánování můžete použít i knihovnu topsms/topsms-php, odkaz je v dokumentaci API.
5. Testování bez odeslání a bez útraty
Při vytváření API klíče zaškrtněte Testovací klíč. Nic neodešle ani neúčtuje, ale vrací stejné odpovědi jako ostrý klíč, včetně simulovaných doručenek na webhook. Jedna výjimka se týká právě plánování: testovací klíč s send_at vrátí 200 se "scheduled": true, ale bez jobId, takže zrušení si otestujete až s ostrým klíčem. Na to stačí 10 testovacích SMS, které dostanete po registraci; zrušená připomínka se navíc vrátí na kredit, takže test nic nestojí.
6. Doručenky k naplánovaným zprávám
Naplánovaná zpráva má po odeslání stejný životní cyklus jako běžná SMS: sent, delivered, případně failed s důvodem v errorCode. Pokud chcete v rezervačním systému vidět, že připomínka dorazila, nastavte u API klíče webhook URL. Při doručení či selhání pošleme HTTP POST s podpisem X-TopSMS-Signature (HMAC-SHA256 těla), 3 pokusy doručení. Podrobnosti najdete na stránce Webhooky, přehled stavů a chybových kódů v článku Proč SMS nedorazila.
Shrnutí pro vývojáře
send_atv ISO 8601 se zónou, max 30 dní, odpověď202+jobId, kredit se strhne hned.DELETE /api/sms/jobs/{jobId}zruší a vrátí kredit;409 not_cancellableznamená, že už odešlo.Idempotency-Key= ID rezervace + typ připomínky; po přesunu termínu nový klíč.- Rate limit je 60 požadavků za minutu na klíč (v dashboardu zvýšíte až na 1 000), což pro rezervační systém bohatě stačí.
- Pro hromadné naplánování (třeba všechny zítřejší termíny jedním voláním) funguje
send_ati vPOST /api/sms/send-bulkaž pro 500 příjemců.
Příklady textů připomínek pro ordinace, salony a restaurace najdete na stránce SMS připomínka rezervace pro lékaře a v přehledu SMS pro zdravotnictví.
Jak začít
- Registrace zdarma, trvá minutu, API klíč máte v dashboardu hned.
- 10 testovacích SMS a testovací klíč, který nic neúčtuje, na odladění celého postupu.
- Úplná dokumentace včetně OpenAPI: REST API.
Připomínky termínů a výsledků, méně nedostavení se pacientů. Od 0,88 Kč za SMS, bez paušálu, 10 testovacích SMS.