@masters-union/outbound-sdk 0.1.4 → 0.1.6
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 +113 -118
- package/dist/index.d.mts +59 -9
- package/dist/index.d.ts +59 -9
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,183 +2,178 @@
|
|
|
2
2
|
|
|
3
3
|
Official Node.js SDK for the [Outbound](https://github.com/AdarshChakrworty/outbound) email platform.
|
|
4
4
|
|
|
5
|
-
## Install
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
::: code-group
|
|
9
|
+
```bash [npm]
|
|
10
|
+
npm install @masters-union/outbound-sdk
|
|
11
|
+
```
|
|
12
|
+
```bash [yarn]
|
|
13
|
+
yarn add @masters-union/outbound-sdk
|
|
14
|
+
```
|
|
15
|
+
```bash [pnpm]
|
|
16
|
+
pnpm add @masters-union/outbound-sdk
|
|
9
17
|
```
|
|
18
|
+
:::
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
**Requirements:** Node.js 18+ (uses native `fetch`)
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
import { Outbound } from 'outbound-sdk';
|
|
22
|
+
## Quick Setup
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
const outbound = new Outbound({ apiKey: 'mu_outbound_...' });
|
|
24
|
+
### 1. Get your API key
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
toEmail: 'user@example.com',
|
|
21
|
-
fromEmail: 'noreply@company.com',
|
|
22
|
-
emailSubject: 'Welcome!',
|
|
23
|
-
htmlBody: '<h1>Hello World</h1>',
|
|
24
|
-
});
|
|
25
|
-
```
|
|
26
|
+
Your Outbound admin will provide you with an API key. It looks like:
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
```
|
|
29
|
+
mu_outbound_a1b2c3d4e5f6...
|
|
30
|
+
```
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
### 2. Initialize the client
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
::: code-group
|
|
35
|
+
```ts [ESM]
|
|
36
|
+
import { Outbound } from '@masters-union/outbound-sdk';
|
|
33
37
|
|
|
34
|
-
const
|
|
35
|
-
|
|
38
|
+
const outbound = new Outbound({ apiKey: 'mu_outbound_...' });
|
|
39
|
+
```
|
|
40
|
+
```js [CommonJS]
|
|
41
|
+
const { Outbound } = require('@masters-union/outbound-sdk');
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
await outbound.email.send({ toEmail: '...', ... }, { apiKey: tenantBKey });
|
|
43
|
+
const outbound = new Outbound({ apiKey: 'mu_outbound_...' });
|
|
39
44
|
```
|
|
45
|
+
:::
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
Once you set the API key in the constructor, every method uses it automatically. No need to pass it on every call.
|
|
42
48
|
|
|
43
|
-
### Send
|
|
49
|
+
### 3. Send your first email
|
|
44
50
|
|
|
45
51
|
```ts
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
{ toEmail: 'bob@example.com', htmlBody: '<h1>Hi Bob</h1>' },
|
|
52
|
-
],
|
|
52
|
+
const { jobId, messageId } = await outbound.email.send({
|
|
53
|
+
toEmail: 'user@example.com',
|
|
54
|
+
fromEmail: 'noreply@yourcompany.com', // must be a verified domain
|
|
55
|
+
emailSubject: 'Welcome!',
|
|
56
|
+
htmlBody: '<h1>Hello World</h1>',
|
|
53
57
|
});
|
|
54
|
-
|
|
58
|
+
|
|
59
|
+
console.log(`Email queued: ${jobId}`);
|
|
55
60
|
```
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
::: warning Verified Domains Only
|
|
63
|
+
The `fromEmail` must use a domain that has been verified and assigned to your tenant account by the admin. Sending from an unverified domain will return a `403 Forbidden` error.
|
|
64
|
+
:::
|
|
65
|
+
|
|
66
|
+
### 4. Check delivery status
|
|
58
67
|
|
|
59
68
|
```ts
|
|
60
|
-
const status = await outbound.email.status(
|
|
61
|
-
|
|
69
|
+
const status = await outbound.email.status(jobId);
|
|
70
|
+
|
|
71
|
+
for (const recipient of status.recipients) {
|
|
72
|
+
console.log(`${recipient.recipient_email}: ${recipient.status}`);
|
|
73
|
+
// "sent" → "delivered" → "opened" → "clicked"
|
|
74
|
+
}
|
|
62
75
|
```
|
|
63
76
|
|
|
64
|
-
###
|
|
77
|
+
### 5. Send with a template
|
|
65
78
|
|
|
66
79
|
```ts
|
|
67
|
-
// Create
|
|
80
|
+
// Create a reusable template
|
|
68
81
|
const { template } = await outbound.templates.create({
|
|
69
|
-
name: 'welcome',
|
|
82
|
+
name: 'welcome-email',
|
|
70
83
|
subject: 'Welcome {{firstName}}!',
|
|
71
|
-
htmlBody: '<h1>Hello {{firstName}}</h1>',
|
|
72
|
-
variables: ['firstName'],
|
|
84
|
+
htmlBody: '<h1>Hello {{firstName}}</h1><p>Welcome to {{company}}.</p>',
|
|
85
|
+
variables: ['firstName', 'company'],
|
|
73
86
|
});
|
|
74
87
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Send using template
|
|
79
|
-
const { jobId } = await outbound.templates.send({
|
|
88
|
+
// Send to one person
|
|
89
|
+
await outbound.templates.send({
|
|
80
90
|
templateId: template.id,
|
|
81
91
|
toEmail: 'user@example.com',
|
|
82
|
-
fromEmail: 'noreply@
|
|
83
|
-
variables: { firstName: 'John' },
|
|
92
|
+
fromEmail: 'noreply@yourcompany.com',
|
|
93
|
+
variables: { firstName: 'John', company: 'Acme' },
|
|
84
94
|
});
|
|
85
95
|
|
|
86
|
-
//
|
|
87
|
-
|
|
96
|
+
// Send to many people
|
|
97
|
+
await outbound.templates.bulkSend({
|
|
88
98
|
templateId: template.id,
|
|
89
|
-
fromEmail: 'noreply@
|
|
99
|
+
fromEmail: 'noreply@yourcompany.com',
|
|
90
100
|
recipients: [
|
|
91
|
-
{ toEmail: 'alice@example.com', variables: { firstName: 'Alice' } },
|
|
92
|
-
{ toEmail: 'bob@example.com', variables: { firstName: 'Bob' } },
|
|
101
|
+
{ toEmail: 'alice@example.com', variables: { firstName: 'Alice', company: 'Acme' } },
|
|
102
|
+
{ toEmail: 'bob@example.com', variables: { firstName: 'Bob', company: 'Acme' } },
|
|
93
103
|
],
|
|
94
104
|
});
|
|
95
|
-
|
|
96
|
-
// Preview
|
|
97
|
-
const preview = await outbound.templates.preview(template.id, {
|
|
98
|
-
variables: { firstName: 'John' },
|
|
99
|
-
});
|
|
100
105
|
```
|
|
101
106
|
|
|
102
|
-
###
|
|
107
|
+
### 6. Multi-tenant usage
|
|
108
|
+
|
|
109
|
+
The SDK supports multi-tenant applications. You can override the API key on any individual call using the optional last argument:
|
|
103
110
|
|
|
104
111
|
```ts
|
|
105
|
-
//
|
|
106
|
-
await outbound.suppressions.add({ email: 'bad@example.com', reason: 'manual' });
|
|
112
|
+
const outbound = new Outbound(); // no default apiKey
|
|
107
113
|
|
|
108
|
-
|
|
109
|
-
const
|
|
114
|
+
const tenantAKey = 'mu_outbound_tenant_a_...';
|
|
115
|
+
const tenantBKey = 'mu_outbound_tenant_b_...';
|
|
110
116
|
|
|
111
|
-
//
|
|
112
|
-
await outbound.
|
|
117
|
+
// Pass { apiKey } as the last argument to override per call
|
|
118
|
+
await outbound.email.send({ /* ... */ }, { apiKey: tenantAKey });
|
|
119
|
+
await outbound.email.send({ /* ... */ }, { apiKey: tenantBKey });
|
|
113
120
|
```
|
|
114
121
|
|
|
115
|
-
|
|
122
|
+
You can also set a default in the constructor and override only when needed:
|
|
116
123
|
|
|
117
124
|
```ts
|
|
118
|
-
//
|
|
119
|
-
const { webhook, secret } = await outbound.webhooks.create({
|
|
120
|
-
url: 'https://myapp.com/webhooks/outbound',
|
|
121
|
-
events: ['delivery', 'bounce', 'complaint'],
|
|
122
|
-
});
|
|
123
|
-
// Store `secret` securely for signature verification
|
|
125
|
+
const outbound = new Outbound({ apiKey: tenantAKey }); // default
|
|
124
126
|
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
+
await outbound.email.send({ /* ... */ }); // uses tenantAKey
|
|
128
|
+
await outbound.email.send({ /* ... */ }, { apiKey: tenantBKey }); // uses tenantBKey
|
|
127
129
|
```
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
## Key Concepts
|
|
130
132
|
|
|
131
|
-
|
|
132
|
-
const dashboard = await outbound.dashboard.get();
|
|
133
|
-
// dashboard.last30Days.sent, dashboard.quota, etc.
|
|
133
|
+
### Email Lifecycle
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
Every email goes through these statuses:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
queued → processing → sent → delivered
|
|
139
|
+
↘ bounced
|
|
140
|
+
↘ complained
|
|
141
|
+
delivered → opened → clicked
|
|
137
142
|
```
|
|
138
143
|
|
|
139
|
-
|
|
144
|
+
| Status | Meaning |
|
|
145
|
+
|--------|---------|
|
|
146
|
+
| `queued` | Email is in the queue, waiting to be processed |
|
|
147
|
+
| `processing` | Email is being sent to AWS SES |
|
|
148
|
+
| `sent` | SES accepted the email |
|
|
149
|
+
| `delivered` | Email landed in recipient's inbox |
|
|
150
|
+
| `bounced` | Email bounced (bad address or mailbox full) |
|
|
151
|
+
| `complained` | Recipient marked it as spam |
|
|
152
|
+
| `opened` | Recipient opened the email (requires tracking) |
|
|
153
|
+
| `clicked` | Recipient clicked a link (requires tracking) |
|
|
154
|
+
| `failed` | Failed to send (SES rejection or error) |
|
|
140
155
|
|
|
141
|
-
|
|
142
|
-
|--------|---------|-------------|
|
|
143
|
-
| `apiKey` | — | Default API key (optional if provided per call) |
|
|
144
|
-
| `baseUrl` | `https://outbound-api.mastersunion.org` | API base URL |
|
|
145
|
-
| `timeout` | `30000` | Request timeout in ms |
|
|
146
|
-
| `maxRetries` | `3` | Max retries on 429/5xx |
|
|
147
|
-
| `retryDelay` | `1000` | Initial retry delay in ms (exponential backoff) |
|
|
156
|
+
### Suppression List
|
|
148
157
|
|
|
149
|
-
|
|
158
|
+
The platform automatically suppresses emails that bounce or receive complaints. You can also manually suppress emails. Any future send to a suppressed address is **silently filtered out** — it won't count against your quota.
|
|
150
159
|
|
|
151
|
-
|
|
160
|
+
### Quotas
|
|
152
161
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
console.log('Resource not found');
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
```
|
|
162
|
+
Your tenant account has:
|
|
163
|
+
- **Daily limit** — Max emails per day
|
|
164
|
+
- **Monthly limit** — Max emails per month
|
|
165
|
+
- **Rate limit** — Max emails per second
|
|
166
|
+
- **Template limit** — Max number of templates
|
|
167
|
+
|
|
168
|
+
Check your quota anytime with `outbound.dashboard.quota()`.
|
|
169
|
+
|
|
170
|
+
## What's Next?
|
|
166
171
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
| `NotFoundError` | 404 |
|
|
173
|
-
| `ConflictError` | 409 |
|
|
174
|
-
| `RateLimitError` | 429 |
|
|
175
|
-
| `ServerError` | 5xx |
|
|
176
|
-
| `TimeoutError` | - |
|
|
177
|
-
| `NetworkError` | - |
|
|
178
|
-
|
|
179
|
-
## Requirements
|
|
180
|
-
|
|
181
|
-
- Node.js 18+ (uses native `fetch`)
|
|
172
|
+
- [Configuration](/guide/configuration) — Customize timeouts, retries, and more
|
|
173
|
+
- [Error Handling](/guide/error-handling) — Handle every error type
|
|
174
|
+
- [Email API](/api/email) — Single and bulk sending reference
|
|
175
|
+
- [Templates API](/api/templates) — Full template lifecycle
|
|
176
|
+
- [Webhooks API](/api/webhooks) — Real-time event notifications
|
|
182
177
|
|
|
183
178
|
## License
|
|
184
179
|
|
package/dist/index.d.mts
CHANGED
|
@@ -27,6 +27,7 @@ interface SendEmailParams {
|
|
|
27
27
|
textBody?: string;
|
|
28
28
|
priority?: 'low' | 'normal' | 'high';
|
|
29
29
|
idempotencyKey?: string;
|
|
30
|
+
campaignId?: string;
|
|
30
31
|
metadata?: Record<string, unknown>;
|
|
31
32
|
headers?: Record<string, string>;
|
|
32
33
|
tracking?: Record<string, unknown>;
|
|
@@ -50,6 +51,7 @@ interface BulkEmailParams {
|
|
|
50
51
|
senderName?: string;
|
|
51
52
|
replyTo?: string;
|
|
52
53
|
idempotencyKey?: string;
|
|
54
|
+
campaignId?: string;
|
|
53
55
|
}
|
|
54
56
|
interface BulkEmailRecipient {
|
|
55
57
|
toEmail: string;
|
|
@@ -66,12 +68,31 @@ interface BulkEmailResponse {
|
|
|
66
68
|
jobId: string;
|
|
67
69
|
recipientCount: number;
|
|
68
70
|
suppressedCount: number;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
/**
|
|
72
|
+
* List of email addresses that were filtered out due to suppression.
|
|
73
|
+
* Present on the first (live) response only.
|
|
74
|
+
* Absent when `duplicate: true` — use `suppressedCount` instead.
|
|
75
|
+
*/
|
|
76
|
+
suppressedEmails?: string[];
|
|
77
|
+
duplicatesRemoved?: number;
|
|
78
|
+
duplicate?: boolean;
|
|
79
|
+
}
|
|
80
|
+
interface EmailJob {
|
|
81
|
+
job_id: string;
|
|
82
|
+
tenant_id: string;
|
|
83
|
+
status: 'queued' | 'processing' | 'completed' | 'failed' | 'partial' | 'retrying' | 'cancelled';
|
|
84
|
+
campaign_id?: string;
|
|
85
|
+
recipient_count: number;
|
|
86
|
+
sent_count: number;
|
|
87
|
+
failed_count: number;
|
|
88
|
+
metadata?: Record<string, unknown>;
|
|
89
|
+
created_at: string;
|
|
90
|
+
updated_at: string;
|
|
71
91
|
}
|
|
72
92
|
interface JobStatusResponse {
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
lookupType: 'jobId' | 'sesMessageId';
|
|
94
|
+
job?: EmailJob;
|
|
95
|
+
recipients?: EmailRecipientStatus[];
|
|
75
96
|
}
|
|
76
97
|
interface EmailRecipientStatus {
|
|
77
98
|
message_id: string;
|
|
@@ -81,7 +102,22 @@ interface EmailRecipientStatus {
|
|
|
81
102
|
error_message?: string;
|
|
82
103
|
created_at: string;
|
|
83
104
|
}
|
|
84
|
-
type EmailStatus = 'queued' | 'processing' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'opened' | 'clicked' | 'failed';
|
|
105
|
+
type EmailStatus = 'queued' | 'processing' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'opened' | 'clicked' | 'failed' | 'cancelled';
|
|
106
|
+
interface CancelEmailParams {
|
|
107
|
+
/** Cancel all jobs belonging to this campaign. */
|
|
108
|
+
campaignId?: string;
|
|
109
|
+
/** Cancel a specific job by its ID. */
|
|
110
|
+
jobId?: string;
|
|
111
|
+
}
|
|
112
|
+
interface CancelEmailResponse {
|
|
113
|
+
message: string;
|
|
114
|
+
/** Number of jobs that were cancelled. */
|
|
115
|
+
cancelledJobs: number;
|
|
116
|
+
/** Number of individual recipient records set to cancelled. */
|
|
117
|
+
cancelledRecipients: number;
|
|
118
|
+
/** Number of jobs successfully removed from the queue before processing started. */
|
|
119
|
+
removedFromQueue: number;
|
|
120
|
+
}
|
|
85
121
|
interface CreateTemplateParams {
|
|
86
122
|
name: string;
|
|
87
123
|
subject: string;
|
|
@@ -149,6 +185,7 @@ interface TemplateSendParams {
|
|
|
149
185
|
variables?: Record<string, string>;
|
|
150
186
|
priority?: 'low' | 'normal' | 'high';
|
|
151
187
|
idempotencyKey?: string;
|
|
188
|
+
campaignId?: string;
|
|
152
189
|
metadata?: Record<string, unknown>;
|
|
153
190
|
headers?: Record<string, string>;
|
|
154
191
|
tracking?: Record<string, unknown>;
|
|
@@ -159,6 +196,7 @@ interface TemplateBulkSendParams {
|
|
|
159
196
|
senderName?: string;
|
|
160
197
|
replyTo?: string;
|
|
161
198
|
idempotencyKey?: string;
|
|
199
|
+
campaignId?: string;
|
|
162
200
|
recipients: TemplateBulkRecipient[];
|
|
163
201
|
}
|
|
164
202
|
interface TemplateBulkRecipient {
|
|
@@ -173,9 +211,15 @@ interface TemplateBulkSendResponse {
|
|
|
173
211
|
jobId: string;
|
|
174
212
|
recipientCount: number;
|
|
175
213
|
suppressedCount: number;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
214
|
+
/**
|
|
215
|
+
* List of email addresses filtered out due to suppression.
|
|
216
|
+
* Present on the first (live) response only.
|
|
217
|
+
* Absent when `duplicate: true` — use `suppressedCount` instead.
|
|
218
|
+
*/
|
|
219
|
+
suppressedEmails?: string[];
|
|
220
|
+
duplicatesRemoved?: number;
|
|
221
|
+
usingSesTemplate?: boolean;
|
|
222
|
+
duplicate?: boolean;
|
|
179
223
|
}
|
|
180
224
|
interface TemplatePreviewParams {
|
|
181
225
|
variables?: Record<string, string>;
|
|
@@ -318,6 +362,12 @@ declare class EmailResource {
|
|
|
318
362
|
send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse>;
|
|
319
363
|
bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse>;
|
|
320
364
|
status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse>;
|
|
365
|
+
/**
|
|
366
|
+
* Cancel all queued/processing emails for a campaign or job.
|
|
367
|
+
* Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).
|
|
368
|
+
* Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.
|
|
369
|
+
*/
|
|
370
|
+
cancel(params: CancelEmailParams, overrides?: RequestOverrides): Promise<CancelEmailResponse>;
|
|
321
371
|
}
|
|
322
372
|
|
|
323
373
|
declare class TemplatesResource {
|
|
@@ -420,4 +470,4 @@ declare class NetworkError extends OutboundError {
|
|
|
420
470
|
constructor(message?: string);
|
|
421
471
|
}
|
|
422
472
|
|
|
423
|
-
export { type AddSuppressionParams, type Attachment, AuthenticationError, BadRequestError, type BulkEmailParams, type BulkEmailRecipient, type BulkEmailResponse, ConflictError, type CreateTemplateParams, type CreateWebhookParams, type CreateWebhookResponse, type DashboardResponse, type EmailRecipientStatus, type EmailStatus, ForbiddenError, type JobStatusResponse, type ListSuppressionsParams, type ListSuppressionsResponse, type ListTemplatesParams, type ListTemplatesResponse, type ListWebhooksResponse, NetworkError, NotFoundError, Outbound, type OutboundConfig, OutboundError, type QuotaResponse, RateLimitError, type RequestOverrides, type ResolvedConfig, type SendEmailParams, type SendEmailResponse, ServerError, type Suppression, type SuppressionResponse, type Template, type TemplateBulkRecipient, type TemplateBulkSendParams, type TemplateBulkSendResponse, type TemplatePreviewParams, type TemplatePreviewResponse, type TemplateResponse, type TemplateSendParams, type TemplateStatsResponse, TimeoutError, type UpdateTemplateParams, type UpdateWebhookParams, type UpdateWebhookResponse, type Webhook, type WebhookEvent };
|
|
473
|
+
export { type AddSuppressionParams, type Attachment, AuthenticationError, BadRequestError, type BulkEmailParams, type BulkEmailRecipient, type BulkEmailResponse, type CancelEmailParams, type CancelEmailResponse, ConflictError, type CreateTemplateParams, type CreateWebhookParams, type CreateWebhookResponse, type DashboardResponse, type EmailJob, type EmailRecipientStatus, type EmailStatus, ForbiddenError, type JobStatusResponse, type ListSuppressionsParams, type ListSuppressionsResponse, type ListTemplatesParams, type ListTemplatesResponse, type ListWebhooksResponse, NetworkError, NotFoundError, Outbound, type OutboundConfig, OutboundError, type QuotaResponse, RateLimitError, type RequestOverrides, type ResolvedConfig, type SendEmailParams, type SendEmailResponse, ServerError, type Suppression, type SuppressionResponse, type Template, type TemplateBulkRecipient, type TemplateBulkSendParams, type TemplateBulkSendResponse, type TemplatePreviewParams, type TemplatePreviewResponse, type TemplateResponse, type TemplateSendParams, type TemplateStatsResponse, TimeoutError, type UpdateTemplateParams, type UpdateWebhookParams, type UpdateWebhookResponse, type Webhook, type WebhookEvent };
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ interface SendEmailParams {
|
|
|
27
27
|
textBody?: string;
|
|
28
28
|
priority?: 'low' | 'normal' | 'high';
|
|
29
29
|
idempotencyKey?: string;
|
|
30
|
+
campaignId?: string;
|
|
30
31
|
metadata?: Record<string, unknown>;
|
|
31
32
|
headers?: Record<string, string>;
|
|
32
33
|
tracking?: Record<string, unknown>;
|
|
@@ -50,6 +51,7 @@ interface BulkEmailParams {
|
|
|
50
51
|
senderName?: string;
|
|
51
52
|
replyTo?: string;
|
|
52
53
|
idempotencyKey?: string;
|
|
54
|
+
campaignId?: string;
|
|
53
55
|
}
|
|
54
56
|
interface BulkEmailRecipient {
|
|
55
57
|
toEmail: string;
|
|
@@ -66,12 +68,31 @@ interface BulkEmailResponse {
|
|
|
66
68
|
jobId: string;
|
|
67
69
|
recipientCount: number;
|
|
68
70
|
suppressedCount: number;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
/**
|
|
72
|
+
* List of email addresses that were filtered out due to suppression.
|
|
73
|
+
* Present on the first (live) response only.
|
|
74
|
+
* Absent when `duplicate: true` — use `suppressedCount` instead.
|
|
75
|
+
*/
|
|
76
|
+
suppressedEmails?: string[];
|
|
77
|
+
duplicatesRemoved?: number;
|
|
78
|
+
duplicate?: boolean;
|
|
79
|
+
}
|
|
80
|
+
interface EmailJob {
|
|
81
|
+
job_id: string;
|
|
82
|
+
tenant_id: string;
|
|
83
|
+
status: 'queued' | 'processing' | 'completed' | 'failed' | 'partial' | 'retrying' | 'cancelled';
|
|
84
|
+
campaign_id?: string;
|
|
85
|
+
recipient_count: number;
|
|
86
|
+
sent_count: number;
|
|
87
|
+
failed_count: number;
|
|
88
|
+
metadata?: Record<string, unknown>;
|
|
89
|
+
created_at: string;
|
|
90
|
+
updated_at: string;
|
|
71
91
|
}
|
|
72
92
|
interface JobStatusResponse {
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
lookupType: 'jobId' | 'sesMessageId';
|
|
94
|
+
job?: EmailJob;
|
|
95
|
+
recipients?: EmailRecipientStatus[];
|
|
75
96
|
}
|
|
76
97
|
interface EmailRecipientStatus {
|
|
77
98
|
message_id: string;
|
|
@@ -81,7 +102,22 @@ interface EmailRecipientStatus {
|
|
|
81
102
|
error_message?: string;
|
|
82
103
|
created_at: string;
|
|
83
104
|
}
|
|
84
|
-
type EmailStatus = 'queued' | 'processing' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'opened' | 'clicked' | 'failed';
|
|
105
|
+
type EmailStatus = 'queued' | 'processing' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'opened' | 'clicked' | 'failed' | 'cancelled';
|
|
106
|
+
interface CancelEmailParams {
|
|
107
|
+
/** Cancel all jobs belonging to this campaign. */
|
|
108
|
+
campaignId?: string;
|
|
109
|
+
/** Cancel a specific job by its ID. */
|
|
110
|
+
jobId?: string;
|
|
111
|
+
}
|
|
112
|
+
interface CancelEmailResponse {
|
|
113
|
+
message: string;
|
|
114
|
+
/** Number of jobs that were cancelled. */
|
|
115
|
+
cancelledJobs: number;
|
|
116
|
+
/** Number of individual recipient records set to cancelled. */
|
|
117
|
+
cancelledRecipients: number;
|
|
118
|
+
/** Number of jobs successfully removed from the queue before processing started. */
|
|
119
|
+
removedFromQueue: number;
|
|
120
|
+
}
|
|
85
121
|
interface CreateTemplateParams {
|
|
86
122
|
name: string;
|
|
87
123
|
subject: string;
|
|
@@ -149,6 +185,7 @@ interface TemplateSendParams {
|
|
|
149
185
|
variables?: Record<string, string>;
|
|
150
186
|
priority?: 'low' | 'normal' | 'high';
|
|
151
187
|
idempotencyKey?: string;
|
|
188
|
+
campaignId?: string;
|
|
152
189
|
metadata?: Record<string, unknown>;
|
|
153
190
|
headers?: Record<string, string>;
|
|
154
191
|
tracking?: Record<string, unknown>;
|
|
@@ -159,6 +196,7 @@ interface TemplateBulkSendParams {
|
|
|
159
196
|
senderName?: string;
|
|
160
197
|
replyTo?: string;
|
|
161
198
|
idempotencyKey?: string;
|
|
199
|
+
campaignId?: string;
|
|
162
200
|
recipients: TemplateBulkRecipient[];
|
|
163
201
|
}
|
|
164
202
|
interface TemplateBulkRecipient {
|
|
@@ -173,9 +211,15 @@ interface TemplateBulkSendResponse {
|
|
|
173
211
|
jobId: string;
|
|
174
212
|
recipientCount: number;
|
|
175
213
|
suppressedCount: number;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
214
|
+
/**
|
|
215
|
+
* List of email addresses filtered out due to suppression.
|
|
216
|
+
* Present on the first (live) response only.
|
|
217
|
+
* Absent when `duplicate: true` — use `suppressedCount` instead.
|
|
218
|
+
*/
|
|
219
|
+
suppressedEmails?: string[];
|
|
220
|
+
duplicatesRemoved?: number;
|
|
221
|
+
usingSesTemplate?: boolean;
|
|
222
|
+
duplicate?: boolean;
|
|
179
223
|
}
|
|
180
224
|
interface TemplatePreviewParams {
|
|
181
225
|
variables?: Record<string, string>;
|
|
@@ -318,6 +362,12 @@ declare class EmailResource {
|
|
|
318
362
|
send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse>;
|
|
319
363
|
bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse>;
|
|
320
364
|
status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse>;
|
|
365
|
+
/**
|
|
366
|
+
* Cancel all queued/processing emails for a campaign or job.
|
|
367
|
+
* Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).
|
|
368
|
+
* Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.
|
|
369
|
+
*/
|
|
370
|
+
cancel(params: CancelEmailParams, overrides?: RequestOverrides): Promise<CancelEmailResponse>;
|
|
321
371
|
}
|
|
322
372
|
|
|
323
373
|
declare class TemplatesResource {
|
|
@@ -420,4 +470,4 @@ declare class NetworkError extends OutboundError {
|
|
|
420
470
|
constructor(message?: string);
|
|
421
471
|
}
|
|
422
472
|
|
|
423
|
-
export { type AddSuppressionParams, type Attachment, AuthenticationError, BadRequestError, type BulkEmailParams, type BulkEmailRecipient, type BulkEmailResponse, ConflictError, type CreateTemplateParams, type CreateWebhookParams, type CreateWebhookResponse, type DashboardResponse, type EmailRecipientStatus, type EmailStatus, ForbiddenError, type JobStatusResponse, type ListSuppressionsParams, type ListSuppressionsResponse, type ListTemplatesParams, type ListTemplatesResponse, type ListWebhooksResponse, NetworkError, NotFoundError, Outbound, type OutboundConfig, OutboundError, type QuotaResponse, RateLimitError, type RequestOverrides, type ResolvedConfig, type SendEmailParams, type SendEmailResponse, ServerError, type Suppression, type SuppressionResponse, type Template, type TemplateBulkRecipient, type TemplateBulkSendParams, type TemplateBulkSendResponse, type TemplatePreviewParams, type TemplatePreviewResponse, type TemplateResponse, type TemplateSendParams, type TemplateStatsResponse, TimeoutError, type UpdateTemplateParams, type UpdateWebhookParams, type UpdateWebhookResponse, type Webhook, type WebhookEvent };
|
|
473
|
+
export { type AddSuppressionParams, type Attachment, AuthenticationError, BadRequestError, type BulkEmailParams, type BulkEmailRecipient, type BulkEmailResponse, type CancelEmailParams, type CancelEmailResponse, ConflictError, type CreateTemplateParams, type CreateWebhookParams, type CreateWebhookResponse, type DashboardResponse, type EmailJob, type EmailRecipientStatus, type EmailStatus, ForbiddenError, type JobStatusResponse, type ListSuppressionsParams, type ListSuppressionsResponse, type ListTemplatesParams, type ListTemplatesResponse, type ListWebhooksResponse, NetworkError, NotFoundError, Outbound, type OutboundConfig, OutboundError, type QuotaResponse, RateLimitError, type RequestOverrides, type ResolvedConfig, type SendEmailParams, type SendEmailResponse, ServerError, type Suppression, type SuppressionResponse, type Template, type TemplateBulkRecipient, type TemplateBulkSendParams, type TemplateBulkSendResponse, type TemplatePreviewParams, type TemplatePreviewResponse, type TemplateResponse, type TemplateSendParams, type TemplateStatsResponse, TimeoutError, type UpdateTemplateParams, type UpdateWebhookParams, type UpdateWebhookResponse, type Webhook, type WebhookEvent };
|
package/dist/index.js
CHANGED
|
@@ -253,6 +253,17 @@ var EmailResource = class {
|
|
|
253
253
|
async status(jobId, overrides) {
|
|
254
254
|
return this.http.get(`/v1/email/status/${encodeURIComponent(jobId)}`, void 0, overrides?.apiKey);
|
|
255
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Cancel all queued/processing emails for a campaign or job.
|
|
258
|
+
* Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).
|
|
259
|
+
* Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.
|
|
260
|
+
*/
|
|
261
|
+
async cancel(params, overrides) {
|
|
262
|
+
if (!params.campaignId && !params.jobId) {
|
|
263
|
+
throw new Error("cancel() requires either campaignId or jobId");
|
|
264
|
+
}
|
|
265
|
+
return this.http.post("/v1/email/cancel", params, overrides?.apiKey);
|
|
266
|
+
}
|
|
256
267
|
};
|
|
257
268
|
|
|
258
269
|
// src/resources/templates.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/resources/email.ts","../src/resources/templates.ts","../src/resources/suppressions.ts","../src/resources/webhooks.ts","../src/resources/dashboard.ts","../src/client.ts"],"sourcesContent":["export { Outbound } from './client';\n\n// Error classes\nexport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\n// All types\nexport type {\n OutboundConfig,\n ResolvedConfig,\n RequestOverrides,\n SendEmailParams,\n Attachment,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailRecipient,\n BulkEmailResponse,\n JobStatusResponse,\n EmailRecipientStatus,\n EmailStatus,\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n TemplateBulkSendParams,\n TemplateBulkRecipient,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n ListSuppressionsParams,\n ListSuppressionsResponse,\n Suppression,\n AddSuppressionParams,\n SuppressionResponse,\n WebhookEvent,\n CreateWebhookParams,\n UpdateWebhookParams,\n Webhook,\n CreateWebhookResponse,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n DashboardResponse,\n QuotaResponse,\n} from './types';\n","export class OutboundError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly details?: unknown,\n public readonly requestId?: string,\n ) {\n super(message);\n this.name = 'OutboundError';\n }\n}\n\nexport class BadRequestError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 400, details, requestId);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 401, details, requestId);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class ForbiddenError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 403, details, requestId);\n this.name = 'ForbiddenError';\n }\n}\n\nexport class NotFoundError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 404, details, requestId);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 409, details, requestId);\n this.name = 'ConflictError';\n }\n}\n\nexport class RateLimitError extends OutboundError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number, details?: unknown, requestId?: string) {\n super(message, 429, details, requestId);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\nexport class ServerError extends OutboundError {\n constructor(message: string, statusCode: number = 500, details?: unknown, requestId?: string) {\n super(message, statusCode, details, requestId);\n this.name = 'ServerError';\n }\n}\n\nexport class TimeoutError extends OutboundError {\n constructor(message: string = 'Request timed out') {\n super(message, 0);\n this.name = 'TimeoutError';\n }\n}\n\nexport class NetworkError extends OutboundError {\n constructor(message: string = 'Network request failed') {\n super(message, 0);\n this.name = 'NetworkError';\n }\n}\n","import type { ResolvedConfig } from './types';\nimport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\nexport interface RequestOptions {\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport class HttpClient {\n constructor(private config: ResolvedConfig) {}\n\n async get<T>(path: string, params?: Record<string, unknown>, apiKey?: string): Promise<T> {\n return this.request<T>('GET', path, { params }, apiKey);\n }\n\n async post<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('POST', path, { body }, apiKey);\n }\n\n async patch<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('PATCH', path, { body }, apiKey);\n }\n\n async delete<T>(path: string, apiKey?: string): Promise<T> {\n return this.request<T>('DELETE', path, undefined, apiKey);\n }\n\n private resolveApiKey(apiKey?: string): string {\n const key = apiKey || this.config.apiKey;\n if (!key) {\n throw new AuthenticationError(\n 'API key is required. Pass it to the constructor or provide it per method call.',\n );\n }\n return key;\n }\n\n private async request<T>(method: string, path: string, options?: RequestOptions, apiKey?: string): Promise<T> {\n const resolvedKey = this.resolveApiKey(apiKey);\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const url = this.buildUrl(path, options?.params);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'X-Api-Key': resolvedKey,\n 'Content-Type': 'application/json',\n },\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n const error = await this.parseError(response);\n\n // Only retry on 429 and 5xx\n if (response.status === 429 || response.status >= 500) {\n lastError = error;\n\n if (attempt < this.config.maxRetries) {\n const retryAfter = error instanceof RateLimitError && error.retryAfter\n ? error.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n\n await this.sleep(retryAfter);\n continue;\n }\n }\n\n throw error;\n } catch (err) {\n if (err instanceof OutboundError) {\n // Already a typed error from parseError — check if retryable\n if ((err.statusCode === 429 || err.statusCode >= 500) && attempt < this.config.maxRetries) {\n lastError = err;\n const retryAfter = err instanceof RateLimitError && err.retryAfter\n ? err.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n await this.sleep(retryAfter);\n continue;\n }\n throw err;\n }\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n lastError = new TimeoutError(`Request timed out after ${this.config.timeout}ms`);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n\n lastError = new NetworkError((err as Error).message);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n }\n\n throw lastError || new NetworkError('Request failed after retries');\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = this.config.baseUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${path}`);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async parseError(response: Response): Promise<OutboundError> {\n let body: { error?: string; message?: string; details?: unknown } = {};\n const requestId = response.headers.get('x-request-id') || undefined;\n\n try {\n body = (await response.json()) as typeof body;\n } catch {\n // Response may not be JSON\n }\n\n const message = body.error || body.message || `HTTP ${response.status}`;\n const details = body.details;\n\n switch (response.status) {\n case 400:\n return new BadRequestError(message, details, requestId);\n case 401:\n return new AuthenticationError(message, details, requestId);\n case 403:\n return new ForbiddenError(message, details, requestId);\n case 404:\n return new NotFoundError(message, details, requestId);\n case 409:\n return new ConflictError(message, details, requestId);\n case 429: {\n const retryAfter = response.headers.get('retry-after');\n return new RateLimitError(\n message,\n retryAfter ? parseInt(retryAfter, 10) : undefined,\n details,\n requestId,\n );\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message, response.status, details, requestId);\n }\n return new OutboundError(message, response.status, details, requestId);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n SendEmailParams,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailResponse,\n JobStatusResponse,\n RequestOverrides,\n} from '../types';\n\nexport class EmailResource {\n constructor(private http: HttpClient) {}\n\n async send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email/send', params, overrides?.apiKey);\n }\n\n async bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse> {\n return this.http.post<BulkEmailResponse>('/v1/email/bulk', params, overrides?.apiKey);\n }\n\n async status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse> {\n return this.http.get<JobStatusResponse>(`/v1/email/status/${encodeURIComponent(jobId)}`, undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n SendEmailResponse,\n TemplateBulkSendParams,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n RequestOverrides,\n} from '../types';\n\nexport class TemplatesResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>('/v1/email-templates', params, overrides?.apiKey);\n }\n\n async list(params?: ListTemplatesParams, overrides?: RequestOverrides): Promise<ListTemplatesResponse> {\n return this.http.get<ListTemplatesResponse>('/v1/email-templates', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListTemplatesParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Template> {\n let page = 1;\n const limit = params?.limit || 20;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const template of result.templates) {\n yield template;\n }\n if (result.templates.length < limit) break;\n page++;\n }\n }\n\n async get(id: string, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.get<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.patch<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/email-templates/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n\n async duplicate(id: string, params?: { name?: string }, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}/duplicate`, params, overrides?.apiKey);\n }\n\n async preview(id: string, params?: TemplatePreviewParams, overrides?: RequestOverrides): Promise<TemplatePreviewResponse> {\n return this.http.post<TemplatePreviewResponse>(`/v1/email-templates/${encodeURIComponent(id)}/preview`, params, overrides?.apiKey);\n }\n\n async send(params: TemplateSendParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email-templates/send', params, overrides?.apiKey);\n }\n\n async bulkSend(params: TemplateBulkSendParams, overrides?: RequestOverrides): Promise<TemplateBulkSendResponse> {\n return this.http.post<TemplateBulkSendResponse>('/v1/email-templates/bulk', params, overrides?.apiKey);\n }\n\n async stats(overrides?: RequestOverrides): Promise<TemplateStatsResponse> {\n return this.http.get<TemplateStatsResponse>('/v1/email-templates/stats', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListSuppressionsParams,\n ListSuppressionsResponse,\n AddSuppressionParams,\n SuppressionResponse,\n Suppression,\n RequestOverrides,\n} from '../types';\n\nexport class SuppressionsResource {\n constructor(private http: HttpClient) {}\n\n async list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse> {\n return this.http.get<ListSuppressionsResponse>('/v1/tenants/suppressions', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression> {\n let page = 1;\n const limit = params?.limit || 50;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const suppression of result.suppressions) {\n yield suppression;\n }\n if (result.suppressions.length < limit) break;\n page++;\n }\n }\n\n async add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse> {\n return this.http.post<SuppressionResponse>('/v1/tenants/suppressions', params, overrides?.apiKey);\n }\n\n async remove(email: string, overrides?: RequestOverrides): Promise<{ message: string }> {\n return this.http.delete<{ message: string }>(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateWebhookParams,\n CreateWebhookResponse,\n UpdateWebhookParams,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n RequestOverrides,\n} from '../types';\n\nexport class WebhooksResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateWebhookParams, overrides?: RequestOverrides): Promise<CreateWebhookResponse> {\n return this.http.post<CreateWebhookResponse>('/v1/tenants/webhooks', params, overrides?.apiKey);\n }\n\n async list(overrides?: RequestOverrides): Promise<ListWebhooksResponse> {\n return this.http.get<ListWebhooksResponse>('/v1/tenants/webhooks', undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateWebhookParams, overrides?: RequestOverrides): Promise<UpdateWebhookResponse> {\n return this.http.patch<UpdateWebhookResponse>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { DashboardResponse, QuotaResponse, RequestOverrides } from '../types';\n\nexport class DashboardResource {\n constructor(private http: HttpClient) {}\n\n async get(overrides?: RequestOverrides): Promise<DashboardResponse> {\n return this.http.get<DashboardResponse>('/v1/tenants/dashboard', undefined, overrides?.apiKey);\n }\n\n async quota(overrides?: RequestOverrides): Promise<QuotaResponse> {\n return this.http.get<QuotaResponse>('/v1/tenants/quota', undefined, overrides?.apiKey);\n }\n}\n","import { HttpClient } from './http';\nimport { EmailResource } from './resources/email';\nimport { TemplatesResource } from './resources/templates';\nimport { SuppressionsResource } from './resources/suppressions';\nimport { WebhooksResource } from './resources/webhooks';\nimport { DashboardResource } from './resources/dashboard';\nimport type { OutboundConfig, ResolvedConfig } from './types';\n\nconst BASE_URL = 'https://outbound-api.mastersunion.org';\n\nexport class Outbound {\n readonly email: EmailResource;\n readonly templates: TemplatesResource;\n readonly suppressions: SuppressionsResource;\n readonly webhooks: WebhooksResource;\n readonly dashboard: DashboardResource;\n\n constructor(config?: OutboundConfig) {\n const resolved: ResolvedConfig = {\n apiKey: config?.apiKey,\n baseUrl: config?.baseUrl || BASE_URL,\n timeout: config?.timeout ?? 30_000,\n maxRetries: config?.maxRetries ?? 3,\n retryDelay: config?.retryDelay ?? 1000,\n };\n\n const http = new HttpClient(resolved);\n\n this.email = new EmailResource(http);\n this.templates = new TemplatesResource(http);\n this.suppressions = new SuppressionsResource(http);\n this.webhooks = new WebhooksResource(http);\n this.dashboard = new DashboardResource(http);\n }\n\n /**\n * Verify a webhook signature using HMAC-SHA256.\n * Use this in your webhook handler to validate incoming requests.\n */\n static verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): boolean {\n // Dynamic import to keep browser-compatible at the type level\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const crypto = require('crypto') as typeof import('crypto');\n const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n try {\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/resources/email.ts","../src/resources/templates.ts","../src/resources/suppressions.ts","../src/resources/webhooks.ts","../src/resources/dashboard.ts","../src/client.ts"],"sourcesContent":["export { Outbound } from './client';\n\n// Error classes\nexport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\n// All types\nexport type {\n OutboundConfig,\n ResolvedConfig,\n RequestOverrides,\n SendEmailParams,\n Attachment,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailRecipient,\n BulkEmailResponse,\n EmailJob,\n JobStatusResponse,\n EmailRecipientStatus,\n EmailStatus,\n CancelEmailParams,\n CancelEmailResponse,\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n TemplateBulkSendParams,\n TemplateBulkRecipient,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n ListSuppressionsParams,\n ListSuppressionsResponse,\n Suppression,\n AddSuppressionParams,\n SuppressionResponse,\n WebhookEvent,\n CreateWebhookParams,\n UpdateWebhookParams,\n Webhook,\n CreateWebhookResponse,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n DashboardResponse,\n QuotaResponse,\n} from './types';\n","export class OutboundError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly details?: unknown,\n public readonly requestId?: string,\n ) {\n super(message);\n this.name = 'OutboundError';\n }\n}\n\nexport class BadRequestError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 400, details, requestId);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 401, details, requestId);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class ForbiddenError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 403, details, requestId);\n this.name = 'ForbiddenError';\n }\n}\n\nexport class NotFoundError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 404, details, requestId);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 409, details, requestId);\n this.name = 'ConflictError';\n }\n}\n\nexport class RateLimitError extends OutboundError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number, details?: unknown, requestId?: string) {\n super(message, 429, details, requestId);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\nexport class ServerError extends OutboundError {\n constructor(message: string, statusCode: number = 500, details?: unknown, requestId?: string) {\n super(message, statusCode, details, requestId);\n this.name = 'ServerError';\n }\n}\n\nexport class TimeoutError extends OutboundError {\n constructor(message: string = 'Request timed out') {\n super(message, 0);\n this.name = 'TimeoutError';\n }\n}\n\nexport class NetworkError extends OutboundError {\n constructor(message: string = 'Network request failed') {\n super(message, 0);\n this.name = 'NetworkError';\n }\n}\n","import type { ResolvedConfig } from './types';\nimport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\nexport interface RequestOptions {\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport class HttpClient {\n constructor(private config: ResolvedConfig) {}\n\n async get<T>(path: string, params?: Record<string, unknown>, apiKey?: string): Promise<T> {\n return this.request<T>('GET', path, { params }, apiKey);\n }\n\n async post<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('POST', path, { body }, apiKey);\n }\n\n async patch<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('PATCH', path, { body }, apiKey);\n }\n\n async delete<T>(path: string, apiKey?: string): Promise<T> {\n return this.request<T>('DELETE', path, undefined, apiKey);\n }\n\n private resolveApiKey(apiKey?: string): string {\n const key = apiKey || this.config.apiKey;\n if (!key) {\n throw new AuthenticationError(\n 'API key is required. Pass it to the constructor or provide it per method call.',\n );\n }\n return key;\n }\n\n private async request<T>(method: string, path: string, options?: RequestOptions, apiKey?: string): Promise<T> {\n const resolvedKey = this.resolveApiKey(apiKey);\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const url = this.buildUrl(path, options?.params);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'X-Api-Key': resolvedKey,\n 'Content-Type': 'application/json',\n },\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n const error = await this.parseError(response);\n\n // Only retry on 429 and 5xx\n if (response.status === 429 || response.status >= 500) {\n lastError = error;\n\n if (attempt < this.config.maxRetries) {\n const retryAfter = error instanceof RateLimitError && error.retryAfter\n ? error.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n\n await this.sleep(retryAfter);\n continue;\n }\n }\n\n throw error;\n } catch (err) {\n if (err instanceof OutboundError) {\n // Already a typed error from parseError — check if retryable\n if ((err.statusCode === 429 || err.statusCode >= 500) && attempt < this.config.maxRetries) {\n lastError = err;\n const retryAfter = err instanceof RateLimitError && err.retryAfter\n ? err.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n await this.sleep(retryAfter);\n continue;\n }\n throw err;\n }\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n lastError = new TimeoutError(`Request timed out after ${this.config.timeout}ms`);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n\n lastError = new NetworkError((err as Error).message);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n }\n\n throw lastError || new NetworkError('Request failed after retries');\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = this.config.baseUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${path}`);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async parseError(response: Response): Promise<OutboundError> {\n let body: { error?: string; message?: string; details?: unknown } = {};\n const requestId = response.headers.get('x-request-id') || undefined;\n\n try {\n body = (await response.json()) as typeof body;\n } catch {\n // Response may not be JSON\n }\n\n const message = body.error || body.message || `HTTP ${response.status}`;\n const details = body.details;\n\n switch (response.status) {\n case 400:\n return new BadRequestError(message, details, requestId);\n case 401:\n return new AuthenticationError(message, details, requestId);\n case 403:\n return new ForbiddenError(message, details, requestId);\n case 404:\n return new NotFoundError(message, details, requestId);\n case 409:\n return new ConflictError(message, details, requestId);\n case 429: {\n const retryAfter = response.headers.get('retry-after');\n return new RateLimitError(\n message,\n retryAfter ? parseInt(retryAfter, 10) : undefined,\n details,\n requestId,\n );\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message, response.status, details, requestId);\n }\n return new OutboundError(message, response.status, details, requestId);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n SendEmailParams,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailResponse,\n JobStatusResponse,\n CancelEmailParams,\n CancelEmailResponse,\n RequestOverrides,\n} from '../types';\n\nexport class EmailResource {\n constructor(private http: HttpClient) {}\n\n async send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email/send', params, overrides?.apiKey);\n }\n\n async bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse> {\n return this.http.post<BulkEmailResponse>('/v1/email/bulk', params, overrides?.apiKey);\n }\n\n async status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse> {\n return this.http.get<JobStatusResponse>(`/v1/email/status/${encodeURIComponent(jobId)}`, undefined, overrides?.apiKey);\n }\n\n /**\n * Cancel all queued/processing emails for a campaign or job.\n * Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).\n * Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.\n */\n async cancel(params: CancelEmailParams, overrides?: RequestOverrides): Promise<CancelEmailResponse> {\n if (!params.campaignId && !params.jobId) {\n throw new Error('cancel() requires either campaignId or jobId');\n }\n return this.http.post<CancelEmailResponse>('/v1/email/cancel', params, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n SendEmailResponse,\n TemplateBulkSendParams,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n RequestOverrides,\n} from '../types';\n\nexport class TemplatesResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>('/v1/email-templates', params, overrides?.apiKey);\n }\n\n async list(params?: ListTemplatesParams, overrides?: RequestOverrides): Promise<ListTemplatesResponse> {\n return this.http.get<ListTemplatesResponse>('/v1/email-templates', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListTemplatesParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Template> {\n let page = 1;\n const limit = params?.limit || 20;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const template of result.templates) {\n yield template;\n }\n if (result.templates.length < limit) break;\n page++;\n }\n }\n\n async get(id: string, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.get<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.patch<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/email-templates/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n\n async duplicate(id: string, params?: { name?: string }, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}/duplicate`, params, overrides?.apiKey);\n }\n\n async preview(id: string, params?: TemplatePreviewParams, overrides?: RequestOverrides): Promise<TemplatePreviewResponse> {\n return this.http.post<TemplatePreviewResponse>(`/v1/email-templates/${encodeURIComponent(id)}/preview`, params, overrides?.apiKey);\n }\n\n async send(params: TemplateSendParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email-templates/send', params, overrides?.apiKey);\n }\n\n async bulkSend(params: TemplateBulkSendParams, overrides?: RequestOverrides): Promise<TemplateBulkSendResponse> {\n return this.http.post<TemplateBulkSendResponse>('/v1/email-templates/bulk', params, overrides?.apiKey);\n }\n\n async stats(overrides?: RequestOverrides): Promise<TemplateStatsResponse> {\n return this.http.get<TemplateStatsResponse>('/v1/email-templates/stats', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListSuppressionsParams,\n ListSuppressionsResponse,\n AddSuppressionParams,\n SuppressionResponse,\n Suppression,\n RequestOverrides,\n} from '../types';\n\nexport class SuppressionsResource {\n constructor(private http: HttpClient) {}\n\n async list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse> {\n return this.http.get<ListSuppressionsResponse>('/v1/tenants/suppressions', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression> {\n let page = 1;\n const limit = params?.limit || 50;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const suppression of result.suppressions) {\n yield suppression;\n }\n if (result.suppressions.length < limit) break;\n page++;\n }\n }\n\n async add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse> {\n return this.http.post<SuppressionResponse>('/v1/tenants/suppressions', params, overrides?.apiKey);\n }\n\n async remove(email: string, overrides?: RequestOverrides): Promise<{ message: string }> {\n return this.http.delete<{ message: string }>(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateWebhookParams,\n CreateWebhookResponse,\n UpdateWebhookParams,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n RequestOverrides,\n} from '../types';\n\nexport class WebhooksResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateWebhookParams, overrides?: RequestOverrides): Promise<CreateWebhookResponse> {\n return this.http.post<CreateWebhookResponse>('/v1/tenants/webhooks', params, overrides?.apiKey);\n }\n\n async list(overrides?: RequestOverrides): Promise<ListWebhooksResponse> {\n return this.http.get<ListWebhooksResponse>('/v1/tenants/webhooks', undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateWebhookParams, overrides?: RequestOverrides): Promise<UpdateWebhookResponse> {\n return this.http.patch<UpdateWebhookResponse>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { DashboardResponse, QuotaResponse, RequestOverrides } from '../types';\n\nexport class DashboardResource {\n constructor(private http: HttpClient) {}\n\n async get(overrides?: RequestOverrides): Promise<DashboardResponse> {\n return this.http.get<DashboardResponse>('/v1/tenants/dashboard', undefined, overrides?.apiKey);\n }\n\n async quota(overrides?: RequestOverrides): Promise<QuotaResponse> {\n return this.http.get<QuotaResponse>('/v1/tenants/quota', undefined, overrides?.apiKey);\n }\n}\n","import { HttpClient } from './http';\nimport { EmailResource } from './resources/email';\nimport { TemplatesResource } from './resources/templates';\nimport { SuppressionsResource } from './resources/suppressions';\nimport { WebhooksResource } from './resources/webhooks';\nimport { DashboardResource } from './resources/dashboard';\nimport type { OutboundConfig, ResolvedConfig } from './types';\n\nconst BASE_URL = 'https://outbound-api.mastersunion.org';\n\nexport class Outbound {\n readonly email: EmailResource;\n readonly templates: TemplatesResource;\n readonly suppressions: SuppressionsResource;\n readonly webhooks: WebhooksResource;\n readonly dashboard: DashboardResource;\n\n constructor(config?: OutboundConfig) {\n const resolved: ResolvedConfig = {\n apiKey: config?.apiKey,\n baseUrl: config?.baseUrl || BASE_URL,\n timeout: config?.timeout ?? 30_000,\n maxRetries: config?.maxRetries ?? 3,\n retryDelay: config?.retryDelay ?? 1000,\n };\n\n const http = new HttpClient(resolved);\n\n this.email = new EmailResource(http);\n this.templates = new TemplatesResource(http);\n this.suppressions = new SuppressionsResource(http);\n this.webhooks = new WebhooksResource(http);\n this.dashboard = new DashboardResource(http);\n }\n\n /**\n * Verify a webhook signature using HMAC-SHA256.\n * Use this in your webhook handler to validate incoming requests.\n */\n static verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): boolean {\n // Dynamic import to keep browser-compatible at the type level\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const crypto = require('crypto') as typeof import('crypto');\n const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n try {\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -224,6 +224,17 @@ var EmailResource = class {
|
|
|
224
224
|
async status(jobId, overrides) {
|
|
225
225
|
return this.http.get(`/v1/email/status/${encodeURIComponent(jobId)}`, void 0, overrides?.apiKey);
|
|
226
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Cancel all queued/processing emails for a campaign or job.
|
|
229
|
+
* Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).
|
|
230
|
+
* Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.
|
|
231
|
+
*/
|
|
232
|
+
async cancel(params, overrides) {
|
|
233
|
+
if (!params.campaignId && !params.jobId) {
|
|
234
|
+
throw new Error("cancel() requires either campaignId or jobId");
|
|
235
|
+
}
|
|
236
|
+
return this.http.post("/v1/email/cancel", params, overrides?.apiKey);
|
|
237
|
+
}
|
|
227
238
|
};
|
|
228
239
|
|
|
229
240
|
// src/resources/templates.ts
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/email.ts","../src/resources/templates.ts","../src/resources/suppressions.ts","../src/resources/webhooks.ts","../src/resources/dashboard.ts","../src/client.ts"],"sourcesContent":["export class OutboundError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly details?: unknown,\n public readonly requestId?: string,\n ) {\n super(message);\n this.name = 'OutboundError';\n }\n}\n\nexport class BadRequestError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 400, details, requestId);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 401, details, requestId);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class ForbiddenError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 403, details, requestId);\n this.name = 'ForbiddenError';\n }\n}\n\nexport class NotFoundError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 404, details, requestId);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 409, details, requestId);\n this.name = 'ConflictError';\n }\n}\n\nexport class RateLimitError extends OutboundError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number, details?: unknown, requestId?: string) {\n super(message, 429, details, requestId);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\nexport class ServerError extends OutboundError {\n constructor(message: string, statusCode: number = 500, details?: unknown, requestId?: string) {\n super(message, statusCode, details, requestId);\n this.name = 'ServerError';\n }\n}\n\nexport class TimeoutError extends OutboundError {\n constructor(message: string = 'Request timed out') {\n super(message, 0);\n this.name = 'TimeoutError';\n }\n}\n\nexport class NetworkError extends OutboundError {\n constructor(message: string = 'Network request failed') {\n super(message, 0);\n this.name = 'NetworkError';\n }\n}\n","import type { ResolvedConfig } from './types';\nimport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\nexport interface RequestOptions {\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport class HttpClient {\n constructor(private config: ResolvedConfig) {}\n\n async get<T>(path: string, params?: Record<string, unknown>, apiKey?: string): Promise<T> {\n return this.request<T>('GET', path, { params }, apiKey);\n }\n\n async post<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('POST', path, { body }, apiKey);\n }\n\n async patch<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('PATCH', path, { body }, apiKey);\n }\n\n async delete<T>(path: string, apiKey?: string): Promise<T> {\n return this.request<T>('DELETE', path, undefined, apiKey);\n }\n\n private resolveApiKey(apiKey?: string): string {\n const key = apiKey || this.config.apiKey;\n if (!key) {\n throw new AuthenticationError(\n 'API key is required. Pass it to the constructor or provide it per method call.',\n );\n }\n return key;\n }\n\n private async request<T>(method: string, path: string, options?: RequestOptions, apiKey?: string): Promise<T> {\n const resolvedKey = this.resolveApiKey(apiKey);\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const url = this.buildUrl(path, options?.params);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'X-Api-Key': resolvedKey,\n 'Content-Type': 'application/json',\n },\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n const error = await this.parseError(response);\n\n // Only retry on 429 and 5xx\n if (response.status === 429 || response.status >= 500) {\n lastError = error;\n\n if (attempt < this.config.maxRetries) {\n const retryAfter = error instanceof RateLimitError && error.retryAfter\n ? error.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n\n await this.sleep(retryAfter);\n continue;\n }\n }\n\n throw error;\n } catch (err) {\n if (err instanceof OutboundError) {\n // Already a typed error from parseError — check if retryable\n if ((err.statusCode === 429 || err.statusCode >= 500) && attempt < this.config.maxRetries) {\n lastError = err;\n const retryAfter = err instanceof RateLimitError && err.retryAfter\n ? err.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n await this.sleep(retryAfter);\n continue;\n }\n throw err;\n }\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n lastError = new TimeoutError(`Request timed out after ${this.config.timeout}ms`);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n\n lastError = new NetworkError((err as Error).message);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n }\n\n throw lastError || new NetworkError('Request failed after retries');\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = this.config.baseUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${path}`);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async parseError(response: Response): Promise<OutboundError> {\n let body: { error?: string; message?: string; details?: unknown } = {};\n const requestId = response.headers.get('x-request-id') || undefined;\n\n try {\n body = (await response.json()) as typeof body;\n } catch {\n // Response may not be JSON\n }\n\n const message = body.error || body.message || `HTTP ${response.status}`;\n const details = body.details;\n\n switch (response.status) {\n case 400:\n return new BadRequestError(message, details, requestId);\n case 401:\n return new AuthenticationError(message, details, requestId);\n case 403:\n return new ForbiddenError(message, details, requestId);\n case 404:\n return new NotFoundError(message, details, requestId);\n case 409:\n return new ConflictError(message, details, requestId);\n case 429: {\n const retryAfter = response.headers.get('retry-after');\n return new RateLimitError(\n message,\n retryAfter ? parseInt(retryAfter, 10) : undefined,\n details,\n requestId,\n );\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message, response.status, details, requestId);\n }\n return new OutboundError(message, response.status, details, requestId);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n SendEmailParams,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailResponse,\n JobStatusResponse,\n RequestOverrides,\n} from '../types';\n\nexport class EmailResource {\n constructor(private http: HttpClient) {}\n\n async send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email/send', params, overrides?.apiKey);\n }\n\n async bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse> {\n return this.http.post<BulkEmailResponse>('/v1/email/bulk', params, overrides?.apiKey);\n }\n\n async status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse> {\n return this.http.get<JobStatusResponse>(`/v1/email/status/${encodeURIComponent(jobId)}`, undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n SendEmailResponse,\n TemplateBulkSendParams,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n RequestOverrides,\n} from '../types';\n\nexport class TemplatesResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>('/v1/email-templates', params, overrides?.apiKey);\n }\n\n async list(params?: ListTemplatesParams, overrides?: RequestOverrides): Promise<ListTemplatesResponse> {\n return this.http.get<ListTemplatesResponse>('/v1/email-templates', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListTemplatesParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Template> {\n let page = 1;\n const limit = params?.limit || 20;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const template of result.templates) {\n yield template;\n }\n if (result.templates.length < limit) break;\n page++;\n }\n }\n\n async get(id: string, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.get<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.patch<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/email-templates/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n\n async duplicate(id: string, params?: { name?: string }, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}/duplicate`, params, overrides?.apiKey);\n }\n\n async preview(id: string, params?: TemplatePreviewParams, overrides?: RequestOverrides): Promise<TemplatePreviewResponse> {\n return this.http.post<TemplatePreviewResponse>(`/v1/email-templates/${encodeURIComponent(id)}/preview`, params, overrides?.apiKey);\n }\n\n async send(params: TemplateSendParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email-templates/send', params, overrides?.apiKey);\n }\n\n async bulkSend(params: TemplateBulkSendParams, overrides?: RequestOverrides): Promise<TemplateBulkSendResponse> {\n return this.http.post<TemplateBulkSendResponse>('/v1/email-templates/bulk', params, overrides?.apiKey);\n }\n\n async stats(overrides?: RequestOverrides): Promise<TemplateStatsResponse> {\n return this.http.get<TemplateStatsResponse>('/v1/email-templates/stats', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListSuppressionsParams,\n ListSuppressionsResponse,\n AddSuppressionParams,\n SuppressionResponse,\n Suppression,\n RequestOverrides,\n} from '../types';\n\nexport class SuppressionsResource {\n constructor(private http: HttpClient) {}\n\n async list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse> {\n return this.http.get<ListSuppressionsResponse>('/v1/tenants/suppressions', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression> {\n let page = 1;\n const limit = params?.limit || 50;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const suppression of result.suppressions) {\n yield suppression;\n }\n if (result.suppressions.length < limit) break;\n page++;\n }\n }\n\n async add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse> {\n return this.http.post<SuppressionResponse>('/v1/tenants/suppressions', params, overrides?.apiKey);\n }\n\n async remove(email: string, overrides?: RequestOverrides): Promise<{ message: string }> {\n return this.http.delete<{ message: string }>(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateWebhookParams,\n CreateWebhookResponse,\n UpdateWebhookParams,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n RequestOverrides,\n} from '../types';\n\nexport class WebhooksResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateWebhookParams, overrides?: RequestOverrides): Promise<CreateWebhookResponse> {\n return this.http.post<CreateWebhookResponse>('/v1/tenants/webhooks', params, overrides?.apiKey);\n }\n\n async list(overrides?: RequestOverrides): Promise<ListWebhooksResponse> {\n return this.http.get<ListWebhooksResponse>('/v1/tenants/webhooks', undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateWebhookParams, overrides?: RequestOverrides): Promise<UpdateWebhookResponse> {\n return this.http.patch<UpdateWebhookResponse>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { DashboardResponse, QuotaResponse, RequestOverrides } from '../types';\n\nexport class DashboardResource {\n constructor(private http: HttpClient) {}\n\n async get(overrides?: RequestOverrides): Promise<DashboardResponse> {\n return this.http.get<DashboardResponse>('/v1/tenants/dashboard', undefined, overrides?.apiKey);\n }\n\n async quota(overrides?: RequestOverrides): Promise<QuotaResponse> {\n return this.http.get<QuotaResponse>('/v1/tenants/quota', undefined, overrides?.apiKey);\n }\n}\n","import { HttpClient } from './http';\nimport { EmailResource } from './resources/email';\nimport { TemplatesResource } from './resources/templates';\nimport { SuppressionsResource } from './resources/suppressions';\nimport { WebhooksResource } from './resources/webhooks';\nimport { DashboardResource } from './resources/dashboard';\nimport type { OutboundConfig, ResolvedConfig } from './types';\n\nconst BASE_URL = 'https://outbound-api.mastersunion.org';\n\nexport class Outbound {\n readonly email: EmailResource;\n readonly templates: TemplatesResource;\n readonly suppressions: SuppressionsResource;\n readonly webhooks: WebhooksResource;\n readonly dashboard: DashboardResource;\n\n constructor(config?: OutboundConfig) {\n const resolved: ResolvedConfig = {\n apiKey: config?.apiKey,\n baseUrl: config?.baseUrl || BASE_URL,\n timeout: config?.timeout ?? 30_000,\n maxRetries: config?.maxRetries ?? 3,\n retryDelay: config?.retryDelay ?? 1000,\n };\n\n const http = new HttpClient(resolved);\n\n this.email = new EmailResource(http);\n this.templates = new TemplatesResource(http);\n this.suppressions = new SuppressionsResource(http);\n this.webhooks = new WebhooksResource(http);\n this.dashboard = new DashboardResource(http);\n }\n\n /**\n * Verify a webhook signature using HMAC-SHA256.\n * Use this in your webhook handler to validate incoming requests.\n */\n static verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): boolean {\n // Dynamic import to keep browser-compatible at the type level\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const crypto = require('crypto') as typeof import('crypto');\n const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n try {\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resources/email.ts","../src/resources/templates.ts","../src/resources/suppressions.ts","../src/resources/webhooks.ts","../src/resources/dashboard.ts","../src/client.ts"],"sourcesContent":["export class OutboundError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly details?: unknown,\n public readonly requestId?: string,\n ) {\n super(message);\n this.name = 'OutboundError';\n }\n}\n\nexport class BadRequestError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 400, details, requestId);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 401, details, requestId);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class ForbiddenError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 403, details, requestId);\n this.name = 'ForbiddenError';\n }\n}\n\nexport class NotFoundError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 404, details, requestId);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends OutboundError {\n constructor(message: string, details?: unknown, requestId?: string) {\n super(message, 409, details, requestId);\n this.name = 'ConflictError';\n }\n}\n\nexport class RateLimitError extends OutboundError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number, details?: unknown, requestId?: string) {\n super(message, 429, details, requestId);\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\nexport class ServerError extends OutboundError {\n constructor(message: string, statusCode: number = 500, details?: unknown, requestId?: string) {\n super(message, statusCode, details, requestId);\n this.name = 'ServerError';\n }\n}\n\nexport class TimeoutError extends OutboundError {\n constructor(message: string = 'Request timed out') {\n super(message, 0);\n this.name = 'TimeoutError';\n }\n}\n\nexport class NetworkError extends OutboundError {\n constructor(message: string = 'Network request failed') {\n super(message, 0);\n this.name = 'NetworkError';\n }\n}\n","import type { ResolvedConfig } from './types';\nimport {\n OutboundError,\n BadRequestError,\n AuthenticationError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n RateLimitError,\n ServerError,\n TimeoutError,\n NetworkError,\n} from './errors';\n\nexport interface RequestOptions {\n body?: unknown;\n params?: Record<string, unknown>;\n}\n\nexport class HttpClient {\n constructor(private config: ResolvedConfig) {}\n\n async get<T>(path: string, params?: Record<string, unknown>, apiKey?: string): Promise<T> {\n return this.request<T>('GET', path, { params }, apiKey);\n }\n\n async post<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('POST', path, { body }, apiKey);\n }\n\n async patch<T>(path: string, body?: unknown, apiKey?: string): Promise<T> {\n return this.request<T>('PATCH', path, { body }, apiKey);\n }\n\n async delete<T>(path: string, apiKey?: string): Promise<T> {\n return this.request<T>('DELETE', path, undefined, apiKey);\n }\n\n private resolveApiKey(apiKey?: string): string {\n const key = apiKey || this.config.apiKey;\n if (!key) {\n throw new AuthenticationError(\n 'API key is required. Pass it to the constructor or provide it per method call.',\n );\n }\n return key;\n }\n\n private async request<T>(method: string, path: string, options?: RequestOptions, apiKey?: string): Promise<T> {\n const resolvedKey = this.resolveApiKey(apiKey);\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const url = this.buildUrl(path, options?.params);\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'X-Api-Key': resolvedKey,\n 'Content-Type': 'application/json',\n },\n body: options?.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n const error = await this.parseError(response);\n\n // Only retry on 429 and 5xx\n if (response.status === 429 || response.status >= 500) {\n lastError = error;\n\n if (attempt < this.config.maxRetries) {\n const retryAfter = error instanceof RateLimitError && error.retryAfter\n ? error.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n\n await this.sleep(retryAfter);\n continue;\n }\n }\n\n throw error;\n } catch (err) {\n if (err instanceof OutboundError) {\n // Already a typed error from parseError — check if retryable\n if ((err.statusCode === 429 || err.statusCode >= 500) && attempt < this.config.maxRetries) {\n lastError = err;\n const retryAfter = err instanceof RateLimitError && err.retryAfter\n ? err.retryAfter * 1000\n : this.config.retryDelay * Math.pow(2, attempt);\n await this.sleep(retryAfter);\n continue;\n }\n throw err;\n }\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n lastError = new TimeoutError(`Request timed out after ${this.config.timeout}ms`);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n\n lastError = new NetworkError((err as Error).message);\n if (attempt < this.config.maxRetries) {\n await this.sleep(this.config.retryDelay * Math.pow(2, attempt));\n continue;\n }\n throw lastError;\n }\n }\n\n throw lastError || new NetworkError('Request failed after retries');\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = this.config.baseUrl.replace(/\\/+$/, '');\n const url = new URL(`${base}${path}`);\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n }\n\n private async parseError(response: Response): Promise<OutboundError> {\n let body: { error?: string; message?: string; details?: unknown } = {};\n const requestId = response.headers.get('x-request-id') || undefined;\n\n try {\n body = (await response.json()) as typeof body;\n } catch {\n // Response may not be JSON\n }\n\n const message = body.error || body.message || `HTTP ${response.status}`;\n const details = body.details;\n\n switch (response.status) {\n case 400:\n return new BadRequestError(message, details, requestId);\n case 401:\n return new AuthenticationError(message, details, requestId);\n case 403:\n return new ForbiddenError(message, details, requestId);\n case 404:\n return new NotFoundError(message, details, requestId);\n case 409:\n return new ConflictError(message, details, requestId);\n case 429: {\n const retryAfter = response.headers.get('retry-after');\n return new RateLimitError(\n message,\n retryAfter ? parseInt(retryAfter, 10) : undefined,\n details,\n requestId,\n );\n }\n default:\n if (response.status >= 500) {\n return new ServerError(message, response.status, details, requestId);\n }\n return new OutboundError(message, response.status, details, requestId);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n SendEmailParams,\n SendEmailResponse,\n BulkEmailParams,\n BulkEmailResponse,\n JobStatusResponse,\n CancelEmailParams,\n CancelEmailResponse,\n RequestOverrides,\n} from '../types';\n\nexport class EmailResource {\n constructor(private http: HttpClient) {}\n\n async send(params: SendEmailParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email/send', params, overrides?.apiKey);\n }\n\n async bulk(params: BulkEmailParams, overrides?: RequestOverrides): Promise<BulkEmailResponse> {\n return this.http.post<BulkEmailResponse>('/v1/email/bulk', params, overrides?.apiKey);\n }\n\n async status(jobId: string, overrides?: RequestOverrides): Promise<JobStatusResponse> {\n return this.http.get<JobStatusResponse>(`/v1/email/status/${encodeURIComponent(jobId)}`, undefined, overrides?.apiKey);\n }\n\n /**\n * Cancel all queued/processing emails for a campaign or job.\n * Provide either `campaignId` (cancels all jobs in that campaign) or `jobId` (cancels one job).\n * Already-sent emails cannot be recalled — only recipients still in `queued` or `processing` state are cancelled.\n */\n async cancel(params: CancelEmailParams, overrides?: RequestOverrides): Promise<CancelEmailResponse> {\n if (!params.campaignId && !params.jobId) {\n throw new Error('cancel() requires either campaignId or jobId');\n }\n return this.http.post<CancelEmailResponse>('/v1/email/cancel', params, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateTemplateParams,\n UpdateTemplateParams,\n ListTemplatesParams,\n ListTemplatesResponse,\n Template,\n TemplateResponse,\n TemplateSendParams,\n SendEmailResponse,\n TemplateBulkSendParams,\n TemplateBulkSendResponse,\n TemplatePreviewParams,\n TemplatePreviewResponse,\n TemplateStatsResponse,\n RequestOverrides,\n} from '../types';\n\nexport class TemplatesResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>('/v1/email-templates', params, overrides?.apiKey);\n }\n\n async list(params?: ListTemplatesParams, overrides?: RequestOverrides): Promise<ListTemplatesResponse> {\n return this.http.get<ListTemplatesResponse>('/v1/email-templates', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListTemplatesParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Template> {\n let page = 1;\n const limit = params?.limit || 20;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const template of result.templates) {\n yield template;\n }\n if (result.templates.length < limit) break;\n page++;\n }\n }\n\n async get(id: string, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.get<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateTemplateParams, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.patch<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/email-templates/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n\n async duplicate(id: string, params?: { name?: string }, overrides?: RequestOverrides): Promise<TemplateResponse> {\n return this.http.post<TemplateResponse>(`/v1/email-templates/${encodeURIComponent(id)}/duplicate`, params, overrides?.apiKey);\n }\n\n async preview(id: string, params?: TemplatePreviewParams, overrides?: RequestOverrides): Promise<TemplatePreviewResponse> {\n return this.http.post<TemplatePreviewResponse>(`/v1/email-templates/${encodeURIComponent(id)}/preview`, params, overrides?.apiKey);\n }\n\n async send(params: TemplateSendParams, overrides?: RequestOverrides): Promise<SendEmailResponse> {\n return this.http.post<SendEmailResponse>('/v1/email-templates/send', params, overrides?.apiKey);\n }\n\n async bulkSend(params: TemplateBulkSendParams, overrides?: RequestOverrides): Promise<TemplateBulkSendResponse> {\n return this.http.post<TemplateBulkSendResponse>('/v1/email-templates/bulk', params, overrides?.apiKey);\n }\n\n async stats(overrides?: RequestOverrides): Promise<TemplateStatsResponse> {\n return this.http.get<TemplateStatsResponse>('/v1/email-templates/stats', undefined, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n ListSuppressionsParams,\n ListSuppressionsResponse,\n AddSuppressionParams,\n SuppressionResponse,\n Suppression,\n RequestOverrides,\n} from '../types';\n\nexport class SuppressionsResource {\n constructor(private http: HttpClient) {}\n\n async list(params?: ListSuppressionsParams, overrides?: RequestOverrides): Promise<ListSuppressionsResponse> {\n return this.http.get<ListSuppressionsResponse>('/v1/tenants/suppressions', params as Record<string, unknown>, overrides?.apiKey);\n }\n\n async *listAll(params?: Omit<ListSuppressionsParams, 'page'>, overrides?: RequestOverrides): AsyncGenerator<Suppression> {\n let page = 1;\n const limit = params?.limit || 50;\n\n while (true) {\n const result = await this.list({ ...params, page, limit }, overrides);\n for (const suppression of result.suppressions) {\n yield suppression;\n }\n if (result.suppressions.length < limit) break;\n page++;\n }\n }\n\n async add(params: AddSuppressionParams, overrides?: RequestOverrides): Promise<SuppressionResponse> {\n return this.http.post<SuppressionResponse>('/v1/tenants/suppressions', params, overrides?.apiKey);\n }\n\n async remove(email: string, overrides?: RequestOverrides): Promise<{ message: string }> {\n return this.http.delete<{ message: string }>(`/v1/tenants/suppressions/${encodeURIComponent(email)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type {\n CreateWebhookParams,\n CreateWebhookResponse,\n UpdateWebhookParams,\n UpdateWebhookResponse,\n ListWebhooksResponse,\n RequestOverrides,\n} from '../types';\n\nexport class WebhooksResource {\n constructor(private http: HttpClient) {}\n\n async create(params: CreateWebhookParams, overrides?: RequestOverrides): Promise<CreateWebhookResponse> {\n return this.http.post<CreateWebhookResponse>('/v1/tenants/webhooks', params, overrides?.apiKey);\n }\n\n async list(overrides?: RequestOverrides): Promise<ListWebhooksResponse> {\n return this.http.get<ListWebhooksResponse>('/v1/tenants/webhooks', undefined, overrides?.apiKey);\n }\n\n async update(id: string, params: UpdateWebhookParams, overrides?: RequestOverrides): Promise<UpdateWebhookResponse> {\n return this.http.patch<UpdateWebhookResponse>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, params, overrides?.apiKey);\n }\n\n async delete(id: string, overrides?: RequestOverrides): Promise<{ message: string; id: string }> {\n return this.http.delete<{ message: string; id: string }>(`/v1/tenants/webhooks/${encodeURIComponent(id)}`, overrides?.apiKey);\n }\n}\n","import type { HttpClient } from '../http';\nimport type { DashboardResponse, QuotaResponse, RequestOverrides } from '../types';\n\nexport class DashboardResource {\n constructor(private http: HttpClient) {}\n\n async get(overrides?: RequestOverrides): Promise<DashboardResponse> {\n return this.http.get<DashboardResponse>('/v1/tenants/dashboard', undefined, overrides?.apiKey);\n }\n\n async quota(overrides?: RequestOverrides): Promise<QuotaResponse> {\n return this.http.get<QuotaResponse>('/v1/tenants/quota', undefined, overrides?.apiKey);\n }\n}\n","import { HttpClient } from './http';\nimport { EmailResource } from './resources/email';\nimport { TemplatesResource } from './resources/templates';\nimport { SuppressionsResource } from './resources/suppressions';\nimport { WebhooksResource } from './resources/webhooks';\nimport { DashboardResource } from './resources/dashboard';\nimport type { OutboundConfig, ResolvedConfig } from './types';\n\nconst BASE_URL = 'https://outbound-api.mastersunion.org';\n\nexport class Outbound {\n readonly email: EmailResource;\n readonly templates: TemplatesResource;\n readonly suppressions: SuppressionsResource;\n readonly webhooks: WebhooksResource;\n readonly dashboard: DashboardResource;\n\n constructor(config?: OutboundConfig) {\n const resolved: ResolvedConfig = {\n apiKey: config?.apiKey,\n baseUrl: config?.baseUrl || BASE_URL,\n timeout: config?.timeout ?? 30_000,\n maxRetries: config?.maxRetries ?? 3,\n retryDelay: config?.retryDelay ?? 1000,\n };\n\n const http = new HttpClient(resolved);\n\n this.email = new EmailResource(http);\n this.templates = new TemplatesResource(http);\n this.suppressions = new SuppressionsResource(http);\n this.webhooks = new WebhooksResource(http);\n this.dashboard = new DashboardResource(http);\n }\n\n /**\n * Verify a webhook signature using HMAC-SHA256.\n * Use this in your webhook handler to validate incoming requests.\n */\n static verifyWebhookSignature(\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): boolean {\n // Dynamic import to keep browser-compatible at the type level\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const crypto = require('crypto') as typeof import('crypto');\n const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex');\n try {\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n } catch {\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
|