mailerbot 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +290 -0
- package/dist/index.cjs +852 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +737 -0
- package/dist/index.d.ts +737 -0
- package/dist/index.js +842 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MailerBot
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# MailerBot Node.js SDK
|
|
2
|
+
|
|
3
|
+
Official TypeScript/JavaScript SDK for the [MailerBot](https://mailerbot.com) direct mail API. Send letters and postcards programmatically.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install mailerbot
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Node.js 18+ (uses native `fetch`).
|
|
12
|
+
|
|
13
|
+
## Authentication
|
|
14
|
+
|
|
15
|
+
Generate an API key in your [MailerBot dashboard](https://app.mailerbot.com/settings/api-keys), then pass it to the client:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { MailerBot } from "mailerbot";
|
|
19
|
+
|
|
20
|
+
const client = new MailerBot("mb_live_...");
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { MailerBot } from "mailerbot";
|
|
27
|
+
|
|
28
|
+
const client = new MailerBot("mb_live_...");
|
|
29
|
+
|
|
30
|
+
// List contacts
|
|
31
|
+
const page = await client.contacts.list({ page: 1, pageSize: 25 });
|
|
32
|
+
console.log(`${page.total} total contacts`);
|
|
33
|
+
for (const contact of page.items) {
|
|
34
|
+
console.log(` ${contact.firstName} ${contact.lastName}, ${contact.city}, ${contact.state}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Dashboard stats
|
|
38
|
+
const stats = await client.dashboard.stats();
|
|
39
|
+
console.log(`Letters sent: ${stats.lettersSent}`);
|
|
40
|
+
console.log(`Total spent: $${stats.totalSpent.toFixed(2)}`);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Common Workflows
|
|
44
|
+
|
|
45
|
+
### Create and send a letter mailing
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { MailerBot } from "mailerbot";
|
|
49
|
+
|
|
50
|
+
const client = new MailerBot("mb_live_...");
|
|
51
|
+
|
|
52
|
+
// 1. Create a contact list
|
|
53
|
+
const contactList = await client.contactLists.create("My Campaign List");
|
|
54
|
+
|
|
55
|
+
// 2. Add contacts (countryCode defaults to "US" if omitted)
|
|
56
|
+
const contact = await client.contacts.create({
|
|
57
|
+
firstName: "Jane",
|
|
58
|
+
lastName: "Smith",
|
|
59
|
+
addressLine1: "123 Main St",
|
|
60
|
+
city: "Austin",
|
|
61
|
+
state: "TX",
|
|
62
|
+
zip: "78701",
|
|
63
|
+
});
|
|
64
|
+
await client.contactLists.addContacts(contactList.id, [contact.id]);
|
|
65
|
+
|
|
66
|
+
// 3. Write a letter document
|
|
67
|
+
const doc = await client.documents.create({
|
|
68
|
+
title: "Spring Promo Letter",
|
|
69
|
+
content: "<p>Dear {{first_name}},</p><p>Check out our spring deals!</p>",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// 4. Create the mailing (postage defaults to cheapest rate per zone)
|
|
73
|
+
const mailing = await client.mailings.create({
|
|
74
|
+
name: "Spring 2026 Promo",
|
|
75
|
+
type: "letter",
|
|
76
|
+
contactListId: contactList.id,
|
|
77
|
+
documentId: doc.id,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 5. Review the cost
|
|
81
|
+
const cost = await client.mailings.calculateCost(mailing.id);
|
|
82
|
+
console.log(`Product cost: $${cost.totalProductCost.toFixed(2)} (${cost.recipientCount} recipients)`);
|
|
83
|
+
for (const zone of cost.zoneCounts) {
|
|
84
|
+
console.log(` ${zone.postageZoneName}: ${zone.recipientCount} recipients`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 6. Create a Stripe payment intent, complete payment on your end, then send
|
|
88
|
+
const intent = await client.payments.createPaymentIntent(mailing.id);
|
|
89
|
+
// ... complete Stripe payment using intent.clientSecret ...
|
|
90
|
+
|
|
91
|
+
// 7. Send
|
|
92
|
+
const sent = await client.mailings.send(mailing.id);
|
|
93
|
+
console.log(`Mailing status: ${sent.status}`);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Bulk import contacts from CSV data
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const result = await client.contacts.importCsv(
|
|
100
|
+
[
|
|
101
|
+
{ firstName: "Alice", lastName: "Wu", addressLine1: "456 Oak Ave",
|
|
102
|
+
city: "Dallas", state: "TX", zip: "75201" },
|
|
103
|
+
{ firstName: "Bob", lastName: "Smith", addressLine1: "789 Pine Rd",
|
|
104
|
+
city: "Houston", state: "TX", zip: "77001" },
|
|
105
|
+
],
|
|
106
|
+
{ listName: "Imported List" },
|
|
107
|
+
);
|
|
108
|
+
console.log(`Imported ${result.importedCount} contacts into list ${result.listId}`);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### International contacts
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// List available countries
|
|
115
|
+
const countries = await client.pricing.countries();
|
|
116
|
+
for (const c of countries) {
|
|
117
|
+
console.log(` ${c.code} — ${c.name} (active: ${c.isActive})`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Create a Canadian contact
|
|
121
|
+
const contact = await client.contacts.create({
|
|
122
|
+
firstName: "Marie",
|
|
123
|
+
lastName: "Tremblay",
|
|
124
|
+
addressLine1: "350 Rue Saint-Paul",
|
|
125
|
+
city: "Montréal",
|
|
126
|
+
state: "QC",
|
|
127
|
+
zip: "H2Y 1H2",
|
|
128
|
+
countryCode: "CA",
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Estimate cost before creating a mailing
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const cost = await client.mailings.estimateCost({
|
|
136
|
+
type: "postcard",
|
|
137
|
+
contactListId: "<list_id>",
|
|
138
|
+
});
|
|
139
|
+
console.log(`Product cost: $${cost.totalProductCost.toFixed(2)}`);
|
|
140
|
+
for (const zone of cost.zoneCounts) {
|
|
141
|
+
console.log(` ${zone.postageZoneName}: ${zone.recipientCount} recipients`);
|
|
142
|
+
}
|
|
143
|
+
for (const u of cost.unavailable) {
|
|
144
|
+
console.log(` ⚠ ${u.countryName}: ${u.recipientCount} recipients (${u.reason})`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// See available postage rates
|
|
148
|
+
const rates = await client.pricing.postageRates({ productType: "postcard" });
|
|
149
|
+
for (const r of rates) {
|
|
150
|
+
console.log(` ${r.postageZoneName} — ${r.label}: $${r.costPerPiece}`);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Iterate over all contacts (auto-pagination)
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
for await (const contact of client.contacts.iterAll({ pageSize: 100 })) {
|
|
158
|
+
console.log(contact.firstName, contact.lastName);
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Send a postcard mailing
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
// Browse available templates
|
|
166
|
+
const templates = await client.postcards.listTemplates();
|
|
167
|
+
console.log(`${templates.length} templates available`);
|
|
168
|
+
|
|
169
|
+
// Create a postcard from scratch (or use the canvas builder in the dashboard)
|
|
170
|
+
const postcard = await client.postcards.create({ title: "Summer Sale Card" });
|
|
171
|
+
|
|
172
|
+
const mailing = await client.mailings.create({
|
|
173
|
+
name: "Summer Postcard Drop",
|
|
174
|
+
type: "postcard",
|
|
175
|
+
contactListId: "<list_id>",
|
|
176
|
+
postcardId: postcard.id,
|
|
177
|
+
});
|
|
178
|
+
const cost = await client.mailings.calculateCost(mailing.id);
|
|
179
|
+
console.log(`$${cost.totalProductCost.toFixed(2)} for ${cost.recipientCount} postcards`);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Track QR code scans
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
// Create a trackable short link
|
|
186
|
+
const link = await client.qr.create({ destinationUrl: "https://yoursite.com/promo" });
|
|
187
|
+
console.log(`Short URL: ${link.shortUrl}`);
|
|
188
|
+
|
|
189
|
+
// Get analytics
|
|
190
|
+
const analytics = await client.qr.analytics({ days: 30 });
|
|
191
|
+
console.log(`${analytics.totalScans} scans across ${analytics.uniqueLinks} links`);
|
|
192
|
+
for (const day of analytics.scansByDay) {
|
|
193
|
+
console.log(` ${day.date}: ${day.count} scans`);
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Track USPS delivery per piece
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
const mailing = await client.mailings.get("<mailing_id>");
|
|
201
|
+
console.log(`${mailing.itemsDelivered}/${mailing.itemCount} delivered, ${mailing.itemsReturned} returned`);
|
|
202
|
+
|
|
203
|
+
// Pieces USPS flagged as return-to-sender (filter: none, in_transit,
|
|
204
|
+
// out_for_delivery, forwarded, delivered, returned)
|
|
205
|
+
for await (const item of client.mailings.iterItems(mailing.id, { trackingStatus: "returned" })) {
|
|
206
|
+
console.log(`${item.recipientName}: ${item.lastScanLabel} at ${item.lastScanLocation}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Full scan history for one piece
|
|
210
|
+
const page = await client.mailings.listItems(mailing.id, { pageSize: 1 });
|
|
211
|
+
for (const scan of await client.mailings.listItemScans(mailing.id, page.items[0].id)) {
|
|
212
|
+
console.log(` ${scan.scanDatetime} ${scan.label} (${scan.facilityCity}, ${scan.facilityState})`);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Or subscribe to the `mail_delivered` and `mail_returned` webhook events to be pushed these updates instead of polling.
|
|
217
|
+
|
|
218
|
+
### Use coupon codes in mailings
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
// Create a coupon list and import codes
|
|
222
|
+
const couponList = await client.coupons.create("Spring Sale Coupons");
|
|
223
|
+
const result = await client.coupons.importCodes(couponList.id, ["SAVE10", "SAVE20", "SAVE30"]);
|
|
224
|
+
console.log(`Imported ${result.importedCount} codes`);
|
|
225
|
+
|
|
226
|
+
// Check there are enough codes before sending
|
|
227
|
+
const avail = await client.coupons.checkAvailability(couponList.id, 500);
|
|
228
|
+
if (!avail.sufficient) {
|
|
229
|
+
console.log(`Only ${avail.availableCodes} codes available, need 500`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Attach to a mailing — each recipient gets a unique code
|
|
233
|
+
const mailing = await client.mailings.create({
|
|
234
|
+
name: "Spring Promo",
|
|
235
|
+
type: "letter",
|
|
236
|
+
contactListId: "<list_id>",
|
|
237
|
+
documentId: "<doc_id>",
|
|
238
|
+
couponListId: couponList.id,
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Error Handling
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
import { MailerBot, AuthenticationError, NotFoundError, ValidationError, MailerBotError } from "mailerbot";
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const client = new MailerBot("bad_key");
|
|
249
|
+
await client.contacts.list();
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (error instanceof AuthenticationError) {
|
|
252
|
+
console.log(`Auth failed: ${error.message}`);
|
|
253
|
+
} else if (error instanceof NotFoundError) {
|
|
254
|
+
console.log(`Resource not found: ${error.message}`);
|
|
255
|
+
} else if (error instanceof ValidationError) {
|
|
256
|
+
console.log(`Bad request: ${error.message}`, error.response);
|
|
257
|
+
} else if (error instanceof MailerBotError) {
|
|
258
|
+
console.log(`API error ${error.statusCode}: ${error.message}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Exception hierarchy
|
|
264
|
+
|
|
265
|
+
| Error class | HTTP status |
|
|
266
|
+
|---|---|
|
|
267
|
+
| `AuthenticationError` | 401 |
|
|
268
|
+
| `PermissionError` | 403 |
|
|
269
|
+
| `NotFoundError` | 404 |
|
|
270
|
+
| `ValidationError` | 422 |
|
|
271
|
+
| `RateLimitError` | 429 |
|
|
272
|
+
| `ServerError` | 5xx |
|
|
273
|
+
| `MailerBotError` | base class / other |
|
|
274
|
+
|
|
275
|
+
## Configuration
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
const client = new MailerBot("mb_live_...", {
|
|
279
|
+
baseUrl: "https://api.mailerbot.com/api/v1", // default
|
|
280
|
+
timeout: 30_000, // milliseconds, default 30000
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Full API Reference
|
|
285
|
+
|
|
286
|
+
See [https://mailerbot.com/docs](https://mailerbot.com/docs) for complete endpoint documentation.
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|