Appointment reminders reduce missed visits only when they reach the patient. A contact check before the reminder is due tells a clinic whether the number is a reachable mobile, which messaging apps it is registered on, and whether the e-mail mailbox exists. Only the number or address is sent to us, never health information.
Why do appointment reminders fail?
Clinics, dental practices, physiotherapists and vaccination centres send reminders to cut no-shows. Research on reminders is broad and results vary by setting, so this page makes no promise about rates. The failure modes are well known, though, and most of them are about the contact details rather than the message:
- Typos at registration. A digit swapped at the front desk, or a misspelled e-mail domain on a web form. The reminder goes nowhere and nobody notices until the empty slot.
- Landlines on file. Older patient records often hold a home number. An SMS reminder to a fixed line fails silently. The carrier lookup reports
line_type. - Numbers that were given up. People change numbers, and operators reassign old ones. A reminder to a reassigned number fails the patient and may reach a stranger.
- Phones that are off or out of coverage. For a same-day reminder, the HLR lookup answers whether the mobile line is reachable right now. See number reachability.
- The wrong channel. Some patients read messaging apps and ignore SMS. If they agreed to be contacted on an app, a WhatsApp or Viber check tells you whether the number has an account there.
How does the reminder workflow look?
- At registration or update, run a real-time lookup on the number and address the patient gave you, with checks such as
["carrier", "whatsapp", "email"]. Ask them to correct anything that comes back invalid while they are still at the desk or on the form. - Store only the answer:
line_type,registeredper channel andchecked_atin your practice management or reminder system. - Choose the reminder channel from the channels the patient agreed to, preferring those with
registered: true. Fall back to SMS for a mobile line, or to a phone call. - Before a same-day or next-day reminder, optionally run
hlron the number. If the phone is unreachable, try another agreed channel or call the patient. - Refresh in bulk. Once a month, run a bulk job over patients with upcoming appointments. iMessage and RCS checks are available in bulk jobs only.
- Act on failures. When a reminder fails, flag the record so staff confirm the contact details at the next visit.
Which checks fit reminders?
| Check | What it answers | When to run it | Link |
|---|---|---|---|
carrier | Is the number mobile, fixed line or VoIP? | At registration or when details change | Carrier lookup |
hlr | Is the mobile line reachable now? Is the number still assigned? | Before same-day reminders; monthly clean-up | HLR lookup |
whatsapp, viber, telegram | Is the number registered on this app? | At registration, if the patient agreed to app messages | WhatsApp, Viber |
email | Does the mailbox exist at a major webmail provider? | At registration or e-mail change | E-mail mailbox check |
imessage, rcs (bulk only) | Can the number receive iMessage or RCS? | Monthly bulk job | iMessage, RCS |
What should you do with each result?
| Result | Suggested action |
|---|---|
line_type: mobile | SMS reminders are possible |
line_type: fixed_line | Use a voice call or e-mail; ask for a mobile number at the next visit |
HLR status: reachable | Send as planned |
HLR status: unreachable | Try another agreed channel now; retry SMS later |
HLR status: invalid | The number isn't assigned; don't send, and confirm contact details with the patient |
App registered: true and the patient agreed to it | Eligible for app reminders |
Mailbox registered: false | Don't rely on e-mail; correct the address at the next contact |
Any check unknown | Send as usual; unknown is free and not evidence of a problem |
What does a request look like?
This test-mode request runs a live network status check on two numbers. +447700900001 answers reachable and +447700900002 answers unreachable.
curl https://api.mobilevalidate.com/v1/lookup \
-H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
-H "Content-Type: application/json" \
-d '{"numbers":["+447700900001","+447700900002"],"checks":["hlr"],"wait":10}'// npm install mobilevalidate · Node.js 20+ · save as check.mjs and run: node check.mjs
import { MobileValidate } from "mobilevalidate";
// Omit apiKey to read MOBILEVALIDATE_API_KEY from the environment.
const mv = new MobileValidate({ apiKey: "mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" });
const { data, error } = await mv.lookup({
numbers: ["+447700900001", "+447700900002"],
checks: ["hlr"],
wait: 10,
});
if (error) console.error(error.code, error.message);
else console.dir(data.results, { depth: null });// Node.js 18+, Deno, Bun or the browser console (ES module: save as .mjs or use "type": "module").
const res = await fetch("https://api.mobilevalidate.com/v1/lookup", {
method: "POST",
headers: {
Authorization: "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym",
"Content-Type": "application/json",
},
body: JSON.stringify({
numbers: ["+447700900001", "+447700900002"],
checks: ["hlr"],
wait: 10,
}),
});
console.log(res.status, res.headers.get("x-request-id"));
console.dir(await res.json(), { depth: null });# pip install mobilevalidate-sdk
from mobilevalidate import MobileValidate
# Omit api_key to read MOBILEVALIDATE_API_KEY from the environment.
mv = MobileValidate(api_key="mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
result = mv.lookup(numbers=["+447700900001", "+447700900002"], checks=["hlr"], wait=10)
print(result)<?php
$ch = curl_init('https://api.mobilevalidate.com/v1/lookup');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'numbers' => ['+447700900001', '+447700900002'],
'checks' => ['hlr'],
'wait' => 10,
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), PHP_EOL, $response, PHP_EOL;package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"numbers":["+447700900001","+447700900002"],"checks":["hlr"],"wait":10}`)
req, err := http.NewRequest("POST", "https://api.mobilevalidate.com/v1/lookup", body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}require "net/http"
require "json"
uri = URI("https://api.mobilevalidate.com/v1/lookup")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"
req["Content-Type"] = "application/json"
req.body = JSON.generate({
"numbers" => ["+447700900001", "+447700900002"],
"checks" => ["hlr"],
"wait" => 10
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyRuns as pasted with the public sandbox key, which answers the test values only. In the SDKs, omit the key to use MOBILEVALIDATE_API_KEY.
Response (excerpt, test mode: the checks of both items in results):
[
{
"number.hlr": {
"service": "number.hlr", "status": "completed", "registered": true,
"attributes": {"status": "reachable", "ported": false, "roaming": false, "network": "Test Mobile", "mcc_mnc": "23415", "country": "GB"},
"confidence": "high", "confidence_score": 0.99, "checked_at": "2026-09-29T07:30:11.402Z",
"cached": false, "age_seconds": 0, "billed": false, "reason": null, "poll_after_ms": null
}
},
{
"number.hlr": {
"service": "number.hlr", "status": "completed", "registered": true,
"attributes": {"status": "unreachable", "ported": false, "roaming": false, "network": "Test Mobile", "mcc_mnc": "23415", "country": "GB"},
"confidence": "high", "confidence_score": 0.99, "checked_at": "2026-09-29T07:30:11.402Z",
"cached": false, "age_seconds": 0, "billed": false, "reason": null, "poll_after_ms": null
}
}
]The first reminder goes out by SMS. For the second patient, try another channel they agreed to, or retry the SMS later in the day. Notice what the request doesn't contain: no name, no clinic, no appointment. Keep it that way, including in the optional metadata field.
How much does it cost?
You pay per check, and only for conclusive answers. Unknown results are free, as are invalid, duplicate and unsupported-country rows. The HLR lookup costs $0.005 per number; reachable, unreachable and invalid are conclusive and billed. Other checks have their own rates on the pricing page.
To keep the cost proportionate, run the cheap, stable checks once at registration, use bulk jobs for monthly refreshes (bulk checks cost less than real-time ones for most services), and reserve real-time HLR checks for reminders where timing matters. POST /v1/jobs/estimate shows the maximum cost of a job for free, and max_cost caps what any single request can spend.
What about privacy, consent and compliance?
Healthcare data carries extra obligations, so the design goal is simple: we never receive anything that says a person is a patient.
- Send only the identifier. The request needs a phone number or e-mail address and a list of checks. Don't put names, dates of birth, appointment details, clinic names or record numbers in the request, the
metadatafield or the webhook configuration. That follows the data-minimisation principle in GDPR Article 5 and the spirit of the HIPAA minimum necessary standard. - Health data is a special category. Under GDPR Article 9, data concerning health needs particular care. Because the check sees only the identifier, it doesn't process health data, but your reminder messages do. Keep their wording neutral on channels others might see.
- Consent per channel. Record which channels each patient agreed to. A registered messaging app is not permission to use it.
- Contracts. If you need a data processing agreement, see our DPA. Our trust page describes encryption, masking and retention.
This page describes a technical workflow and is not legal advice. Your privacy officer or counsel decides which channels and wording are appropriate.
What are the common mistakes?
- Putting appointment details in the request. The check never needs them. Keep the payload to the identifier.
- Blocking reminders on
unknown. Missing data is not a bad number. Send as usual. - Checking the whole patient list before every reminder run. Channel and line-type data is stable. Check once, refresh monthly, and use HLR only where timing matters.
- Switching channels without consent. A patient who agreed to SMS reminders shouldn't start getting app messages because an account exists.
- Forgetting landlines. Many long-standing patients still have one on file. Identify them once and call or e-mail instead.
Frequently asked questions
How do contact checks help reduce no-shows?
A reminder only helps if it arrives. Checking the patient's number and e-mail before the reminder is due lets you catch mistyped details, fixed lines that can't take SMS and unreachable phones, and send the reminder on a channel the patient agreed to and can receive.
Do you receive any health information?
No. The API only needs the phone number or e-mail address to check. Don't send names, appointment types, clinic names or any other information in the request or in the metadata field.
Is a phone number health data?
A phone number on its own is personal data, not health data. Combined with the fact that someone is a patient of a particular clinic it can become sensitive, which is why the request should carry nothing but the number or address.
Should I check before every reminder?
No. Check when the patient registers or updates their details, store the result with checked_at, and re-check if a reminder fails or the stored answer is old. For a same-day reminder, an HLR check tells you whether the phone is reachable now.
What if a check returns unknown?
Send the reminder as you normally would. Unknown means no conclusive answer, it is never charged, and it is not evidence that the contact details are wrong.
Can I send reminders on WhatsApp because the patient has an account?
Only if the patient agreed to be contacted on that channel and your reminder process meets the platform's business rules and your own privacy obligations. An account is not consent.
Is this HIPAA or GDPR advice?
No. This page describes a technical workflow. Your privacy officer or counsel decides which reminder channels and wording are appropriate for your organisation.


