Moorsyl Docs

TypeScript SDK

Official TypeScript and JavaScript client for the Moorsyl API.

The Moorsyl TypeScript SDK provides typed request/response models for all API endpoints and works in any environment with a global fetch — Node.js, Bun, Deno, browsers, and edge runtimes.

Installation

npm install @moorsyl/sdk

Initialization

import { Configuration, SMSApi, VerifyApi } from "@moorsyl/sdk";

const config = new Configuration({
  apiKey: "sk_live_...",
});

const sms = new SMSApi(config);
const verify = new VerifyApi(config);

The SDK defaults to https://api.moorsyl.com/api. No additional configuration is required for production use.

Send an SMS

Requires a secret key (sk_…). See API Keys.

import { Configuration, SMSApi } from "@moorsyl/sdk";

const config = new Configuration({ apiKey: "sk_live_..." });
const sms = new SMSApi(config);

const response = await sms.smsSend({
  smsSendRequest: {
    to: "+22236551999",
    from: "MyBrand",
    body: "Your order #1042 has been shipped.",
  },
});

console.log("Accepted:", response.accepted);
console.log("Message ID:", response.messageId);
console.log("Idempotency key:", response.idempotencyKey);

Supply idempotencyKey to prevent duplicate sends on retries:

await sms.smsSend({
  smsSendRequest: {
    to: "+22236551999",
    from: "MyBrand",
    body: "Your code is 847291",
    idempotencyKey: "otp-user42-20240101T120000",
  },
});

Phone Verification

Use a publishable key (pk_…) so this code can run safely in a browser or mobile app. See API Keys.

import { Configuration, VerifyApi } from "@moorsyl/sdk";

const config = new Configuration({ apiKey: "pk_live_..." });
const verify = new VerifyApi(config);

Send a verification code

const { verificationId } = await verify.verifySend({
  verifySendRequest: { to: "+22236551999" },
});

Check the code

const { status } = await verify.verifyCheck({
  verifyCheckRequest: { verificationId, code: userEnteredCode },
});

if (status === "approved") {
  // phone is verified — proceed
}

Full flow

Verify a phone number (TypeScript)
import { Configuration, VerifyApi } from "@moorsyl/sdk";

async function verifyPhoneNumber(phoneNumber: string, userCode: string) {
  const config = new Configuration({ apiKey: "pk_live_..." });
  const verify = new VerifyApi(config);

  // Step 1 — send the code
  const { verificationId } = await verify.verifySend({
    verifySendRequest: { to: phoneNumber },
  });

  // Step 2 — check the code the user entered
  const { status } = await verify.verifyCheck({
    verifyCheckRequest: { verificationId, code: userCode },
  });

  if (status === "approved") {
    console.log("Phone verified!");
  } else {
    console.log("Invalid or expired code.");
  }
}

Error handling

API methods throw ResponseError on non-2xx responses:

import { ResponseError } from "@moorsyl/sdk";

try {
  await sms.smsSend({
    smsSendRequest: { to: "+22236551999", from: "MyBrand", body: "Hello!" },
  });
} catch (e) {
  if (e instanceof ResponseError) {
    console.log("Status:", e.response.status);
    console.log("Body:", await e.response.json());
  }
}

Common status codes are documented on the SMS and Verify pages.

Webhook signature verification

Verify incoming webhook signatures to ensure requests are genuine. See Webhooks.

import { createHmac, timingSafeEqual } from "node:crypto";

function verifyWebhook(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(
    header.split(",").map((p) => p.split("=") as [string, string])
  );
  const timestamp = parts["t"];
  const signature = parts["v1"];

  if (!timestamp || !signature) return false;

  const age = Math.floor(Date.now() / 1000) - Number(timestamp);
  if (age > 300) return false;

  const keyBytes = Buffer.from(secret.replace("whsec_", ""), "base64");
  const expected = createHmac("sha256", keyBytes)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

Requirements

  • Node.js 18+ (or any environment with a global fetch)
  • TypeScript 4.x or 5.x (optional — plain JavaScript works too)

On this page