npm package: @smsgist/node
Description: Official Node.js SDK for the SMSGist messaging platform
Author: Zedeks
Latest version: 1.0.0

# @smsgist/node

Official Node.js SDK for the [SMSGist](https://smsgist.com) messaging platform.

- **TypeScript-first** — full type definitions included
- **Zero dependencies** — only Node.js built-ins (`crypto`, `https`)
- **Dual format** — works with both `require()` and `import`
- **Node >= 18** — uses built-in `crypto` for AES-256-CBC authentication

## Installation

```bash
npm install @smsgist/node
```

## Quick Start

```ts
import { SMSGist } from '@smsgist/node';

const client = new SMSGist({
  clientId: process.env.SMSGIST_CLIENT_ID,
  clientSecret: process.env.SMSGIST_CLIENT_SECRET,
  appKey: process.env.SMSGIST_APP_KEY,
});

// Send SMS
const res = await client.sms.send({
  senderId: 'MyApp',
  recipients: ['+233245972246'],
  message: 'Hello from SMSGist!',
});

console.log(res.messageId, res.status);
```

## Configuration

Create a client by passing config directly or using environment variables:

```ts
const client = new SMSGist({
  clientId: 'your-client-id',       // or SMSGIST_CLIENT_ID env var
  clientSecret: 'your-client-secret', // or SMSGIST_CLIENT_SECRET env var
  appKey: 'your-32-byte-app-key',    // or SMSGIST_APP_KEY env var (16, 24, or 32 bytes)
  baseUri: 'https://api.smsgist.com', // or SMSGIST_URI env var
  timeout: 30000,                     // HTTP timeout in ms (default: 30s)
  mode: 'live',                       // 'live' | 'test' | 'dryrun'
  testPhone: '+233200000000',         // redirect SMS in test/dryrun mode
  testEmail: 'test@example.com',      // redirect emails in test/dryrun mode
});
```

With environment variables only:

```ts
const client = new SMSGist(); // reads from SMSGIST_CLIENT_ID, SMSGIST_CLIENT_SECRET, SMSGIST_APP_KEY
```

## SMS

### Send SMS

```ts
const res = await client.sms.send({
  senderId: 'MyApp',
  recipients: ['+233245972246', '+233201234567'],
  message: 'Hello!',
  idempotencyKey: 'unique-key-123', // optional: prevent duplicate sends
});

console.log(res.messageId);      // "9b1deb4d-3b7d-..."
console.log(res.status);         // "queued"
console.log(res.recipientsCount); // 2
console.log(res.totalCost);      // 0.06
```

### Send OTP

```ts
const res = await client.sms.sendOtp({
  recipients: ['+233245972246'],
  message: 'Your verification code is 482916',
  senderId: 'MyApp',
});
```

## Email

```ts
const res = await client.email.send({
  from: 'My App',
  recipients: ['user@example.com'],
  subject: 'Welcome!',
  message: '<h1>Hello</h1><p>Welcome to our platform.</p>',
  replyTo: 'support@myapp.com',
  noTracking: true,
  attachments: [
    { filename: 'invoice.pdf', data: base64EncodedData },
  ],
});
```

## Message Operations

```ts
// Get delivery status
const status = await client.messages.getStatus('message-id');
console.log(status.delivered, status.pending, status.failed);

// Retry a failed message
const res = await client.messages.retry('message-id');
```

## Credit Balance

```ts
const balance = await client.credits.getBalance();
console.log(balance.available, balance.currency); // 150.50 "GHS"
```

## Fluent Builder API

An alternative chainable API that mirrors the Go and PHP SDKs:

```ts
// SMS
const res = await client.send('sms')
  .to('+233245972246')
  .from('MyApp')
  .message('Hello!')
  .idempotencyKey('msg-001')
  .exec();

// SMS with OTP priority
const res = await client.send('sms')
  .to('+233245972246')
  .from('MyApp')
  .messageOtp('Your code is 123456')
  .exec();

// Email
const res = await client.send('email')
  .to('user@example.com')
  .from('My App')
  .subject('Welcome')
  .message('<h1>Hello</h1>')
  .replyTo('support@app.com')
  .noTracking()
  .exec();
```

## Webhooks

Verify and parse incoming webhook payloads:

```ts
import { Webhook, Events } from '@smsgist/node';

// Verify signature (HMAC-SHA256 + 5-min replay protection)
const isValid = Webhook.verifySignature({
  body: rawBody,
  signature: req.headers['x-smsgist-signature'],
  timestamp: req.headers['x-smsgist-timestamp'],
  secret: 'your-webhook-secret',
});

if (isValid) {
  const event = Webhook.parse(rawBody);

  switch (event.event) {
    case Events.MESSAGE_DELIVERED:
      console.log(`Delivered to ${event.recipient}`);
      break;
    case Events.MESSAGE_FAILED:
      console.log(`Failed: ${event.status}`);
      break;
  }
}
```

### Express Example

```ts
import express from 'express';
import { Webhook, Events } from '@smsgist/node';

const app = express();
app.use(express.raw({ type: 'application/json' }));

app.post('/webhooks/smsgist', (req, res) => {
  const body = req.body.toString();

  const isValid = Webhook.verifySignature({
    body,
    signature: req.headers['x-smsgist-signature'] as string,
    timestamp: req.headers['x-smsgist-timestamp'] as string,
    secret: process.env.SMSGIST_WEBHOOK_SECRET!,
  });

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const event = Webhook.parse(body);
  console.log('Webhook:', event.event, event.messageId);

  res.status(200).send('OK');
});
```

## Error Handling

```ts
import { SMSGist, ApiError, ValidationError, AuthenticationError } from '@smsgist/node';

try {
  await client.sms.send({ recipients: [], message: '' });
} catch (err) {
  if (err instanceof ValidationError) {
    console.log('Validation:', err.message);
  } else if (err instanceof ApiError) {
    console.log('API error:', err.statusCode, err.responseBody);
  } else if (err instanceof AuthenticationError) {
    console.log('Auth error:', err.message);
  }
}
```

## Modes

```ts
import { SMSGist, Mode } from '@smsgist/node';

// Test mode — redirects all messages to testPhone/testEmail
const testClient = new SMSGist({
  ...config,
  mode: Mode.Test,
  testPhone: '+233200000000',
  testEmail: 'test@example.com',
});

// DryRun mode — logs messages, returns mock response
const dryClient = new SMSGist({
  ...config,
  mode: Mode.DryRun,
});
```

## CommonJS

```js
const { SMSGist } = require('@smsgist/node');

const client = new SMSGist({
  clientId: process.env.SMSGIST_CLIENT_ID,
  clientSecret: process.env.SMSGIST_CLIENT_SECRET,
  appKey: process.env.SMSGIST_APP_KEY,
});
```

## License

MIT