sendr-js 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/LICENSE +22 -0
- package/README.md +356 -0
- package/dist/client.d.ts +62 -0
- package/dist/client.js +179 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +42 -0
- package/dist/types.d.ts +113 -0
- package/dist/types.js +16 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Sendr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# @sendr/sendr
|
|
2
|
+
|
|
3
|
+
Official Sendr email platform SDK for Node.js and TypeScript. Send transactional emails easily with a simple, type-safe API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @sendr/sendr
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Requirements:** Node.js 18.0.0 or higher (uses native `fetch` API)
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { SendrClient } from '@sendr/sendr';
|
|
17
|
+
|
|
18
|
+
// Initialize the client with your API key
|
|
19
|
+
const client = new SendrClient({
|
|
20
|
+
apiKey: 'your-api-key-here'
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Send an email
|
|
24
|
+
const response = await client.sendEmail({
|
|
25
|
+
recipients: [
|
|
26
|
+
{ email: 'user1@example.com' },
|
|
27
|
+
{ email: 'user2@example.com' }
|
|
28
|
+
],
|
|
29
|
+
emailConfig: {
|
|
30
|
+
subject: 'Welcome to Sendr',
|
|
31
|
+
from: 'noreply@yourdomain.com',
|
|
32
|
+
fromName: 'Your Company',
|
|
33
|
+
body: '<html><body><h1>Hello!</h1><p>Welcome to our platform.</p></body></html>'
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log('Email sent!', response.batchId);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## API Reference
|
|
41
|
+
|
|
42
|
+
### SendrClient
|
|
43
|
+
|
|
44
|
+
The main client class for interacting with the Sendr API.
|
|
45
|
+
|
|
46
|
+
#### Constructor
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
new SendrClient(options: SendrClientOptions)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Options:**
|
|
53
|
+
- `apiKey` (required): Your Sendr API key
|
|
54
|
+
- `baseUrl` (optional): Custom API base URL (defaults to `https://sending.sendrapp.org`)
|
|
55
|
+
- `statsBaseUrl` (optional): Custom stats API base URL (defaults to `https://stats.sendrapp.org`)
|
|
56
|
+
- `logsBaseUrl` (optional): Custom logs API base URL (defaults to `https://logs.sendrapp.org`)
|
|
57
|
+
|
|
58
|
+
#### Methods
|
|
59
|
+
|
|
60
|
+
##### `sendEmail(request: SendEmailRequest): Promise<SendEmailResponse>`
|
|
61
|
+
|
|
62
|
+
Send an email to one or more recipients.
|
|
63
|
+
|
|
64
|
+
**Parameters:**
|
|
65
|
+
- `request.recipients`: Array of recipient objects (max 100)
|
|
66
|
+
- `email`: Recipient email address (required)
|
|
67
|
+
- `name`: Recipient name (optional)
|
|
68
|
+
- `substitutions`: Template variable substitutions (optional)
|
|
69
|
+
- `request.emailConfig.subject`: Email subject line (required)
|
|
70
|
+
- `request.emailConfig.from`: Sender email address (must be verified domain) (required)
|
|
71
|
+
- `request.emailConfig.fromName`: Sender display name (optional)
|
|
72
|
+
- `request.emailConfig.body`: HTML email body (required)
|
|
73
|
+
- `request.emailConfig.headers`: Custom email headers (optional)
|
|
74
|
+
|
|
75
|
+
**Returns:** Promise resolving to `SendEmailResponse` with:
|
|
76
|
+
- `status`: Processing status
|
|
77
|
+
- `message`: Status message
|
|
78
|
+
- `limits`: Current usage limits
|
|
79
|
+
- `batchId`: Unique batch identifier
|
|
80
|
+
|
|
81
|
+
**Throws:** `SendrError` if the request fails
|
|
82
|
+
|
|
83
|
+
**Example:**
|
|
84
|
+
```typescript
|
|
85
|
+
const response = await client.sendEmail({
|
|
86
|
+
recipients: [
|
|
87
|
+
{ email: 'user@example.com' }
|
|
88
|
+
],
|
|
89
|
+
emailConfig: {
|
|
90
|
+
subject: 'Welcome!',
|
|
91
|
+
from: 'noreply@yourdomain.com',
|
|
92
|
+
fromName: 'Your Company',
|
|
93
|
+
body: '<html><body><h1>Welcome!</h1></body></html>'
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
##### `sendEmailTo(recipientEmail: string, emailConfig: EmailConfig): Promise<SendEmailResponse>`
|
|
99
|
+
|
|
100
|
+
Convenience method to send an email to a single recipient.
|
|
101
|
+
|
|
102
|
+
**Example:**
|
|
103
|
+
```typescript
|
|
104
|
+
const response = await client.sendEmailTo('user@example.com', {
|
|
105
|
+
subject: 'Welcome!',
|
|
106
|
+
from: 'noreply@yourdomain.com',
|
|
107
|
+
fromName: 'Your Company',
|
|
108
|
+
body: '<html><body><h1>Welcome!</h1></body></html>'
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
##### `getDomains(): Promise<DomainListResponse>`
|
|
113
|
+
|
|
114
|
+
Get a list of all domains associated with your tenant.
|
|
115
|
+
|
|
116
|
+
**Returns:** Promise resolving to `DomainListResponse` with:
|
|
117
|
+
- `domains`: Array of domain objects
|
|
118
|
+
- `count`: Total number of domains
|
|
119
|
+
|
|
120
|
+
**Example:**
|
|
121
|
+
```typescript
|
|
122
|
+
const response = await client.getDomains();
|
|
123
|
+
console.log(`You have ${response.count} domain(s)`);
|
|
124
|
+
response.domains.forEach(domain => {
|
|
125
|
+
console.log(`${domain.name} - Verified: ${domain.isVerified}`);
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
##### `verifyDomain(domainId: string): Promise<DomainVerifyResponse>`
|
|
130
|
+
|
|
131
|
+
Verify a domain's DNS settings.
|
|
132
|
+
|
|
133
|
+
**Parameters:**
|
|
134
|
+
- `domainId`: Domain ID to verify (required)
|
|
135
|
+
|
|
136
|
+
**Returns:** Promise resolving to `DomainVerifyResponse` with:
|
|
137
|
+
- `isVerified`: Whether the domain is verified
|
|
138
|
+
|
|
139
|
+
**Example:**
|
|
140
|
+
```typescript
|
|
141
|
+
const response = await client.verifyDomain('domain-id-here');
|
|
142
|
+
if (response.isVerified) {
|
|
143
|
+
console.log('Domain is verified!');
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
##### `createTenant(request: CreateTenantRequest): Promise<Tenant>`
|
|
148
|
+
|
|
149
|
+
Create a new tenant/workspace in the system.
|
|
150
|
+
|
|
151
|
+
**Parameters:**
|
|
152
|
+
- `request.name`: Workspace/tenant name (required)
|
|
153
|
+
- `request.domain`: Default domain name (required)
|
|
154
|
+
- `request.sendingEmail`: Default sending email address (required)
|
|
155
|
+
|
|
156
|
+
**Returns:** Promise resolving to `Tenant` object
|
|
157
|
+
|
|
158
|
+
**Example:**
|
|
159
|
+
```typescript
|
|
160
|
+
const tenant = await client.createTenant({
|
|
161
|
+
name: 'mycompany',
|
|
162
|
+
domain: 'mycompany.com',
|
|
163
|
+
sendingEmail: 'noreply@mycompany.com'
|
|
164
|
+
});
|
|
165
|
+
console.log('Tenant created:', tenant.id);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
##### `getStatistics(): Promise<TenantStatistics>`
|
|
169
|
+
|
|
170
|
+
Get tenant statistics including email delivery metrics.
|
|
171
|
+
|
|
172
|
+
**Returns:** Promise resolving to `TenantStatistics` with:
|
|
173
|
+
- `totalSent`: Total emails sent
|
|
174
|
+
- `totalDelivered`: Total emails delivered
|
|
175
|
+
- `totalBounced`: Total emails bounced
|
|
176
|
+
- `totalOpened`: Total emails opened
|
|
177
|
+
- `totalClicked`: Total emails clicked
|
|
178
|
+
|
|
179
|
+
**Example:**
|
|
180
|
+
```typescript
|
|
181
|
+
const stats = await client.getStatistics();
|
|
182
|
+
console.log(`Sent: ${stats.totalSent}, Delivered: ${stats.totalDelivered}`);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
##### `getLogs(options?: GetLogsOptions): Promise<EmailLogsResponse>`
|
|
186
|
+
|
|
187
|
+
Get email delivery logs for your domain.
|
|
188
|
+
|
|
189
|
+
**Parameters (optional):**
|
|
190
|
+
- `options.limit`: Maximum number of logs to return
|
|
191
|
+
- `options.offset`: Number of logs to skip
|
|
192
|
+
- `options.status`: Filter by status
|
|
193
|
+
- `options.startDate`: Start date for filtering
|
|
194
|
+
- `options.endDate`: End date for filtering
|
|
195
|
+
|
|
196
|
+
**Returns:** Promise resolving to `EmailLogsResponse` with:
|
|
197
|
+
- `logs`: Array of email log objects
|
|
198
|
+
- `count`: Total number of logs
|
|
199
|
+
|
|
200
|
+
**Example:**
|
|
201
|
+
```typescript
|
|
202
|
+
// Get all logs
|
|
203
|
+
const allLogs = await client.getLogs();
|
|
204
|
+
|
|
205
|
+
// Get logs with filters
|
|
206
|
+
const filteredLogs = await client.getLogs({
|
|
207
|
+
limit: 50,
|
|
208
|
+
status: 'delivered',
|
|
209
|
+
startDate: '2024-01-01',
|
|
210
|
+
endDate: '2024-01-31'
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Types
|
|
215
|
+
|
|
216
|
+
### Recipient
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
interface Recipient {
|
|
220
|
+
email: string;
|
|
221
|
+
name?: string;
|
|
222
|
+
substitutions?: Record<string, any>;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### EmailConfig
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
interface EmailConfig {
|
|
230
|
+
subject: string;
|
|
231
|
+
from: string;
|
|
232
|
+
fromName?: string;
|
|
233
|
+
body: string;
|
|
234
|
+
headers?: Record<string, string>;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### SendEmailResponse
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
interface SendEmailResponse {
|
|
242
|
+
status: string;
|
|
243
|
+
message: string;
|
|
244
|
+
limits: {
|
|
245
|
+
limit: number;
|
|
246
|
+
used: number;
|
|
247
|
+
remaining: number;
|
|
248
|
+
requested: number;
|
|
249
|
+
};
|
|
250
|
+
batchId?: string;
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Domain
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
interface Domain {
|
|
258
|
+
id: string;
|
|
259
|
+
createdAt: string;
|
|
260
|
+
updatedAt: string;
|
|
261
|
+
name: string;
|
|
262
|
+
description?: string;
|
|
263
|
+
sendingEmail: string;
|
|
264
|
+
dnsRecords: DNSRecord[];
|
|
265
|
+
isActive: boolean;
|
|
266
|
+
isApproved: boolean;
|
|
267
|
+
isVerified: boolean;
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Tenant
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
interface Tenant {
|
|
275
|
+
id: string;
|
|
276
|
+
name: string;
|
|
277
|
+
createdAt: string;
|
|
278
|
+
updatedAt: string;
|
|
279
|
+
domain: string;
|
|
280
|
+
sendingEmail: string;
|
|
281
|
+
currentEmailCount: number;
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### TenantStatistics
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
interface TenantStatistics {
|
|
289
|
+
totalSent: number;
|
|
290
|
+
totalDelivered: number;
|
|
291
|
+
totalBounced: number;
|
|
292
|
+
totalOpened: number;
|
|
293
|
+
totalClicked: number;
|
|
294
|
+
[key: string]: any;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### EmailLog
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
interface EmailLog {
|
|
302
|
+
id: string;
|
|
303
|
+
email: string;
|
|
304
|
+
subject: string;
|
|
305
|
+
status: string;
|
|
306
|
+
sentAt: string;
|
|
307
|
+
deliveredAt?: string;
|
|
308
|
+
openedAt?: string;
|
|
309
|
+
clickedAt?: string;
|
|
310
|
+
bouncedAt?: string;
|
|
311
|
+
[key: string]: any;
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Error Handling
|
|
316
|
+
|
|
317
|
+
The SDK throws `SendrError` for API errors:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
import { SendrError } from '@sendr/sendr';
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
await client.sendEmail({ ... });
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (error instanceof SendrError) {
|
|
326
|
+
console.error('API Error:', error.message);
|
|
327
|
+
console.error('Status Code:', error.statusCode);
|
|
328
|
+
console.error('Response:', error.response);
|
|
329
|
+
} else {
|
|
330
|
+
console.error('Unexpected error:', error);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Limits
|
|
336
|
+
|
|
337
|
+
- **Maximum recipients per API call**: 100 subscribers
|
|
338
|
+
- **Maximum sending rate**: 250 emails per hour (default, custom rates available)
|
|
339
|
+
- **Email content**: HTML format supported
|
|
340
|
+
- **From address**: Must be a verified domain email address
|
|
341
|
+
|
|
342
|
+
## Getting Your API Key
|
|
343
|
+
|
|
344
|
+
1. Sign up at [https://platform.sendr.app/auth/sign-up](https://platform.sendr.app/auth/sign-up)
|
|
345
|
+
2. Verify your email address
|
|
346
|
+
3. Complete domain verification (you'll receive DNS settings via email)
|
|
347
|
+
4. Your API key will be automatically generated and sent to your email after domain onboarding
|
|
348
|
+
|
|
349
|
+
## Documentation
|
|
350
|
+
|
|
351
|
+
For more information, visit the [Sendr API Documentation](https://docs.sendr.app/api-reference/introduction).
|
|
352
|
+
|
|
353
|
+
## License
|
|
354
|
+
|
|
355
|
+
MIT
|
|
356
|
+
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { SendEmailRequest, SendEmailResponse, SendrClientOptions, DomainListResponse, DomainVerifyResponse, CreateTenantRequest, Tenant, TenantStatistics, EmailLogsResponse, GetLogsOptions } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Sendr Email API Client
|
|
4
|
+
*/
|
|
5
|
+
export declare class SendrClient {
|
|
6
|
+
private apiKey;
|
|
7
|
+
private baseUrl;
|
|
8
|
+
private statsBaseUrl;
|
|
9
|
+
private logsBaseUrl;
|
|
10
|
+
constructor(options: SendrClientOptions);
|
|
11
|
+
/**
|
|
12
|
+
* Make an authenticated API request
|
|
13
|
+
*/
|
|
14
|
+
private request;
|
|
15
|
+
/**
|
|
16
|
+
* Send an email to one or more recipients
|
|
17
|
+
* @param request Email request with recipients and email configuration
|
|
18
|
+
* @returns Promise resolving to the send email response
|
|
19
|
+
* @throws SendrError if the request fails
|
|
20
|
+
*/
|
|
21
|
+
sendEmail(request: SendEmailRequest): Promise<SendEmailResponse>;
|
|
22
|
+
/**
|
|
23
|
+
* Send an email to a single recipient (convenience method)
|
|
24
|
+
* @param recipientEmail Email address of the recipient
|
|
25
|
+
* @param emailConfig Email configuration
|
|
26
|
+
* @returns Promise resolving to the send email response
|
|
27
|
+
*/
|
|
28
|
+
sendEmailTo(recipientEmail: string, emailConfig: SendEmailRequest["emailConfig"]): Promise<SendEmailResponse>;
|
|
29
|
+
/**
|
|
30
|
+
* Get a list of all domains associated with your tenant
|
|
31
|
+
* @returns Promise resolving to the domain list response
|
|
32
|
+
* @throws SendrError if the request fails
|
|
33
|
+
*/
|
|
34
|
+
getDomains(): Promise<DomainListResponse>;
|
|
35
|
+
/**
|
|
36
|
+
* Verify a domain's DNS settings
|
|
37
|
+
* @param domainId Domain ID to verify
|
|
38
|
+
* @returns Promise resolving to the verification response
|
|
39
|
+
* @throws SendrError if the request fails
|
|
40
|
+
*/
|
|
41
|
+
verifyDomain(domainId: string): Promise<DomainVerifyResponse>;
|
|
42
|
+
/**
|
|
43
|
+
* Create a new tenant/workspace
|
|
44
|
+
* @param request Tenant creation request
|
|
45
|
+
* @returns Promise resolving to the created tenant
|
|
46
|
+
* @throws SendrError if the request fails
|
|
47
|
+
*/
|
|
48
|
+
createTenant(request: CreateTenantRequest): Promise<Tenant>;
|
|
49
|
+
/**
|
|
50
|
+
* Get tenant statistics
|
|
51
|
+
* @returns Promise resolving to tenant statistics
|
|
52
|
+
* @throws SendrError if the request fails
|
|
53
|
+
*/
|
|
54
|
+
getStatistics(): Promise<TenantStatistics>;
|
|
55
|
+
/**
|
|
56
|
+
* Get email logs
|
|
57
|
+
* @param options Optional query parameters for filtering logs
|
|
58
|
+
* @returns Promise resolving to email logs response
|
|
59
|
+
* @throws SendrError if the request fails
|
|
60
|
+
*/
|
|
61
|
+
getLogs(options?: GetLogsOptions): Promise<EmailLogsResponse>;
|
|
62
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SendrClient = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
5
|
+
/**
|
|
6
|
+
* Sendr Email API Client
|
|
7
|
+
*/
|
|
8
|
+
class SendrClient {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
if (!options.apiKey) {
|
|
11
|
+
throw new Error("API key is required");
|
|
12
|
+
}
|
|
13
|
+
this.apiKey = options.apiKey;
|
|
14
|
+
this.baseUrl = options.baseUrl || "https://sending.sendrapp.org";
|
|
15
|
+
this.statsBaseUrl = options.statsBaseUrl || "https://stats.sendrapp.org";
|
|
16
|
+
this.logsBaseUrl = options.logsBaseUrl || "https://logs.sendrapp.org";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Make an authenticated API request
|
|
20
|
+
*/
|
|
21
|
+
async request(url, options = {}) {
|
|
22
|
+
try {
|
|
23
|
+
const response = await fetch(url, {
|
|
24
|
+
...options,
|
|
25
|
+
headers: {
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
Authorization: this.apiKey,
|
|
28
|
+
...options.headers,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
const data = (await response.json());
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new types_1.SendrError(data.message || `HTTP ${response.status}: ${response.statusText}`, response.status, data);
|
|
34
|
+
}
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error instanceof types_1.SendrError) {
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
if (error instanceof Error) {
|
|
42
|
+
throw new types_1.SendrError(`API request failed: ${error.message}`, undefined, error);
|
|
43
|
+
}
|
|
44
|
+
throw new types_1.SendrError("API request failed: Unknown error");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Send an email to one or more recipients
|
|
49
|
+
* @param request Email request with recipients and email configuration
|
|
50
|
+
* @returns Promise resolving to the send email response
|
|
51
|
+
* @throws SendrError if the request fails
|
|
52
|
+
*/
|
|
53
|
+
async sendEmail(request) {
|
|
54
|
+
// Validate recipients
|
|
55
|
+
if (!request.recipients || request.recipients.length === 0) {
|
|
56
|
+
throw new Error("At least one recipient is required");
|
|
57
|
+
}
|
|
58
|
+
if (request.recipients.length > 100) {
|
|
59
|
+
throw new Error("Maximum 100 recipients per API call. You provided " +
|
|
60
|
+
request.recipients.length);
|
|
61
|
+
}
|
|
62
|
+
// Validate email config
|
|
63
|
+
if (!request.emailConfig) {
|
|
64
|
+
throw new Error("emailConfig is required");
|
|
65
|
+
}
|
|
66
|
+
if (!request.emailConfig.subject) {
|
|
67
|
+
throw new Error("emailConfig.subject is required");
|
|
68
|
+
}
|
|
69
|
+
if (!request.emailConfig.from) {
|
|
70
|
+
throw new Error("emailConfig.from is required");
|
|
71
|
+
}
|
|
72
|
+
if (!request.emailConfig.body) {
|
|
73
|
+
throw new Error("emailConfig.body is required");
|
|
74
|
+
}
|
|
75
|
+
const url = `${this.baseUrl}/email`;
|
|
76
|
+
return this.request(url, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
body: JSON.stringify(request),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Send an email to a single recipient (convenience method)
|
|
83
|
+
* @param recipientEmail Email address of the recipient
|
|
84
|
+
* @param emailConfig Email configuration
|
|
85
|
+
* @returns Promise resolving to the send email response
|
|
86
|
+
*/
|
|
87
|
+
async sendEmailTo(recipientEmail, emailConfig) {
|
|
88
|
+
return this.sendEmail({
|
|
89
|
+
recipients: [{ email: recipientEmail }],
|
|
90
|
+
emailConfig,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get a list of all domains associated with your tenant
|
|
95
|
+
* @returns Promise resolving to the domain list response
|
|
96
|
+
* @throws SendrError if the request fails
|
|
97
|
+
*/
|
|
98
|
+
async getDomains() {
|
|
99
|
+
const url = `${this.baseUrl}/v2/domain`;
|
|
100
|
+
return this.request(url, {
|
|
101
|
+
method: "GET",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Verify a domain's DNS settings
|
|
106
|
+
* @param domainId Domain ID to verify
|
|
107
|
+
* @returns Promise resolving to the verification response
|
|
108
|
+
* @throws SendrError if the request fails
|
|
109
|
+
*/
|
|
110
|
+
async verifyDomain(domainId) {
|
|
111
|
+
if (!domainId) {
|
|
112
|
+
throw new Error("Domain ID is required");
|
|
113
|
+
}
|
|
114
|
+
const url = `${this.baseUrl}/v2/domain/${domainId}/verify`;
|
|
115
|
+
return this.request(url, {
|
|
116
|
+
method: "POST",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Create a new tenant/workspace
|
|
121
|
+
* @param request Tenant creation request
|
|
122
|
+
* @returns Promise resolving to the created tenant
|
|
123
|
+
* @throws SendrError if the request fails
|
|
124
|
+
*/
|
|
125
|
+
async createTenant(request) {
|
|
126
|
+
if (!request.name) {
|
|
127
|
+
throw new Error("Tenant name is required");
|
|
128
|
+
}
|
|
129
|
+
if (!request.domain) {
|
|
130
|
+
throw new Error("Domain is required");
|
|
131
|
+
}
|
|
132
|
+
if (!request.sendingEmail) {
|
|
133
|
+
throw new Error("Sending email is required");
|
|
134
|
+
}
|
|
135
|
+
const url = `${this.baseUrl}/v2/tenant`;
|
|
136
|
+
return this.request(url, {
|
|
137
|
+
method: "POST",
|
|
138
|
+
body: JSON.stringify(request),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get tenant statistics
|
|
143
|
+
* @returns Promise resolving to tenant statistics
|
|
144
|
+
* @throws SendrError if the request fails
|
|
145
|
+
*/
|
|
146
|
+
async getStatistics() {
|
|
147
|
+
const url = `${this.statsBaseUrl}/stats`;
|
|
148
|
+
return this.request(url, {
|
|
149
|
+
method: "GET",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get email logs
|
|
154
|
+
* @param options Optional query parameters for filtering logs
|
|
155
|
+
* @returns Promise resolving to email logs response
|
|
156
|
+
* @throws SendrError if the request fails
|
|
157
|
+
*/
|
|
158
|
+
async getLogs(options) {
|
|
159
|
+
const params = new URLSearchParams();
|
|
160
|
+
if (options) {
|
|
161
|
+
if (options.limit)
|
|
162
|
+
params.append("limit", options.limit.toString());
|
|
163
|
+
if (options.offset)
|
|
164
|
+
params.append("offset", options.offset.toString());
|
|
165
|
+
if (options.status)
|
|
166
|
+
params.append("status", options.status);
|
|
167
|
+
if (options.startDate)
|
|
168
|
+
params.append("startDate", options.startDate);
|
|
169
|
+
if (options.endDate)
|
|
170
|
+
params.append("endDate", options.endDate);
|
|
171
|
+
}
|
|
172
|
+
const queryString = params.toString();
|
|
173
|
+
const url = `${this.logsBaseUrl}/logs${queryString ? `?${queryString}` : ""}`;
|
|
174
|
+
return this.request(url, {
|
|
175
|
+
method: "GET",
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.SendrClient = SendrClient;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sendr Email Platform SDK
|
|
3
|
+
*
|
|
4
|
+
* Official SDK for sending emails through the Sendr platform
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { SendrClient } from '@sendr/sendr';
|
|
9
|
+
*
|
|
10
|
+
* const client = new SendrClient({ apiKey: 'your-api-key' });
|
|
11
|
+
*
|
|
12
|
+
* const response = await client.sendEmail({
|
|
13
|
+
* recipients: [{ email: 'recipient@example.com' }],
|
|
14
|
+
* emailConfig: {
|
|
15
|
+
* subject: 'Welcome!',
|
|
16
|
+
* from: 'noreply@yourdomain.com',
|
|
17
|
+
* fromName: 'Your Company',
|
|
18
|
+
* body: '<html><body><h1>Hello!</h1></body></html>'
|
|
19
|
+
* }
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export { SendrClient } from "./client";
|
|
24
|
+
export * from "./types";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Sendr Email Platform SDK
|
|
4
|
+
*
|
|
5
|
+
* Official SDK for sending emails through the Sendr platform
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { SendrClient } from '@sendr/sendr';
|
|
10
|
+
*
|
|
11
|
+
* const client = new SendrClient({ apiKey: 'your-api-key' });
|
|
12
|
+
*
|
|
13
|
+
* const response = await client.sendEmail({
|
|
14
|
+
* recipients: [{ email: 'recipient@example.com' }],
|
|
15
|
+
* emailConfig: {
|
|
16
|
+
* subject: 'Welcome!',
|
|
17
|
+
* from: 'noreply@yourdomain.com',
|
|
18
|
+
* fromName: 'Your Company',
|
|
19
|
+
* body: '<html><body><h1>Hello!</h1></body></html>'
|
|
20
|
+
* }
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
27
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
28
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
29
|
+
}
|
|
30
|
+
Object.defineProperty(o, k2, desc);
|
|
31
|
+
}) : (function(o, m, k, k2) {
|
|
32
|
+
if (k2 === undefined) k2 = k;
|
|
33
|
+
o[k2] = m[k];
|
|
34
|
+
}));
|
|
35
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
36
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.SendrClient = void 0;
|
|
40
|
+
var client_1 = require("./client");
|
|
41
|
+
Object.defineProperty(exports, "SendrClient", { enumerable: true, get: function () { return client_1.SendrClient; } });
|
|
42
|
+
__exportStar(require("./types"), exports);
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for Sendr Email API
|
|
3
|
+
*/
|
|
4
|
+
export interface Recipient {
|
|
5
|
+
email: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
substitutions?: Record<string, any>;
|
|
8
|
+
}
|
|
9
|
+
export interface EmailConfig {
|
|
10
|
+
subject: string;
|
|
11
|
+
from: string;
|
|
12
|
+
fromName?: string;
|
|
13
|
+
body: string;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
export interface SendEmailRequest {
|
|
17
|
+
recipients: Recipient[];
|
|
18
|
+
emailConfig: EmailConfig;
|
|
19
|
+
}
|
|
20
|
+
export interface EmailLimits {
|
|
21
|
+
limit: number;
|
|
22
|
+
used: number;
|
|
23
|
+
remaining: number;
|
|
24
|
+
requested: number;
|
|
25
|
+
}
|
|
26
|
+
export interface SendEmailResponse {
|
|
27
|
+
status: string;
|
|
28
|
+
message: string;
|
|
29
|
+
limits: EmailLimits;
|
|
30
|
+
batchId?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface DNSRecord {
|
|
33
|
+
type?: string;
|
|
34
|
+
name?: string;
|
|
35
|
+
value?: string;
|
|
36
|
+
ttl?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface Domain {
|
|
39
|
+
id: string;
|
|
40
|
+
createdAt: string;
|
|
41
|
+
updatedAt: string;
|
|
42
|
+
name: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
sendingEmail: string;
|
|
45
|
+
dnsRecords: DNSRecord[];
|
|
46
|
+
isActive: boolean;
|
|
47
|
+
isApproved: boolean;
|
|
48
|
+
isVerified: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface DomainListResponse {
|
|
51
|
+
domains: Domain[];
|
|
52
|
+
count: number;
|
|
53
|
+
}
|
|
54
|
+
export interface DomainVerifyResponse {
|
|
55
|
+
isVerified: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface CreateTenantRequest {
|
|
58
|
+
name: string;
|
|
59
|
+
domain: string;
|
|
60
|
+
sendingEmail: string;
|
|
61
|
+
}
|
|
62
|
+
export interface Tenant {
|
|
63
|
+
id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
createdAt: string;
|
|
66
|
+
updatedAt: string;
|
|
67
|
+
domain: string;
|
|
68
|
+
sendingEmail: string;
|
|
69
|
+
currentEmailCount: number;
|
|
70
|
+
}
|
|
71
|
+
export interface TenantStatistics {
|
|
72
|
+
totalSent: number;
|
|
73
|
+
totalDelivered: number;
|
|
74
|
+
totalBounced: number;
|
|
75
|
+
totalOpened: number;
|
|
76
|
+
totalClicked: number;
|
|
77
|
+
[key: string]: any;
|
|
78
|
+
}
|
|
79
|
+
export interface EmailLog {
|
|
80
|
+
id: string;
|
|
81
|
+
email: string;
|
|
82
|
+
subject: string;
|
|
83
|
+
status: string;
|
|
84
|
+
sentAt: string;
|
|
85
|
+
deliveredAt?: string;
|
|
86
|
+
openedAt?: string;
|
|
87
|
+
clickedAt?: string;
|
|
88
|
+
bouncedAt?: string;
|
|
89
|
+
[key: string]: any;
|
|
90
|
+
}
|
|
91
|
+
export interface EmailLogsResponse {
|
|
92
|
+
logs: EmailLog[];
|
|
93
|
+
count: number;
|
|
94
|
+
[key: string]: any;
|
|
95
|
+
}
|
|
96
|
+
export interface GetLogsOptions {
|
|
97
|
+
limit?: number;
|
|
98
|
+
offset?: number;
|
|
99
|
+
status?: string;
|
|
100
|
+
startDate?: string;
|
|
101
|
+
endDate?: string;
|
|
102
|
+
}
|
|
103
|
+
export interface SendrClientOptions {
|
|
104
|
+
apiKey: string;
|
|
105
|
+
baseUrl?: string;
|
|
106
|
+
statsBaseUrl?: string;
|
|
107
|
+
logsBaseUrl?: string;
|
|
108
|
+
}
|
|
109
|
+
export declare class SendrError extends Error {
|
|
110
|
+
statusCode?: number | undefined;
|
|
111
|
+
response?: any | undefined;
|
|
112
|
+
constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
|
|
113
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Types for Sendr Email API
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SendrError = void 0;
|
|
7
|
+
class SendrError extends Error {
|
|
8
|
+
constructor(message, statusCode, response) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
this.response = response;
|
|
12
|
+
this.name = "SendrError";
|
|
13
|
+
Object.setPrototypeOf(this, SendrError.prototype);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.SendrError = SendrError;
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sendr-js",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official Sendr email platform SDK for Node.js and TypeScript",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"prepublishOnly": "npm run build",
|
|
10
|
+
"test": "node test-local.js",
|
|
11
|
+
"test:link": "npm run build && npm link"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"sendr",
|
|
15
|
+
"email",
|
|
16
|
+
"email-api",
|
|
17
|
+
"email-sdk",
|
|
18
|
+
"transactional-email",
|
|
19
|
+
"email-service"
|
|
20
|
+
],
|
|
21
|
+
"author": "Sendr",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/maratgaliev/sendr.js.git"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"typescript": "^5.2.2"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|