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

Validation and encoding

Check a message locally with sms.validate() before you pay for a send: E.164, sender, capabilities, encoding, and segments.

sms.validate() tells you whether a message would be refused and how many segments it uses, before you send. It is synchronous, free, and never contacts a provider.

Validate a message

import { sms } from "./sms";

const check = sms.validate({
  to: "+14155550123",
  body: "Your package is ready 📦",
});

console.log(check.supported); // true
console.log(check.encoding, check.segments); // "ucs2" 1
console.log(check.nonGsmCharacters); // ["📦"]
console.log(check.issues); // []

supported is true when the primary adapter can send the message and nothing is wrong with it. The emoji forces UCS-2, as encoding and nonGsmCharacters show.

Read the issues

Each problem is a ValidationIssue with a stable code, the field it concerns, a readable message, and provider when it applies to one adapter.

import { sms } from "./sms";

const check = sms.validate({
  to: "+14155550123",
  body: "Your appointment is tomorrow.",
  sendAt: new Date("2026-12-01T09:00:00Z"),
});

for (const issue of check.issues) {
  console.log(`${issue.provider ?? "message"}: ${issue.field} ${issue.code}: ${issue.message}`);
}

With an adapter that cannot schedule, this prints one unsupported_field issue for sendAt, and supported is false.

code Meaning
invalid_recipient to is not E.164.
invalid_sender from (or the adapter default) is malformed, such as a 12-character sender ID.
missing_sender No from on the message and no default on the adapter.
empty_body The body is empty and there is no media.
unsupported_field The adapter cannot send this field or sender type.
invalid_field A field is malformed or outside the adapter’s range: a relative URL, an invalid Date, a validityPeriodSec outside the provider’s range, a Twilio body over 1600 characters, more than 10 Twilio media URLs, or an idempotencyKey outside 1 to 255 characters.

Check every adapter

With several adapters, validate() checks each one. adapterCandidates lists the adapters that could send the message, in order. issues collects the problems from the rest.

import { createSmsClient } from "@opencoredev/sms-sdk";
import { telnyx } from "@opencoredev/sms-sdk/telnyx";
import { vonage } from "@opencoredev/sms-sdk/vonage";

const sms = createSmsClient({
  adapters: [
    vonage({ apiKey: process.env.VONAGE_API_KEY ?? "", apiSecret: process.env.VONAGE_API_SECRET ?? "", from: "+15550100001" }),
    telnyx({ apiKey: process.env.TELNYX_API_KEY ?? "", from: "+15550100002" }),
  ],
  fallback: "on-known-rejection",
});

const check = sms.validate({
  to: "+14155550123",
  body: "See the attached receipt.",
  mediaUrls: ["https://example.com/receipt.png"],
});

console.log(check.supported); // false: Vonage cannot send MMS
console.log(check.adapterCandidates); // ["telnyx"]

supported reflects only the primary adapter, because send() throws UnsupportedFieldError when the primary cannot send the message, even with fallback on.

What validate() cannot know

Validation checks format and declared capabilities. It cannot see whether your sender is provisioned or registered for 10DLC or toll-free, whether the destination country accepts your sender type, whether the recipient opted out with the provider, or your balance, trial status, and geographic permissions. The provider reports those as rejections when you send.

Encoding

Every result includes the estimateSegments() fields:

Field Meaning
encoding "gsm7" when every character is in the GSM-7 alphabet or its extension table, otherwise "ucs2".
units GSM-7 septets (extension characters such as €, [, { count 2) or UTF-16 code units.
segments Estimated segments. 0 for an empty body.
unitsPerSegment 160 or 153 for GSM-7, 70 or 67 for UCS-2.
remainingInSegment Units left before another segment starts.
containsUnicode true when at least one character forces UCS-2.
nonGsmCharacters The distinct characters that forced UCS-2, in order.

The validate() reference has the full result type.

SMS SDK never rewrites your text. One curly quote or emoji puts the whole message in UCS-2. For GSM-7, replace those characters yourself. Segments and billing covers the counting rules.

Next steps