Skip to content
SMS SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

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 input send() 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.