JSPM

semaphoreco

1.0.3
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 12
  • Score
    100M100P100Q36223F
  • License MIT

TypeScript/JavaScript client for Semaphore.co - Philippines SMS Gateway API for sending SMS and OTP messages

Package Exports

  • semaphoreco

Readme

semaphoreco

npm version License: MIT

TypeScript/JavaScript client for the Semaphore.co SMS Gateway API (Philippines).

Note: This package was previously published as semaphore-client-js. If you're migrating, simply update your package name - the API is unchanged.

Installation

npm install semaphoreco

Or with yarn:

yarn add semaphoreco

Or with pnpm:

pnpm add semaphoreco

Quick Start

import { SemaphoreClient } from 'semaphoreco';

const client = new SemaphoreClient({ apiKey: 'your-api-key' });

// Send an SMS
const result = await client.messages.send({
  number: '639171234567',
  message: 'Hello from Semaphore!',
  sendername: 'SEMAPHORE',
});

console.log('Message ID:', result.message_id);

Configuration

Create a client instance with your API key:

import { SemaphoreClient } from 'semaphoreco';

const client = new SemaphoreClient({
  apiKey: 'your-api-key',      // Required (or set SEMAPHORE_API_KEY env var)
  baseUrl: 'https://api.semaphore.co/api/v4',  // Optional, this is the default
  timeout: 30000,              // Optional, request timeout in ms (default: 30000)
  onRetry: (attempt, delayMs) => {
    console.log(`Retry attempt ${attempt} after ${delayMs}ms`);
  },
});

Configuration Options

Option Type Default Description
apiKey string SEMAPHORE_API_KEY env var Your Semaphore API key
baseUrl string https://api.semaphore.co/api/v4 API base URL
timeout number 30000 Request timeout in milliseconds
onRetry function undefined Callback when rate-limited request is retried

Environment Variable

You can set your API key via environment variable instead of passing it to the constructor:

export SEMAPHORE_API_KEY=your-api-key
// API key will be read from SEMAPHORE_API_KEY
const client = new SemaphoreClient();

API Reference

Messages (client.messages)

send(params)

Send an SMS to one or more recipients.

// Send to a single recipient
const response = await client.messages.send({
  number: '639171234567',
  message: 'Hello!',
  sendername: 'SEMAPHORE',
});

console.log(response.message_id);  // 12345
console.log(response.status);      // 'Queued'

Bulk send - send to multiple recipients (up to 1000):

const responses = await client.messages.send({
  number: ['639171234567', '639181234567', '639191234567'],
  message: 'Hello everyone!',
  sendername: 'SEMAPHORE',
});

// Returns array of responses
for (const response of responses) {
  console.log(`Sent to ${response.recipient}: ${response.message_id}`);
}

Request Parameters:

Parameter Type Required Description
number string | string[] Yes Recipient number(s). Max 1000 for bulk
message string Yes Message content
sendername string Yes Approved sender name

Response:

interface SendMessageResponse {
  message_id: number;
  user_id: number;
  user: string;
  account_id: number;
  account: string;
  recipient: string;
  message: string;
  sender_name: string;
  network: string;
  status: string;
  type: string;
  source: string;
  created_at: string;
  updated_at: string;
}

list(params?)

List sent messages with optional filtering.

// List all messages
const messages = await client.messages.list();

// List with filters
const filtered = await client.messages.list({
  page: 1,
  limit: 50,
  status: 'Sent',
  startDate: '2024-01-01',
  endDate: '2024-01-31',
});

Filter Parameters:

Parameter Type Description
page number Page number for pagination
limit number Number of results per page
startDate string Filter by start date (YYYY-MM-DD)
endDate string Filter by end date (YYYY-MM-DD)
network string Filter by network
status string Filter by status
sendername string Filter by sender name

get(id)

Retrieve a specific message by ID.

const message = await client.messages.get(12345);

console.log(message.status);     // 'Sent'
console.log(message.recipient);  // '639171234567'
console.log(message.message);    // 'Hello!'

OTP (client.otp)

Send OTP messages via priority route with no rate limit. Each OTP message costs 2 credits per 160 characters.

send(params)

Send an OTP message. The API generates the OTP code and replaces the {otp} placeholder in your message.

const response = await client.otp.send({
  number: '639171234567',
  message: 'Your verification code is {otp}',
  sendername: 'MyApp',
});

console.log('OTP code:', response.code);        // '123456'
console.log('Message ID:', response.message_id);

With custom code length:

