@sendly/node 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +329 -0
- package/dist/index.d.mts +1204 -0
- package/dist/index.d.ts +1204 -0
- package/dist/index.js +1181 -0
- package/dist/index.mjs +1121 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# @sendly/node
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for the [Sendly](https://sendly.live) SMS API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @sendly/node
|
|
9
|
+
# or
|
|
10
|
+
yarn add @sendly/node
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @sendly/node
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- Node.js 18.0.0 or higher
|
|
18
|
+
- A Sendly API key ([get one here](https://sendly.live/dashboard))
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import Sendly from '@sendly/node';
|
|
24
|
+
|
|
25
|
+
// Initialize with your API key
|
|
26
|
+
const sendly = new Sendly('sk_live_v1_your_api_key');
|
|
27
|
+
|
|
28
|
+
// Send an SMS
|
|
29
|
+
const message = await sendly.messages.send({
|
|
30
|
+
to: '+15551234567',
|
|
31
|
+
text: 'Hello from Sendly!'
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
console.log(`Message sent: ${message.id}`);
|
|
35
|
+
console.log(`Status: ${message.status}`);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Prerequisites for Live Messaging
|
|
39
|
+
|
|
40
|
+
Before sending live SMS messages, you need:
|
|
41
|
+
|
|
42
|
+
1. **Business Verification** - Complete verification in the [Sendly dashboard](https://sendly.live/dashboard)
|
|
43
|
+
- **International**: Instant approval (just provide Sender ID)
|
|
44
|
+
- **US/Canada**: Requires carrier approval (3-7 business days)
|
|
45
|
+
|
|
46
|
+
2. **Credits** - Add credits to your account
|
|
47
|
+
- Test keys (`sk_test_*`) work without credits (sandbox mode)
|
|
48
|
+
- Live keys (`sk_live_*`) require credits for each message
|
|
49
|
+
|
|
50
|
+
3. **Live API Key** - Generate after verification + credits
|
|
51
|
+
- Dashboard → API Keys → Create Live Key
|
|
52
|
+
|
|
53
|
+
### Test vs Live Keys
|
|
54
|
+
|
|
55
|
+
| Key Type | Prefix | Credits Required | Verification Required | Use Case |
|
|
56
|
+
|----------|--------|------------------|----------------------|----------|
|
|
57
|
+
| Test | `sk_test_v1_*` | No | No | Development, testing |
|
|
58
|
+
| Live | `sk_live_v1_*` | Yes | Yes | Production messaging |
|
|
59
|
+
|
|
60
|
+
> **Note**: You can start development immediately with a test key. Messages to sandbox test numbers are free and don't require verification.
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
|
|
64
|
+
- ✅ Full TypeScript support with exported types
|
|
65
|
+
- ✅ Automatic retries with exponential backoff
|
|
66
|
+
- ✅ Rate limit handling (respects `Retry-After`)
|
|
67
|
+
- ✅ Promise-based async/await API
|
|
68
|
+
- ✅ ESM and CommonJS support
|
|
69
|
+
- ✅ Zero runtime dependencies
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
### Sending Messages
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import Sendly from '@sendly/node';
|
|
77
|
+
|
|
78
|
+
const sendly = new Sendly('sk_live_v1_xxx');
|
|
79
|
+
|
|
80
|
+
// Basic usage
|
|
81
|
+
const message = await sendly.messages.send({
|
|
82
|
+
to: '+15551234567',
|
|
83
|
+
text: 'Your verification code is: 123456'
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// With custom sender ID (international)
|
|
87
|
+
const message = await sendly.messages.send({
|
|
88
|
+
to: '+447700900123',
|
|
89
|
+
text: 'Hello from MyApp!',
|
|
90
|
+
from: 'MYAPP'
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Listing Messages
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Get recent messages (default limit: 50)
|
|
98
|
+
const { data: messages, count } = await sendly.messages.list();
|
|
99
|
+
|
|
100
|
+
// Get last 10 messages
|
|
101
|
+
const { data: messages } = await sendly.messages.list({ limit: 10 });
|
|
102
|
+
|
|
103
|
+
// Iterate through messages
|
|
104
|
+
for (const msg of messages) {
|
|
105
|
+
console.log(`${msg.to}: ${msg.status}`);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Getting a Message
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const message = await sendly.messages.get('msg_xxx');
|
|
113
|
+
|
|
114
|
+
console.log(`Status: ${message.status}`);
|
|
115
|
+
console.log(`Delivered: ${message.deliveredAt}`);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Rate Limit Information
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// After any API call, you can check rate limit status
|
|
122
|
+
await sendly.messages.send({ to: '+1555...', text: 'Hello!' });
|
|
123
|
+
|
|
124
|
+
const rateLimit = sendly.getRateLimitInfo();
|
|
125
|
+
if (rateLimit) {
|
|
126
|
+
console.log(`${rateLimit.remaining}/${rateLimit.limit} requests remaining`);
|
|
127
|
+
console.log(`Resets in ${rateLimit.reset} seconds`);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Configuration
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import Sendly from '@sendly/node';
|
|
135
|
+
|
|
136
|
+
const sendly = new Sendly({
|
|
137
|
+
apiKey: 'sk_live_v1_xxx',
|
|
138
|
+
|
|
139
|
+
// Optional: Custom base URL (for testing)
|
|
140
|
+
baseUrl: 'https://sendly.live/api',
|
|
141
|
+
|
|
142
|
+
// Optional: Request timeout in ms (default: 30000)
|
|
143
|
+
timeout: 60000,
|
|
144
|
+
|
|
145
|
+
// Optional: Max retry attempts (default: 3)
|
|
146
|
+
maxRetries: 5
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Error Handling
|
|
151
|
+
|
|
152
|
+
The SDK provides typed error classes for different error scenarios:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import Sendly, {
|
|
156
|
+
SendlyError,
|
|
157
|
+
AuthenticationError,
|
|
158
|
+
RateLimitError,
|
|
159
|
+
InsufficientCreditsError,
|
|
160
|
+
ValidationError,
|
|
161
|
+
NotFoundError
|
|
162
|
+
} from '@sendly/node';
|
|
163
|
+
|
|
164
|
+
const sendly = new Sendly('sk_live_v1_xxx');
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
await sendly.messages.send({
|
|
168
|
+
to: '+15551234567',
|
|
169
|
+
text: 'Hello!'
|
|
170
|
+
});
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (error instanceof AuthenticationError) {
|
|
173
|
+
console.error('Invalid API key:', error.message);
|
|
174
|
+
} else if (error instanceof RateLimitError) {
|
|
175
|
+
console.error(`Rate limited. Retry after ${error.retryAfter} seconds`);
|
|
176
|
+
} else if (error instanceof InsufficientCreditsError) {
|
|
177
|
+
console.error(`Not enough credits. Need ${error.creditsNeeded}, have ${error.currentBalance}`);
|
|
178
|
+
} else if (error instanceof ValidationError) {
|
|
179
|
+
console.error('Invalid request:', error.message);
|
|
180
|
+
} else if (error instanceof NotFoundError) {
|
|
181
|
+
console.error('Resource not found:', error.message);
|
|
182
|
+
} else if (error instanceof SendlyError) {
|
|
183
|
+
console.error(`API error [${error.code}]:`, error.message);
|
|
184
|
+
} else {
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Testing (Sandbox Mode)
|
|
191
|
+
|
|
192
|
+
Use a test API key (`sk_test_v1_xxx`) to test without sending real messages:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import Sendly, { SANDBOX_TEST_NUMBERS } from '@sendly/node';
|
|
196
|
+
|
|
197
|
+
const sendly = new Sendly('sk_test_v1_xxx');
|
|
198
|
+
|
|
199
|
+
// Check if in test mode
|
|
200
|
+
console.log(sendly.isTestMode()); // true
|
|
201
|
+
|
|
202
|
+
// Use sandbox test numbers
|
|
203
|
+
await sendly.messages.send({
|
|
204
|
+
to: SANDBOX_TEST_NUMBERS.SUCCESS, // +15550001234 - Always succeeds
|
|
205
|
+
text: 'Test message'
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await sendly.messages.send({
|
|
209
|
+
to: SANDBOX_TEST_NUMBERS.INVALID, // +15550001001 - Returns invalid_number error
|
|
210
|
+
text: 'Test message'
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Available Test Numbers
|
|
215
|
+
|
|
216
|
+
| Number | Behavior |
|
|
217
|
+
|--------|----------|
|
|
218
|
+
| `+15550001234` | Instant success |
|
|
219
|
+
| `+15550001010` | Success after 10s delay |
|
|
220
|
+
| `+15550001001` | Fails: invalid_number |
|
|
221
|
+
| `+15550001002` | Fails: carrier_rejected (2s delay) |
|
|
222
|
+
| `+15550001003` | Fails: rate_limit_exceeded |
|
|
223
|
+
|
|
224
|
+
## Pricing Tiers
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { CREDITS_PER_SMS, SUPPORTED_COUNTRIES } from '@sendly/node';
|
|
228
|
+
|
|
229
|
+
// Credits per SMS by tier
|
|
230
|
+
console.log(CREDITS_PER_SMS.domestic); // 1 (US/Canada)
|
|
231
|
+
console.log(CREDITS_PER_SMS.tier1); // 8 (UK, Poland, India, etc.)
|
|
232
|
+
console.log(CREDITS_PER_SMS.tier2); // 12 (France, Japan, Australia, etc.)
|
|
233
|
+
console.log(CREDITS_PER_SMS.tier3); // 16 (Germany, Italy, Mexico, etc.)
|
|
234
|
+
|
|
235
|
+
// Supported countries by tier
|
|
236
|
+
console.log(SUPPORTED_COUNTRIES.domestic); // ['US', 'CA']
|
|
237
|
+
console.log(SUPPORTED_COUNTRIES.tier1); // ['GB', 'PL', 'IN', ...]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Utilities
|
|
241
|
+
|
|
242
|
+
The SDK exports validation utilities for advanced use cases:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import {
|
|
246
|
+
validatePhoneNumber,
|
|
247
|
+
getCountryFromPhone,
|
|
248
|
+
isCountrySupported,
|
|
249
|
+
calculateSegments
|
|
250
|
+
} from '@sendly/node';
|
|
251
|
+
|
|
252
|
+
// Validate phone number format
|
|
253
|
+
validatePhoneNumber('+15551234567'); // OK
|
|
254
|
+
validatePhoneNumber('555-1234'); // Throws ValidationError
|
|
255
|
+
|
|
256
|
+
// Get country from phone number
|
|
257
|
+
getCountryFromPhone('+447700900123'); // 'GB'
|
|
258
|
+
getCountryFromPhone('+15551234567'); // 'US'
|
|
259
|
+
|
|
260
|
+
// Check if country is supported
|
|
261
|
+
isCountrySupported('GB'); // true
|
|
262
|
+
isCountrySupported('XX'); // false
|
|
263
|
+
|
|
264
|
+
// Calculate SMS segments
|
|
265
|
+
calculateSegments('Hello!'); // 1
|
|
266
|
+
calculateSegments('A'.repeat(200)); // 2
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## TypeScript
|
|
270
|
+
|
|
271
|
+
The SDK is written in TypeScript and exports all types:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import type {
|
|
275
|
+
SendlyConfig,
|
|
276
|
+
SendMessageRequest,
|
|
277
|
+
Message,
|
|
278
|
+
MessageStatus,
|
|
279
|
+
ListMessagesOptions,
|
|
280
|
+
MessageListResponse,
|
|
281
|
+
RateLimitInfo,
|
|
282
|
+
PricingTier
|
|
283
|
+
} from '@sendly/node';
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## API Reference
|
|
287
|
+
|
|
288
|
+
### `Sendly`
|
|
289
|
+
|
|
290
|
+
#### Constructor
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
new Sendly(apiKey: string)
|
|
294
|
+
new Sendly(config: SendlyConfig)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
#### Properties
|
|
298
|
+
|
|
299
|
+
- `messages` - Messages resource
|
|
300
|
+
|
|
301
|
+
#### Methods
|
|
302
|
+
|
|
303
|
+
- `isTestMode()` - Returns `true` if using a test API key
|
|
304
|
+
- `getRateLimitInfo()` - Returns current rate limit info
|
|
305
|
+
- `getBaseUrl()` - Returns configured base URL
|
|
306
|
+
|
|
307
|
+
### `sendly.messages`
|
|
308
|
+
|
|
309
|
+
#### `send(request: SendMessageRequest): Promise<Message>`
|
|
310
|
+
|
|
311
|
+
Send an SMS message.
|
|
312
|
+
|
|
313
|
+
#### `list(options?: ListMessagesOptions): Promise<MessageListResponse>`
|
|
314
|
+
|
|
315
|
+
List sent messages.
|
|
316
|
+
|
|
317
|
+
#### `get(id: string): Promise<Message>`
|
|
318
|
+
|
|
319
|
+
Get a specific message by ID.
|
|
320
|
+
|
|
321
|
+
## Support
|
|
322
|
+
|
|
323
|
+
- 📚 [Documentation](https://sendly.live/docs)
|
|
324
|
+
- 💬 [Discord](https://discord.gg/sendly)
|
|
325
|
+
- 📧 [support@sendly.live](mailto:support@sendly.live)
|
|
326
|
+
|
|
327
|
+
## License
|
|
328
|
+
|
|
329
|
+
MIT
|