validate() and encoding
sms.validate(input), the segment and encoding helpers (estimateSegments, isGsm7, SEGMENT_LIMITS), and the phone number and redaction utilities.
sms.validate(input)
Checks a message locally and estimates its segments. It is synchronous and free, and it never contacts a provider or claims that an account or sender is approved.
import { sms } from "./sms";
const check = sms.validate({ to: "+14155550123", body: "Your package is ready 📦" });
if (!check.supported) {
for (const issue of check.issues) console.warn(issue.code, issue.field, issue.message);
}
console.log(check.encoding, check.segments, check.adapterCandidates);
Guide: Message length and cost.
Parameters
input: SmsSendInput, the same inputsend()takes. See send().
Returns: SmsValidationResult
SegmentPreview plus four fields:
| Field | Type | Meaning |
|---|---|---|
supported |
boolean |
true when the primary adapter can send the message and the message has no issues. |
issues |
readonly ValidationIssue[] |
Every problem found, across the message and every adapter. |
adapterCandidates |
readonly string[] |
Adapters that could send it, in configured order. Empty when the message itself is invalid. |
providerOptionsFor |
readonly string[] |
The candidates in adapterCandidates that have an entry in providerOptions, in configured order. Each of these sends its own entry if it ends up sending. Empty when the message itself is invalid or no candidate has an entry. |
encoding |
"gsm7" | "ucs2" |
|
segments |
number |
Estimated segments, 0 for an empty body. |
units |
number |
Septets (GSM-7) or UTF-16 code units (UCS-2). |
unitsPerSegment |
number |
160 or 153 (GSM-7), 70 or 67 (UCS-2). |
remainingInSegment |
number |
Units left before another segment starts. |
containsUnicode |
boolean |
At least one character forces UCS-2. |
nonGsmCharacters |
readonly string[] |
Distinct characters that forced UCS-2. |
supported matches what send() does before any request. When the primary adapter makes supported false, send() throws even with fallback on.
Message-level issues come first: recipient, body, field formats, and providerOptions checked against every configured adapter. When there are any, adapters are not checked, and adapterCandidates and providerOptionsFor are empty.
ValidationIssue
type ValidationIssue = {
readonly code: ValidationIssueCode;
readonly field: ValidationField;
readonly message: string;
readonly provider?: string; // set when the issue applies to one adapter
};
code |
send() throws |
Meaning |
|---|---|---|
invalid_recipient |
InvalidRecipientError |
to is not E.164. |
invalid_sender |
InvalidSenderError |
The sender is malformed, such as a sender ID over 11 characters, a short code that is not 3 to 8 digits, a Twilio Messaging Service SID that is not MG plus 32 hex, or a Telnyx alphanumeric sender without messagingProfileId. |
missing_sender |
InvalidSenderError |
No from and no adapter default. |
empty_body |
InvalidMessageError |
Empty body without media, or any empty body on Vonage. |
unsupported_field |
UnsupportedFieldError |
The adapter cannot send this field or sender type, Twilio sendAt without a Messaging Service sender, or a providerOptions key the adapter does not accept or that names a parameter the SDK sets. |
invalid_field |
InvalidMessageError |
A malformed field, a value outside the adapter’s range, or a providerOptions value of the wrong type. |
ValidationField is "to" | "from" | "body" | "mediaUrls" | "sendAt" | "validityPeriodSec" | "webhookUrl" | "idempotencyKey" | "providerOptions".
What it does not check
- Whether the sender is provisioned or registered in your account.
- Whether the destination accepts your sender type or MMS.
- Recipient opt-outs held by the provider, account balance, or permissions.
The provider reports these as rejections at send time.
Encoding and utility helpers
Synchronous helpers with no network access. The encoding functions live in @opencoredev/sms-sdk/encoding; estimateSegments and isGsm7 are also exported from the root.
estimateSegments(body)
Estimates the encoding and segment count for a message body.
import { estimateSegments } from "@opencoredev/sms-sdk/encoding";
const preview = estimateSegments("Café opens at 9 € 5");
console.log(preview.encoding, preview.units, preview.segments); // "gsm7" 20 1
é is in the GSM-7 basic table and € is in the extension table, so it counts 2 septets.
Returns: SegmentPreview
| Field | Type | Meaning |
|---|---|---|
encoding |
"gsm7" | "ucs2" |
GSM-7 when every character is in the basic or extension table. |
segments |
number |
Estimated segments. 0 for an empty body. |
units |
number |
Septets (extension characters count 2) or UTF-16 code units. |
unitsPerSegment |
number |
160 or 153 for GSM-7, 70 or 67 for UCS-2. |
remainingInSegment |
number |
Units left before another segment is needed. |
containsUnicode |
boolean |
At least one character forced UCS-2. |
nonGsmCharacters |
readonly string[] |
Distinct characters that forced UCS-2, in order of first appearance. |
Extension characters and surrogate pairs never split across segments, and nothing is transliterated. The result is an estimate, not a price. See Message length and cost.
isGsm7(text)
Returns true when every character is in the GSM-7 basic or extension table.
GSM7_BASIC_CHARACTERS and GSM7_EXTENSION_CHARACTERS
Strings holding the GSM 03.38 basic alphabet, one septet each, and the extension table, two septets each. The extension table is form feed, ^, {, }, \, [, ~, ], |, and €.
SEGMENT_LIMITS
{ gsm7: { single: 160, concatenated: 153 }, ucs2: { single: 70, concatenated: 67 } }
Exported from /encoding only.
isE164(value)
import { isE164 } from "@opencoredev/sms-sdk";
console.log(isE164("+14155550123"), isE164("+1 415 555 0123"), isE164("0044207946000")); // true false false
A type guard (value is E164) for ^\+[1-9]\d{1,14}$. It checks format only, not whether the number exists or receives SMS. It accepts unknown, so you can pass process.env values directly.
maskPhoneNumber(value)
Masks a number for logs, keeping the +, the first digit, and the last two digits: +14155550123 becomes +1********23. Short strings are fully masked.
redactText(text)
Makes provider text safe to log. It masks digit runs that look like phone numbers, removes control characters, and truncates to 200 characters. Use it in custom adapters for details.message.
import { maskPhoneNumber, redactText } from "@opencoredev/sms-sdk";
console.log(maskPhoneNumber("+14155550123")); // "+1********23"
console.log(redactText("The 'To' number +14155550123 is not valid")); // "The 'To' number +1********23 is not valid"
resolveSender(from) and supportsSender(capabilities, sender)
resolveSender validates an SmsFrom and returns { kind: "ok", sender: SmsSender }, { kind: "missing" }, or { kind: "invalid", message }. supportsSender returns whether an adapter’s capabilities.senderTypes allow that sender. The client uses both. They are exported for adapter authors.
isSmsError(value) and isFallbackEligible(category)
isSmsError is a type guard for any SDK error. isFallbackEligible returns true for auth, rate_limited, sender, and account. See Errors.
Types
SmsValidationResult, ValidationIssue, ValidationIssueCode, ValidationField, and SegmentPreview are exported as types from @opencoredev/sms-sdk.
E164, SegmentPreview, SmsEncoding, and SenderResolution are exported as types from @opencoredev/sms-sdk.