const response = await client.otp.send({
  number: '639171234567',
  message: 'Your code: {otp}',
  sendername: 'MyApp',
  code_length: 4,  // Generate 4-digit code instead of default 6
});

Request Parameters:

Parameter Type Required Description
number string Yes Recipient phone number
message string Yes Message template with {otp} placeholder
sendername string Yes Approved sender name
code_length number No OTP code length (default: 6)

Response:

interface SendOtpResponse {
  message_id: number;
  code: string;           // The generated OTP code
  user_id: number;
  user: string;
  account_id: number;
  account: string;
  recipient: string;
  message: string;
  sender_name: string;
  network: string;
  status: string;
  type: string;
  source: string;
  created_at: string;
  updated_at: string;
}

Account (client.account)

info()

Get account information including credit balance.

const info = await client.account.info();

console.log('Account:', info.account_name);
console.log('Status:', info.status);
console.log('Balance:', info.credit_balance);

Response:

interface AccountInfo {
  account_id: number;
  account_name: string;
  status: string;
  credit_balance: string;
}

transactions(params?)

Get transaction history with optional filtering.

// Get all transactions
const transactions = await client.account.transactions();

// Get with pagination and date range
const filtered = await client.account.transactions({
  page: 1,
  limit: 50,
  startDate: '2024-01-01',
  endDate: '2024-01-31',
});

for (const tx of filtered) {
  console.log(`${tx.type}: ${tx.amount} credits - ${tx.description}`);
}

Filter Parameters:

Parameter Type Description
page number Page number for pagination
limit number Number of results per page
startDate string Filter by start date (YYYY-MM-DD)
endDate string Filter by end date (YYYY-MM-DD)

Response:

interface Transaction {
  id: number;
  account_id: number;
  user_id: number;
  type: string;
  amount: string;
  balance_before: string;
  balance_after: string;
  description: string;
  created_at: string;
}

senderNames()

Get list of approved sender names.

const senderNames = await client.account.senderNames();

for (const sender of senderNames) {
  console.log(`${sender.name}: ${sender.status}`);
}

Response:

interface SenderName {
  name: string;
  status: string;
  created_at: string;
}

Error Handling

The library throws typed errors for different failure scenarios:

import {
  SemaphoreClient,
  SemaphoreError,
  AuthenticationError,
  RateLimitError,
  ValidationError,
  NetworkError,
  TimeoutError,
} from 'semaphoreco';

const client = new SemaphoreClient({ apiKey: 'your-api-key' });

try {
  await client.messages.send({
    number: '639171234567',
    message: 'Hello!',
    sendername: 'SEMAPHORE',
  });
} catch (error) {
  if (error instanceof RateLimitError) {
    // Rate limited (429) - retry after delay
    console.log('Rate limited, retry after:', error.retryAfter, 'seconds');
  } else if (error instanceof AuthenticationError) {
    // Invalid API key (401)
    console.log('Invalid API key');
  } else if (error instanceof ValidationError) {
    // Invalid request parameters (400)
    console.log('Validation error:', error.message);
    console.log('Field:', error.field);
  } else if (error instanceof TimeoutError) {
    // Request timed out
    console.log('Request timed out after:', error.timeoutMs, 'ms');
  } else if (error instanceof NetworkError) {
    // Network connection failed
    console.log('Network error:', error.message);
    console.log('Cause:', error.cause);
  } else if (error instanceof SemaphoreError) {
    // Other API errors (5xx, etc.)
    console.log('API error:', error.message);
    console.log('Status:', error.statusCode);
  }
}

Error Types

Error Status Code Description
AuthenticationError 401 Invalid or missing API key
RateLimitError 429 Rate limit exceeded (120 req/min for standard SMS)
ValidationError 400 Invalid request parameters
NetworkError - Connection failure, DNS error
TimeoutError - Request exceeded timeout
SemaphoreError 5xx Server errors and other failures

Automatic Retry

The client automatically retries rate-limited requests (429 responses) with exponential backoff. You can track retries with the onRetry callback:

const client = new SemaphoreClient({
  apiKey: 'your-api-key',
  onRetry: (attempt, delayMs) => {
    console.log(`Rate limited. Retry attempt ${attempt} in ${delayMs}ms`);
  },
});

SMS Credits

  • Standard SMS: 1 credit per 160 ASCII characters
  • OTP SMS: 2 credits per 160 ASCII characters (priority route)
  • Long messages: Split into 153-character segments
  • Unicode: Limited to 70 characters per segment

Requirements

  • Node.js 18.0.0 or later (uses native fetch)
  • Also works with Deno, Bun, and other modern JavaScript runtimes

License

MIT