retransmit.dev 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -4
- package/dist/index.cjs +76 -17
- package/dist/index.d.cts +116 -9
- package/dist/index.d.ts +116 -9
- package/dist/index.js +75 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,6 +101,80 @@ like `From`, `To`, `Subject`, `Date` or `Message-ID`, are rejected with a
|
|
|
101
101
|
headers even if you pass your own. Headers come back on `emails.get`, and
|
|
102
102
|
batch emails accept the same field.
|
|
103
103
|
|
|
104
|
+
### Attachments
|
|
105
|
+
|
|
106
|
+
Attach up to 20 files, 30 MB in total. Pass the bytes as `content` (a
|
|
107
|
+
`Buffer`, `Uint8Array` or base64 string), or a public URL as `path` and
|
|
108
|
+
Retransmit fetches it while the request runs. Either way the file travels
|
|
109
|
+
inside the email.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { readFile } from "node:fs/promises";
|
|
113
|
+
|
|
114
|
+
await retransmit.emails.send({
|
|
115
|
+
from: "Acme <billing@yourdomain.com>",
|
|
116
|
+
to: "user@example.com",
|
|
117
|
+
subject: "Your invoice",
|
|
118
|
+
html: "<p>Your invoice is attached.</p>",
|
|
119
|
+
attachments: [
|
|
120
|
+
{ filename: "invoice.pdf", content: await readFile("./invoice.pdf") },
|
|
121
|
+
{ filename: "terms.pdf", path: "https://yourdomain.com/terms.pdf" },
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
To embed an image in the HTML, give it a `contentId` and reference it as
|
|
127
|
+
`cid:`:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
await retransmit.emails.send({
|
|
131
|
+
from: "Acme <hello@yourdomain.com>",
|
|
132
|
+
to: "user@example.com",
|
|
133
|
+
subject: "Welcome",
|
|
134
|
+
html: '<p><img src="cid:logo" alt="Acme" /> Glad to have you.</p>',
|
|
135
|
+
attachments: [{ filename: "logo.png", path: "https://yourdomain.com/logo.png", contentId: "logo" }],
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Executables, scripts and installers are rejected with `invalid_attachment`,
|
|
140
|
+
and `batch.send` does not accept attachments. Files are kept for 30 days so
|
|
141
|
+
you can see what went out:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
const { data } = await retransmit.emails.attachments("em_xxxxxxxxxxxx");
|
|
145
|
+
// data.attachments: [{ id, filename, content_type, size, download_url, expires_at, ... }]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`download_url` is signed and valid for one hour; it is `null` once the file
|
|
149
|
+
has expired.
|
|
150
|
+
|
|
151
|
+
### Idempotency
|
|
152
|
+
|
|
153
|
+
A send can succeed on the server and still fail on your side, through a
|
|
154
|
+
timeout or a dropped connection. Retrying it blindly sends the email twice.
|
|
155
|
+
Pass an `idempotencyKey` and retries become safe: for 24 hours the same key
|
|
156
|
+
with the same payload returns the original response, `id` included, and
|
|
157
|
+
nothing is queued again.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
await retransmit.emails.send(
|
|
161
|
+
{
|
|
162
|
+
from: "Acme <hello@yourdomain.com>",
|
|
163
|
+
to: "user@example.com",
|
|
164
|
+
subject: "Welcome to Acme",
|
|
165
|
+
html: "<p>Glad to have you.</p>",
|
|
166
|
+
},
|
|
167
|
+
{ idempotencyKey: "welcome-user/123" },
|
|
168
|
+
);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Use a value that identifies that exact email, such as a UUID or
|
|
172
|
+
`<event>/<entity-id>`. Keys are 1 to 256 characters and are shared by every
|
|
173
|
+
API key in your organization. The same key with a different payload returns
|
|
174
|
+
`invalid_idempotent_request`; a retry that overlaps the first request returns
|
|
175
|
+
`concurrent_idempotent_requests`, so wait a moment and try again. Batches
|
|
176
|
+
accept the same option with one key for the whole batch.
|
|
177
|
+
|
|
104
178
|
### List and filter emails
|
|
105
179
|
|
|
106
180
|
`emails.list` returns your emails newest first. Every tag you pass must match.
|
|
@@ -155,15 +229,59 @@ const { data } = await retransmit.sms.get("sms_xxxxxxxxxxxx");
|
|
|
155
229
|
console.log(data?.status); // "sent" | "delivered" | "undelivered" | ...
|
|
156
230
|
```
|
|
157
231
|
|
|
232
|
+
### Sender IDs
|
|
233
|
+
|
|
234
|
+
`from` is the name shown on the handset instead of a phone number. Approval is
|
|
235
|
+
per country, so request it first in the dashboard under **SMS > Sender IDs**.
|
|
236
|
+
The request asks for the name and the countries you send to. Most countries
|
|
237
|
+
accept the name as it is; where the carriers require a registration, the form
|
|
238
|
+
adds what that filing needs (what you send, a sample message, your legal
|
|
239
|
+
entity). Retransmit files it for you, and there is no AWS or carrier account to
|
|
240
|
+
set up.
|
|
241
|
+
|
|
242
|
+
Sending with a name that is not approved for the destination country fails with
|
|
243
|
+
`sender_not_allowed`. Leave `from` out to use your approved sender for that
|
|
244
|
+
country, or the provider default when you have none.
|
|
245
|
+
|
|
246
|
+
The United States, Canada, and Mexico do not accept alphanumeric sender IDs.
|
|
247
|
+
|
|
248
|
+
### Choosing a carrier
|
|
249
|
+
|
|
250
|
+
You do not have to work out which carrier fits a number. Leave `provider` out
|
|
251
|
+
and Retransmit routes by destination country and price.
|
|
252
|
+
|
|
253
|
+
Pass it to pin the send to one carrier. `sns` (AWS End User Messaging) is the
|
|
254
|
+
one that reaches every destination; `mtn` and `orange` only cover the countries
|
|
255
|
+
Retransmit has that carrier in:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
await retransmit.sms.send({
|
|
259
|
+
to: "+237670000000",
|
|
260
|
+
text: "Your verification code is 482913",
|
|
261
|
+
provider: "sns", // "sns" | "mtn" | "orange"
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The value is the carrier, not one of its country operations, so it keeps
|
|
266
|
+
working as more countries are added. A pinned send never falls back: if that
|
|
267
|
+
carrier cannot deliver to the destination, the request fails with `no_route`.
|
|
268
|
+
|
|
269
|
+
`get` returns both. `requested_provider` is what you asked for, `provider` is
|
|
270
|
+
the country operation that carried the message (`mtn_cm`, `orange_cm`,
|
|
271
|
+
`aws_sns`), and is null until the message is routed.
|
|
272
|
+
|
|
158
273
|
## Email batches
|
|
159
274
|
|
|
160
275
|
Send up to 10,000 emails in one request:
|
|
161
276
|
|
|
162
277
|
```ts
|
|
163
|
-
const { data: batch } = await retransmit.batch.send(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
278
|
+
const { data: batch } = await retransmit.batch.send(
|
|
279
|
+
[
|
|
280
|
+
{ from: "Acme <hello@yourdomain.com>", to: "a@example.com", subject: "Hi", text: "Hello A" },
|
|
281
|
+
{ from: "Acme <hello@yourdomain.com>", to: "b@example.com", subject: "Hi", text: "Hello B" },
|
|
282
|
+
],
|
|
283
|
+
{ idempotencyKey: "weekly-digest/2026-09-07" },
|
|
284
|
+
);
|
|
167
285
|
|
|
168
286
|
const { data: progress } = await retransmit.batch.get(batch!.id);
|
|
169
287
|
console.log(progress?.processed, "/", progress?.total, progress?.counts);
|
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
EMAIL_STATUSES: () => EMAIL_STATUSES,
|
|
25
25
|
Emails: () => Emails,
|
|
26
26
|
Retransmit: () => Retransmit,
|
|
27
|
+
SMS_PROVIDERS: () => SMS_PROVIDERS,
|
|
27
28
|
SMS_STATUSES: () => SMS_STATUSES,
|
|
28
29
|
Sms: () => Sms,
|
|
29
30
|
WHATSAPP_STATUSES: () => WHATSAPP_STATUSES,
|
|
@@ -32,6 +33,24 @@ __export(index_exports, {
|
|
|
32
33
|
module.exports = __toCommonJS(index_exports);
|
|
33
34
|
|
|
34
35
|
// src/emails.ts
|
|
36
|
+
function toBase64(bytes) {
|
|
37
|
+
if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
|
|
38
|
+
let binary = "";
|
|
39
|
+
const CHUNK = 32768;
|
|
40
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
41
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
42
|
+
}
|
|
43
|
+
return btoa(binary);
|
|
44
|
+
}
|
|
45
|
+
function toWireAttachment(attachment) {
|
|
46
|
+
return {
|
|
47
|
+
filename: attachment.filename,
|
|
48
|
+
content: attachment.content === void 0 || typeof attachment.content === "string" ? attachment.content : toBase64(attachment.content),
|
|
49
|
+
path: attachment.path,
|
|
50
|
+
content_type: attachment.contentType,
|
|
51
|
+
content_id: attachment.contentId
|
|
52
|
+
};
|
|
53
|
+
}
|
|
35
54
|
function toWirePayload(options) {
|
|
36
55
|
return {
|
|
37
56
|
from: options.from,
|
|
@@ -44,7 +63,8 @@ function toWirePayload(options) {
|
|
|
44
63
|
text: options.text,
|
|
45
64
|
marketing: options.marketing,
|
|
46
65
|
tags: options.tags,
|
|
47
|
-
headers: options.headers
|
|
66
|
+
headers: options.headers,
|
|
67
|
+
attachments: options.attachments?.map(toWireAttachment)
|
|
48
68
|
};
|
|
49
69
|
}
|
|
50
70
|
var Emails = class {
|
|
@@ -52,9 +72,18 @@ var Emails = class {
|
|
|
52
72
|
this.client = client;
|
|
53
73
|
}
|
|
54
74
|
client;
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Queues a single email. Poll `get(id)` or subscribe to webhooks for the
|
|
77
|
+
* outcome. Pass `{ idempotencyKey }` to make the call safe to retry.
|
|
78
|
+
*/
|
|
79
|
+
send(options, requestOptions) {
|
|
80
|
+
return this.client.request(
|
|
81
|
+
"POST",
|
|
82
|
+
"/v1/emails",
|
|
83
|
+
toWirePayload(options),
|
|
84
|
+
void 0,
|
|
85
|
+
requestOptions
|
|
86
|
+
);
|
|
58
87
|
}
|
|
59
88
|
/** Retrieves an email with its current status and event history. */
|
|
60
89
|
get(id) {
|
|
@@ -77,6 +106,21 @@ var Emails = class {
|
|
|
77
106
|
tags() {
|
|
78
107
|
return this.client.request("GET", "/v1/emails/tags");
|
|
79
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The attachments of an email, each with a signed `download_url` valid for
|
|
111
|
+
* one hour. Files are kept for 30 days after the send; after that the link
|
|
112
|
+
* is `null` and only the metadata remains.
|
|
113
|
+
*/
|
|
114
|
+
attachments(emailId) {
|
|
115
|
+
return this.client.request("GET", `/v1/emails/${encodeURIComponent(emailId)}/attachments`);
|
|
116
|
+
}
|
|
117
|
+
/** One attachment of an email with a signed download link. */
|
|
118
|
+
getAttachment(emailId, attachmentId) {
|
|
119
|
+
return this.client.request(
|
|
120
|
+
"GET",
|
|
121
|
+
`/v1/emails/${encodeURIComponent(emailId)}/attachments/${encodeURIComponent(attachmentId)}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
80
124
|
};
|
|
81
125
|
|
|
82
126
|
// src/batch.ts
|
|
@@ -85,11 +129,19 @@ var Batch = class {
|
|
|
85
129
|
this.client = client;
|
|
86
130
|
}
|
|
87
131
|
client;
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Queues up to 10,000 emails in one request. Track progress with `get(id)`.
|
|
134
|
+
* Pass `{ idempotencyKey }` covering the whole batch to make the call safe
|
|
135
|
+
* to retry.
|
|
136
|
+
*/
|
|
137
|
+
send(emails, requestOptions) {
|
|
138
|
+
return this.client.request(
|
|
139
|
+
"POST",
|
|
140
|
+
"/v1/emails/batch",
|
|
141
|
+
{ emails: emails.map(toWirePayload) },
|
|
142
|
+
void 0,
|
|
143
|
+
requestOptions
|
|
144
|
+
);
|
|
93
145
|
}
|
|
94
146
|
/** Batch progress: how many emails are in each status so far. */
|
|
95
147
|
get(id) {
|
|
@@ -108,7 +160,8 @@ var Sms = class {
|
|
|
108
160
|
return this.client.request("POST", "/v1/sms", {
|
|
109
161
|
from: options.from,
|
|
110
162
|
to: options.to,
|
|
111
|
-
text: options.text
|
|
163
|
+
text: options.text,
|
|
164
|
+
provider: options.provider
|
|
112
165
|
});
|
|
113
166
|
}
|
|
114
167
|
/** Retrieves an SMS with its current status and event history. */
|
|
@@ -148,7 +201,7 @@ var Whatsapp = class {
|
|
|
148
201
|
|
|
149
202
|
// src/retransmit.ts
|
|
150
203
|
var DEFAULT_BASE_URL = "https://api.retransmit.dev";
|
|
151
|
-
var USER_AGENT = "retransmit.dev-node/0.
|
|
204
|
+
var USER_AGENT = "retransmit.dev-node/0.6.0";
|
|
152
205
|
function readEnv(name) {
|
|
153
206
|
return typeof process !== "undefined" ? process.env?.[name] : void 0;
|
|
154
207
|
}
|
|
@@ -173,7 +226,7 @@ var Retransmit = class {
|
|
|
173
226
|
);
|
|
174
227
|
}
|
|
175
228
|
/** Internal transport shared by the resource classes. API failures are returned, never thrown. */
|
|
176
|
-
async request(method, path, body, query) {
|
|
229
|
+
async request(method, path, body, query, options = {}) {
|
|
177
230
|
const params = new URLSearchParams();
|
|
178
231
|
for (const [key, value] of Object.entries(query ?? {})) {
|
|
179
232
|
if (value === void 0) continue;
|
|
@@ -181,15 +234,19 @@ var Retransmit = class {
|
|
|
181
234
|
}
|
|
182
235
|
const encoded = params.toString();
|
|
183
236
|
const search = encoded ? `?${encoded}` : "";
|
|
237
|
+
const headers = {
|
|
238
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
239
|
+
"Content-Type": "application/json",
|
|
240
|
+
"User-Agent": USER_AGENT
|
|
241
|
+
};
|
|
242
|
+
if (options.idempotencyKey !== void 0) {
|
|
243
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
244
|
+
}
|
|
184
245
|
let response;
|
|
185
246
|
try {
|
|
186
247
|
response = await fetch(`${this.baseUrl}${path}${search}`, {
|
|
187
248
|
method,
|
|
188
|
-
headers
|
|
189
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
190
|
-
"Content-Type": "application/json",
|
|
191
|
-
"User-Agent": USER_AGENT
|
|
192
|
-
},
|
|
249
|
+
headers,
|
|
193
250
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
194
251
|
});
|
|
195
252
|
} catch (cause) {
|
|
@@ -245,6 +302,7 @@ var SMS_STATUSES = [
|
|
|
245
302
|
"rejected",
|
|
246
303
|
"failed"
|
|
247
304
|
];
|
|
305
|
+
var SMS_PROVIDERS = ["sns", "mtn", "orange"];
|
|
248
306
|
var WHATSAPP_STATUSES = ["queued", "sent", "delivered", "read", "failed"];
|
|
249
307
|
// Annotate the CommonJS export names for ESM import in node:
|
|
250
308
|
0 && (module.exports = {
|
|
@@ -252,6 +310,7 @@ var WHATSAPP_STATUSES = ["queued", "sent", "delivered", "read", "failed"];
|
|
|
252
310
|
EMAIL_STATUSES,
|
|
253
311
|
Emails,
|
|
254
312
|
Retransmit,
|
|
313
|
+
SMS_PROVIDERS,
|
|
255
314
|
SMS_STATUSES,
|
|
256
315
|
Sms,
|
|
257
316
|
WHATSAPP_STATUSES,
|
package/dist/index.d.cts
CHANGED
|
@@ -20,6 +20,18 @@ interface RetransmitOptions {
|
|
|
20
20
|
/** Override the API origin. Defaults to `https://api.retransmit.dev` (or `RETRANSMIT_BASE_URL`). */
|
|
21
21
|
baseUrl?: string;
|
|
22
22
|
}
|
|
23
|
+
/** Per-request options, passed as the second argument of `emails.send` and `batch.send`. */
|
|
24
|
+
interface RequestOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Makes the request safe to retry. Sent as the `Idempotency-Key` header.
|
|
27
|
+
* For 24 hours, a retry with the same key and payload returns the original
|
|
28
|
+
* response instead of queuing the email again. 1 to 256 characters; a UUID
|
|
29
|
+
* or `<event>/<entity-id>` such as `welcome-user/123` works well. A retry
|
|
30
|
+
* with a different payload fails with `invalid_idempotent_request`, and one
|
|
31
|
+
* that overlaps the first request fails with `concurrent_idempotent_requests`.
|
|
32
|
+
*/
|
|
33
|
+
idempotencyKey?: string;
|
|
34
|
+
}
|
|
23
35
|
interface SendEmailOptions {
|
|
24
36
|
/** Sender, as `address@domain.com` or `Name <address@domain.com>`. The domain must be verified on your account. */
|
|
25
37
|
from: string;
|
|
@@ -58,8 +70,58 @@ interface SendEmailOptions {
|
|
|
58
70
|
* List-Unsubscribe headers take precedence over yours.
|
|
59
71
|
*/
|
|
60
72
|
headers?: EmailHeaders;
|
|
73
|
+
/**
|
|
74
|
+
* Files to attach, up to 20 per email and 30 MB in total. Each needs a
|
|
75
|
+
* `filename` and either `content` (the bytes) or `path` (a public URL
|
|
76
|
+
* fetched when you call `send`). Add `contentId` to embed an image inline.
|
|
77
|
+
* Not accepted by `batch.send`.
|
|
78
|
+
*/
|
|
79
|
+
attachments?: Attachment[];
|
|
61
80
|
}
|
|
62
81
|
type EmailHeaders = Record<string, string>;
|
|
82
|
+
/** A file to attach. Provide exactly one of `content` and `path`. */
|
|
83
|
+
interface Attachment {
|
|
84
|
+
/**
|
|
85
|
+
* Name the recipient sees, up to 255 characters, no path separators. Its
|
|
86
|
+
* extension sets the content type when `contentType` is omitted.
|
|
87
|
+
*/
|
|
88
|
+
filename: string;
|
|
89
|
+
/** The file: a `Buffer`, a `Uint8Array`, or an already base64 encoded string. */
|
|
90
|
+
content?: string | Uint8Array;
|
|
91
|
+
/**
|
|
92
|
+
* Public http(s) URL the file is fetched from while the request runs, with
|
|
93
|
+
* a 15 second budget. Private and internal hosts are refused.
|
|
94
|
+
*/
|
|
95
|
+
path?: string;
|
|
96
|
+
/** MIME type such as `application/pdf`. Inferred from the filename when omitted. */
|
|
97
|
+
contentType?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Embeds the file inline and sets its Content-ID. Reference it in `html`
|
|
100
|
+
* as `<img src="cid:the-id">`. Letters, digits, `.`, `_`, `@` and `-`, up
|
|
101
|
+
* to 128 characters, unique per email.
|
|
102
|
+
*/
|
|
103
|
+
contentId?: string;
|
|
104
|
+
}
|
|
105
|
+
/** An attachment as it was sent, without a download link. */
|
|
106
|
+
interface EmailAttachment {
|
|
107
|
+
id: string;
|
|
108
|
+
filename: string;
|
|
109
|
+
content_type: string;
|
|
110
|
+
/** Size in bytes of the decoded file. */
|
|
111
|
+
size: number;
|
|
112
|
+
content_id: string | null;
|
|
113
|
+
/** True for images embedded via `contentId`. */
|
|
114
|
+
inline: boolean;
|
|
115
|
+
}
|
|
116
|
+
interface EmailAttachmentWithDownload extends EmailAttachment {
|
|
117
|
+
/** When the stored file is deleted, 30 days after the send. */
|
|
118
|
+
expires_at: string;
|
|
119
|
+
/** Signed link to download the file, valid for one hour. `null` once the file has expired. */
|
|
120
|
+
download_url: string | null;
|
|
121
|
+
}
|
|
122
|
+
interface ListEmailAttachmentsResponse {
|
|
123
|
+
attachments: EmailAttachmentWithDownload[];
|
|
124
|
+
}
|
|
63
125
|
interface EmailTag {
|
|
64
126
|
name: string;
|
|
65
127
|
value: string;
|
|
@@ -91,6 +153,8 @@ interface GetEmailResponse {
|
|
|
91
153
|
created_at: string;
|
|
92
154
|
last_event_at: string | null;
|
|
93
155
|
events: EmailEvent[];
|
|
156
|
+
/** Attachments given at send time, without download links. See `emails.attachments`. */
|
|
157
|
+
attachments: EmailAttachment[];
|
|
94
158
|
}
|
|
95
159
|
interface ListEmailsOptions {
|
|
96
160
|
/**
|
|
@@ -136,16 +200,38 @@ interface ListEmailTagsResponse {
|
|
|
136
200
|
}
|
|
137
201
|
declare const SMS_STATUSES: readonly ["queued", "sent", "delivered", "undelivered", "expired", "rejected", "failed"];
|
|
138
202
|
type SmsStatus = (typeof SMS_STATUSES)[number];
|
|
203
|
+
/**
|
|
204
|
+
* Carriers a send can be pinned to. Carrier-level, not per-country: `mtn`
|
|
205
|
+
* covers every MTN network Retransmit integrates with. `sns` (AWS End User
|
|
206
|
+
* Messaging) is first because it is the one that reaches every destination.
|
|
207
|
+
*/
|
|
208
|
+
declare const SMS_PROVIDERS: readonly ["sns", "mtn", "orange"];
|
|
209
|
+
type SmsProvider = (typeof SMS_PROVIDERS)[number];
|
|
139
210
|
interface SendSmsOptions {
|
|
140
211
|
/**
|
|
141
212
|
* Sender id shown on the recipient's device (up to 11 characters:
|
|
142
|
-
* letters, digits, space, - and _).
|
|
143
|
-
*
|
|
213
|
+
* letters, digits, space, - and _).
|
|
214
|
+
*
|
|
215
|
+
* Approval is per country, so this must be a name your organization has had
|
|
216
|
+
* approved for the destination; request it in the dashboard under
|
|
217
|
+
* SMS > Sender IDs, which asks for the name and the countries, and for
|
|
218
|
+
* registration details only where the carriers require a filing. Sending
|
|
219
|
+
* with an unapproved name fails with `sender_not_allowed`. Leave it out to
|
|
220
|
+
* use your approved sender for that country, or the provider default when
|
|
221
|
+
* you have none.
|
|
144
222
|
*/
|
|
145
223
|
from?: string;
|
|
146
224
|
/** One recipient or up to 50, in international format (`+237670000000`). All must share one country. */
|
|
147
225
|
to: string | string[];
|
|
148
226
|
text: string;
|
|
227
|
+
/**
|
|
228
|
+
* Pins the send to one carrier. Leave it out to let Retransmit route by
|
|
229
|
+
* destination country and price. `sns` covers every destination; `mtn` and
|
|
230
|
+
* `orange` only the countries Retransmit has that carrier in. A pinned send
|
|
231
|
+
* never falls back to another carrier: it fails with `no_route` when that
|
|
232
|
+
* one cannot deliver.
|
|
233
|
+
*/
|
|
234
|
+
provider?: SmsProvider;
|
|
149
235
|
}
|
|
150
236
|
interface SendSmsResponse {
|
|
151
237
|
id: string;
|
|
@@ -167,7 +253,13 @@ interface GetSmsResponse {
|
|
|
167
253
|
text: string;
|
|
168
254
|
country: string | null;
|
|
169
255
|
segments: number;
|
|
170
|
-
/**
|
|
256
|
+
/** Carrier the send was pinned to, null when routing chose freely. */
|
|
257
|
+
requested_provider: SmsProvider | null;
|
|
258
|
+
/**
|
|
259
|
+
* Country operation that carried the message, e.g. `mtn_cm`, `orange_cm`
|
|
260
|
+
* or `aws_sns`. More specific than `requested_provider`, and null until
|
|
261
|
+
* the message is routed.
|
|
262
|
+
*/
|
|
171
263
|
provider: string | null;
|
|
172
264
|
status: SmsStatus;
|
|
173
265
|
error: string | null;
|
|
@@ -269,8 +361,12 @@ interface GetBatchResponse {
|
|
|
269
361
|
declare class Batch {
|
|
270
362
|
private readonly client;
|
|
271
363
|
constructor(client: Retransmit);
|
|
272
|
-
/**
|
|
273
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Queues up to 10,000 emails in one request. Track progress with `get(id)`.
|
|
366
|
+
* Pass `{ idempotencyKey }` covering the whole batch to make the call safe
|
|
367
|
+
* to retry.
|
|
368
|
+
*/
|
|
369
|
+
send(emails: SendEmailOptions[], requestOptions?: RequestOptions): Promise<Result<SendBatchResponse>>;
|
|
274
370
|
/** Batch progress: how many emails are in each status so far. */
|
|
275
371
|
get(id: string): Promise<Result<GetBatchResponse>>;
|
|
276
372
|
}
|
|
@@ -278,8 +374,11 @@ declare class Batch {
|
|
|
278
374
|
declare class Emails {
|
|
279
375
|
private readonly client;
|
|
280
376
|
constructor(client: Retransmit);
|
|
281
|
-
/**
|
|
282
|
-
|
|
377
|
+
/**
|
|
378
|
+
* Queues a single email. Poll `get(id)` or subscribe to webhooks for the
|
|
379
|
+
* outcome. Pass `{ idempotencyKey }` to make the call safe to retry.
|
|
380
|
+
*/
|
|
381
|
+
send(options: SendEmailOptions, requestOptions?: RequestOptions): Promise<Result<SendEmailResponse>>;
|
|
283
382
|
/** Retrieves an email with its current status and event history. */
|
|
284
383
|
get(id: string): Promise<Result<GetEmailResponse>>;
|
|
285
384
|
/**
|
|
@@ -289,6 +388,14 @@ declare class Emails {
|
|
|
289
388
|
list(options?: ListEmailsOptions): Promise<Result<ListEmailsResponse>>;
|
|
290
389
|
/** Every distinct tag on your emails, with a count of emails carrying it. */
|
|
291
390
|
tags(): Promise<Result<ListEmailTagsResponse>>;
|
|
391
|
+
/**
|
|
392
|
+
* The attachments of an email, each with a signed `download_url` valid for
|
|
393
|
+
* one hour. Files are kept for 30 days after the send; after that the link
|
|
394
|
+
* is `null` and only the metadata remains.
|
|
395
|
+
*/
|
|
396
|
+
attachments(emailId: string): Promise<Result<ListEmailAttachmentsResponse>>;
|
|
397
|
+
/** One attachment of an email with a signed download link. */
|
|
398
|
+
getAttachment(emailId: string, attachmentId: string): Promise<Result<EmailAttachmentWithDownload>>;
|
|
292
399
|
}
|
|
293
400
|
|
|
294
401
|
declare class Sms {
|
|
@@ -322,7 +429,7 @@ declare class Retransmit {
|
|
|
322
429
|
private readonly baseUrl;
|
|
323
430
|
constructor(apiKey?: string, options?: RetransmitOptions);
|
|
324
431
|
/** Internal transport shared by the resource classes. API failures are returned, never thrown. */
|
|
325
|
-
request<T>(method: "GET" | "POST", path: string, body?: unknown, query?: Record<string, string | number | string[] | undefined
|
|
432
|
+
request<T>(method: "GET" | "POST", path: string, body?: unknown, query?: Record<string, string | number | string[] | undefined>, options?: RequestOptions): Promise<Result<T>>;
|
|
326
433
|
}
|
|
327
434
|
|
|
328
|
-
export { Batch, EMAIL_STATUSES, type EmailEvent, type EmailStatus, type EmailSummary, type EmailTag, type EmailTagCount, Emails, type GetBatchResponse, type GetEmailResponse, type GetSmsResponse, type GetWhatsappResponse, type ListEmailTagsResponse, type ListEmailsOptions, type ListEmailsResponse, type Result, Retransmit, type RetransmitError, type RetransmitOptions, SMS_STATUSES, type SendBatchResponse, type SendEmailOptions, type SendEmailResponse, type SendSmsOptions, type SendSmsResponse, type SendWhatsappOptions, type SendWhatsappResponse, Sms, type SmsEvent, type SmsStatus, WHATSAPP_STATUSES, Whatsapp, type WhatsappDocument, type WhatsappEvent, type WhatsappMedia, type WhatsappMessageType, type WhatsappStatus, type WhatsappTemplate };
|
|
435
|
+
export { type Attachment, Batch, EMAIL_STATUSES, type EmailAttachment, type EmailAttachmentWithDownload, type EmailEvent, type EmailStatus, type EmailSummary, type EmailTag, type EmailTagCount, Emails, type GetBatchResponse, type GetEmailResponse, type GetSmsResponse, type GetWhatsappResponse, type ListEmailAttachmentsResponse, type ListEmailTagsResponse, type ListEmailsOptions, type ListEmailsResponse, type RequestOptions, type Result, Retransmit, type RetransmitError, type RetransmitOptions, SMS_PROVIDERS, SMS_STATUSES, type SendBatchResponse, type SendEmailOptions, type SendEmailResponse, type SendSmsOptions, type SendSmsResponse, type SendWhatsappOptions, type SendWhatsappResponse, Sms, type SmsEvent, type SmsProvider, type SmsStatus, WHATSAPP_STATUSES, Whatsapp, type WhatsappDocument, type WhatsappEvent, type WhatsappMedia, type WhatsappMessageType, type WhatsappStatus, type WhatsappTemplate };
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,18 @@ interface RetransmitOptions {
|
|
|
20
20
|
/** Override the API origin. Defaults to `https://api.retransmit.dev` (or `RETRANSMIT_BASE_URL`). */
|
|
21
21
|
baseUrl?: string;
|
|
22
22
|
}
|
|
23
|
+
/** Per-request options, passed as the second argument of `emails.send` and `batch.send`. */
|
|
24
|
+
interface RequestOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Makes the request safe to retry. Sent as the `Idempotency-Key` header.
|
|
27
|
+
* For 24 hours, a retry with the same key and payload returns the original
|
|
28
|
+
* response instead of queuing the email again. 1 to 256 characters; a UUID
|
|
29
|
+
* or `<event>/<entity-id>` such as `welcome-user/123` works well. A retry
|
|
30
|
+
* with a different payload fails with `invalid_idempotent_request`, and one
|
|
31
|
+
* that overlaps the first request fails with `concurrent_idempotent_requests`.
|
|
32
|
+
*/
|
|
33
|
+
idempotencyKey?: string;
|
|
34
|
+
}
|
|
23
35
|
interface SendEmailOptions {
|
|
24
36
|
/** Sender, as `address@domain.com` or `Name <address@domain.com>`. The domain must be verified on your account. */
|
|
25
37
|
from: string;
|
|
@@ -58,8 +70,58 @@ interface SendEmailOptions {
|
|
|
58
70
|
* List-Unsubscribe headers take precedence over yours.
|
|
59
71
|
*/
|
|
60
72
|
headers?: EmailHeaders;
|
|
73
|
+
/**
|
|
74
|
+
* Files to attach, up to 20 per email and 30 MB in total. Each needs a
|
|
75
|
+
* `filename` and either `content` (the bytes) or `path` (a public URL
|
|
76
|
+
* fetched when you call `send`). Add `contentId` to embed an image inline.
|
|
77
|
+
* Not accepted by `batch.send`.
|
|
78
|
+
*/
|
|
79
|
+
attachments?: Attachment[];
|
|
61
80
|
}
|
|
62
81
|
type EmailHeaders = Record<string, string>;
|
|
82
|
+
/** A file to attach. Provide exactly one of `content` and `path`. */
|
|
83
|
+
interface Attachment {
|
|
84
|
+
/**
|
|
85
|
+
* Name the recipient sees, up to 255 characters, no path separators. Its
|
|
86
|
+
* extension sets the content type when `contentType` is omitted.
|
|
87
|
+
*/
|
|
88
|
+
filename: string;
|
|
89
|
+
/** The file: a `Buffer`, a `Uint8Array`, or an already base64 encoded string. */
|
|
90
|
+
content?: string | Uint8Array;
|
|
91
|
+
/**
|
|
92
|
+
* Public http(s) URL the file is fetched from while the request runs, with
|
|
93
|
+
* a 15 second budget. Private and internal hosts are refused.
|
|
94
|
+
*/
|
|
95
|
+
path?: string;
|
|
96
|
+
/** MIME type such as `application/pdf`. Inferred from the filename when omitted. */
|
|
97
|
+
contentType?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Embeds the file inline and sets its Content-ID. Reference it in `html`
|
|
100
|
+
* as `<img src="cid:the-id">`. Letters, digits, `.`, `_`, `@` and `-`, up
|
|
101
|
+
* to 128 characters, unique per email.
|
|
102
|
+
*/
|
|
103
|
+
contentId?: string;
|
|
104
|
+
}
|
|
105
|
+
/** An attachment as it was sent, without a download link. */
|
|
106
|
+
interface EmailAttachment {
|
|
107
|
+
id: string;
|
|
108
|
+
filename: string;
|
|
109
|
+
content_type: string;
|
|
110
|
+
/** Size in bytes of the decoded file. */
|
|
111
|
+
size: number;
|
|
112
|
+
content_id: string | null;
|
|
113
|
+
/** True for images embedded via `contentId`. */
|
|
114
|
+
inline: boolean;
|
|
115
|
+
}
|
|
116
|
+
interface EmailAttachmentWithDownload extends EmailAttachment {
|
|
117
|
+
/** When the stored file is deleted, 30 days after the send. */
|
|
118
|
+
expires_at: string;
|
|
119
|
+
/** Signed link to download the file, valid for one hour. `null` once the file has expired. */
|
|
120
|
+
download_url: string | null;
|
|
121
|
+
}
|
|
122
|
+
interface ListEmailAttachmentsResponse {
|
|
123
|
+
attachments: EmailAttachmentWithDownload[];
|
|
124
|
+
}
|
|
63
125
|
interface EmailTag {
|
|
64
126
|
name: string;
|
|
65
127
|
value: string;
|
|
@@ -91,6 +153,8 @@ interface GetEmailResponse {
|
|
|
91
153
|
created_at: string;
|
|
92
154
|
last_event_at: string | null;
|
|
93
155
|
events: EmailEvent[];
|
|
156
|
+
/** Attachments given at send time, without download links. See `emails.attachments`. */
|
|
157
|
+
attachments: EmailAttachment[];
|
|
94
158
|
}
|
|
95
159
|
interface ListEmailsOptions {
|
|
96
160
|
/**
|
|
@@ -136,16 +200,38 @@ interface ListEmailTagsResponse {
|
|
|
136
200
|
}
|
|
137
201
|
declare const SMS_STATUSES: readonly ["queued", "sent", "delivered", "undelivered", "expired", "rejected", "failed"];
|
|
138
202
|
type SmsStatus = (typeof SMS_STATUSES)[number];
|
|
203
|
+
/**
|
|
204
|
+
* Carriers a send can be pinned to. Carrier-level, not per-country: `mtn`
|
|
205
|
+
* covers every MTN network Retransmit integrates with. `sns` (AWS End User
|
|
206
|
+
* Messaging) is first because it is the one that reaches every destination.
|
|
207
|
+
*/
|
|
208
|
+
declare const SMS_PROVIDERS: readonly ["sns", "mtn", "orange"];
|
|
209
|
+
type SmsProvider = (typeof SMS_PROVIDERS)[number];
|
|
139
210
|
interface SendSmsOptions {
|
|
140
211
|
/**
|
|
141
212
|
* Sender id shown on the recipient's device (up to 11 characters:
|
|
142
|
-
* letters, digits, space, - and _).
|
|
143
|
-
*
|
|
213
|
+
* letters, digits, space, - and _).
|
|
214
|
+
*
|
|
215
|
+
* Approval is per country, so this must be a name your organization has had
|
|
216
|
+
* approved for the destination; request it in the dashboard under
|
|
217
|
+
* SMS > Sender IDs, which asks for the name and the countries, and for
|
|
218
|
+
* registration details only where the carriers require a filing. Sending
|
|
219
|
+
* with an unapproved name fails with `sender_not_allowed`. Leave it out to
|
|
220
|
+
* use your approved sender for that country, or the provider default when
|
|
221
|
+
* you have none.
|
|
144
222
|
*/
|
|
145
223
|
from?: string;
|
|
146
224
|
/** One recipient or up to 50, in international format (`+237670000000`). All must share one country. */
|
|
147
225
|
to: string | string[];
|
|
148
226
|
text: string;
|
|
227
|
+
/**
|
|
228
|
+
* Pins the send to one carrier. Leave it out to let Retransmit route by
|
|
229
|
+
* destination country and price. `sns` covers every destination; `mtn` and
|
|
230
|
+
* `orange` only the countries Retransmit has that carrier in. A pinned send
|
|
231
|
+
* never falls back to another carrier: it fails with `no_route` when that
|
|
232
|
+
* one cannot deliver.
|
|
233
|
+
*/
|
|
234
|
+
provider?: SmsProvider;
|
|
149
235
|
}
|
|
150
236
|
interface SendSmsResponse {
|
|
151
237
|
id: string;
|
|
@@ -167,7 +253,13 @@ interface GetSmsResponse {
|
|
|
167
253
|
text: string;
|
|
168
254
|
country: string | null;
|
|
169
255
|
segments: number;
|
|
170
|
-
/**
|
|
256
|
+
/** Carrier the send was pinned to, null when routing chose freely. */
|
|
257
|
+
requested_provider: SmsProvider | null;
|
|
258
|
+
/**
|
|
259
|
+
* Country operation that carried the message, e.g. `mtn_cm`, `orange_cm`
|
|
260
|
+
* or `aws_sns`. More specific than `requested_provider`, and null until
|
|
261
|
+
* the message is routed.
|
|
262
|
+
*/
|
|
171
263
|
provider: string | null;
|
|
172
264
|
status: SmsStatus;
|
|
173
265
|
error: string | null;
|
|
@@ -269,8 +361,12 @@ interface GetBatchResponse {
|
|
|
269
361
|
declare class Batch {
|
|
270
362
|
private readonly client;
|
|
271
363
|
constructor(client: Retransmit);
|
|
272
|
-
/**
|
|
273
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Queues up to 10,000 emails in one request. Track progress with `get(id)`.
|
|
366
|
+
* Pass `{ idempotencyKey }` covering the whole batch to make the call safe
|
|
367
|
+
* to retry.
|
|
368
|
+
*/
|
|
369
|
+
send(emails: SendEmailOptions[], requestOptions?: RequestOptions): Promise<Result<SendBatchResponse>>;
|
|
274
370
|
/** Batch progress: how many emails are in each status so far. */
|
|
275
371
|
get(id: string): Promise<Result<GetBatchResponse>>;
|
|
276
372
|
}
|
|
@@ -278,8 +374,11 @@ declare class Batch {
|
|
|
278
374
|
declare class Emails {
|
|
279
375
|
private readonly client;
|
|
280
376
|
constructor(client: Retransmit);
|
|
281
|
-
/**
|
|
282
|
-
|
|
377
|
+
/**
|
|
378
|
+
* Queues a single email. Poll `get(id)` or subscribe to webhooks for the
|
|
379
|
+
* outcome. Pass `{ idempotencyKey }` to make the call safe to retry.
|
|
380
|
+
*/
|
|
381
|
+
send(options: SendEmailOptions, requestOptions?: RequestOptions): Promise<Result<SendEmailResponse>>;
|
|
283
382
|
/** Retrieves an email with its current status and event history. */
|
|
284
383
|
get(id: string): Promise<Result<GetEmailResponse>>;
|
|
285
384
|
/**
|
|
@@ -289,6 +388,14 @@ declare class Emails {
|
|
|
289
388
|
list(options?: ListEmailsOptions): Promise<Result<ListEmailsResponse>>;
|
|
290
389
|
/** Every distinct tag on your emails, with a count of emails carrying it. */
|
|
291
390
|
tags(): Promise<Result<ListEmailTagsResponse>>;
|
|
391
|
+
/**
|
|
392
|
+
* The attachments of an email, each with a signed `download_url` valid for
|
|
393
|
+
* one hour. Files are kept for 30 days after the send; after that the link
|
|
394
|
+
* is `null` and only the metadata remains.
|
|
395
|
+
*/
|
|
396
|
+
attachments(emailId: string): Promise<Result<ListEmailAttachmentsResponse>>;
|
|
397
|
+
/** One attachment of an email with a signed download link. */
|
|
398
|
+
getAttachment(emailId: string, attachmentId: string): Promise<Result<EmailAttachmentWithDownload>>;
|
|
292
399
|
}
|
|
293
400
|
|
|
294
401
|
declare class Sms {
|
|
@@ -322,7 +429,7 @@ declare class Retransmit {
|
|
|
322
429
|
private readonly baseUrl;
|
|
323
430
|
constructor(apiKey?: string, options?: RetransmitOptions);
|
|
324
431
|
/** Internal transport shared by the resource classes. API failures are returned, never thrown. */
|
|
325
|
-
request<T>(method: "GET" | "POST", path: string, body?: unknown, query?: Record<string, string | number | string[] | undefined
|
|
432
|
+
request<T>(method: "GET" | "POST", path: string, body?: unknown, query?: Record<string, string | number | string[] | undefined>, options?: RequestOptions): Promise<Result<T>>;
|
|
326
433
|
}
|
|
327
434
|
|
|
328
|
-
export { Batch, EMAIL_STATUSES, type EmailEvent, type EmailStatus, type EmailSummary, type EmailTag, type EmailTagCount, Emails, type GetBatchResponse, type GetEmailResponse, type GetSmsResponse, type GetWhatsappResponse, type ListEmailTagsResponse, type ListEmailsOptions, type ListEmailsResponse, type Result, Retransmit, type RetransmitError, type RetransmitOptions, SMS_STATUSES, type SendBatchResponse, type SendEmailOptions, type SendEmailResponse, type SendSmsOptions, type SendSmsResponse, type SendWhatsappOptions, type SendWhatsappResponse, Sms, type SmsEvent, type SmsStatus, WHATSAPP_STATUSES, Whatsapp, type WhatsappDocument, type WhatsappEvent, type WhatsappMedia, type WhatsappMessageType, type WhatsappStatus, type WhatsappTemplate };
|
|
435
|
+
export { type Attachment, Batch, EMAIL_STATUSES, type EmailAttachment, type EmailAttachmentWithDownload, type EmailEvent, type EmailStatus, type EmailSummary, type EmailTag, type EmailTagCount, Emails, type GetBatchResponse, type GetEmailResponse, type GetSmsResponse, type GetWhatsappResponse, type ListEmailAttachmentsResponse, type ListEmailTagsResponse, type ListEmailsOptions, type ListEmailsResponse, type RequestOptions, type Result, Retransmit, type RetransmitError, type RetransmitOptions, SMS_PROVIDERS, SMS_STATUSES, type SendBatchResponse, type SendEmailOptions, type SendEmailResponse, type SendSmsOptions, type SendSmsResponse, type SendWhatsappOptions, type SendWhatsappResponse, Sms, type SmsEvent, type SmsProvider, type SmsStatus, WHATSAPP_STATUSES, Whatsapp, type WhatsappDocument, type WhatsappEvent, type WhatsappMedia, type WhatsappMessageType, type WhatsappStatus, type WhatsappTemplate };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
// src/emails.ts
|
|
2
|
+
function toBase64(bytes) {
|
|
3
|
+
if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
|
|
4
|
+
let binary = "";
|
|
5
|
+
const CHUNK = 32768;
|
|
6
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
7
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
8
|
+
}
|
|
9
|
+
return btoa(binary);
|
|
10
|
+
}
|
|
11
|
+
function toWireAttachment(attachment) {
|
|
12
|
+
return {
|
|
13
|
+
filename: attachment.filename,
|
|
14
|
+
content: attachment.content === void 0 || typeof attachment.content === "string" ? attachment.content : toBase64(attachment.content),
|
|
15
|
+
path: attachment.path,
|
|
16
|
+
content_type: attachment.contentType,
|
|
17
|
+
content_id: attachment.contentId
|
|
18
|
+
};
|
|
19
|
+
}
|
|
2
20
|
function toWirePayload(options) {
|
|
3
21
|
return {
|
|
4
22
|
from: options.from,
|
|
@@ -11,7 +29,8 @@ function toWirePayload(options) {
|
|
|
11
29
|
text: options.text,
|
|
12
30
|
marketing: options.marketing,
|
|
13
31
|
tags: options.tags,
|
|
14
|
-
headers: options.headers
|
|
32
|
+
headers: options.headers,
|
|
33
|
+
attachments: options.attachments?.map(toWireAttachment)
|
|
15
34
|
};
|
|
16
35
|
}
|
|
17
36
|
var Emails = class {
|
|
@@ -19,9 +38,18 @@ var Emails = class {
|
|
|
19
38
|
this.client = client;
|
|
20
39
|
}
|
|
21
40
|
client;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Queues a single email. Poll `get(id)` or subscribe to webhooks for the
|
|
43
|
+
* outcome. Pass `{ idempotencyKey }` to make the call safe to retry.
|
|
44
|
+
*/
|
|
45
|
+
send(options, requestOptions) {
|
|
46
|
+
return this.client.request(
|
|
47
|
+
"POST",
|
|
48
|
+
"/v1/emails",
|
|
49
|
+
toWirePayload(options),
|
|
50
|
+
void 0,
|
|
51
|
+
requestOptions
|
|
52
|
+
);
|
|
25
53
|
}
|
|
26
54
|
/** Retrieves an email with its current status and event history. */
|
|
27
55
|
get(id) {
|
|
@@ -44,6 +72,21 @@ var Emails = class {
|
|
|
44
72
|
tags() {
|
|
45
73
|
return this.client.request("GET", "/v1/emails/tags");
|
|
46
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* The attachments of an email, each with a signed `download_url` valid for
|
|
77
|
+
* one hour. Files are kept for 30 days after the send; after that the link
|
|
78
|
+
* is `null` and only the metadata remains.
|
|
79
|
+
*/
|
|
80
|
+
attachments(emailId) {
|
|
81
|
+
return this.client.request("GET", `/v1/emails/${encodeURIComponent(emailId)}/attachments`);
|
|
82
|
+
}
|
|
83
|
+
/** One attachment of an email with a signed download link. */
|
|
84
|
+
getAttachment(emailId, attachmentId) {
|
|
85
|
+
return this.client.request(
|
|
86
|
+
"GET",
|
|
87
|
+
`/v1/emails/${encodeURIComponent(emailId)}/attachments/${encodeURIComponent(attachmentId)}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
47
90
|
};
|
|
48
91
|
|
|
49
92
|
// src/batch.ts
|
|
@@ -52,11 +95,19 @@ var Batch = class {
|
|
|
52
95
|
this.client = client;
|
|
53
96
|
}
|
|
54
97
|
client;
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Queues up to 10,000 emails in one request. Track progress with `get(id)`.
|
|
100
|
+
* Pass `{ idempotencyKey }` covering the whole batch to make the call safe
|
|
101
|
+
* to retry.
|
|
102
|
+
*/
|
|
103
|
+
send(emails, requestOptions) {
|
|
104
|
+
return this.client.request(
|
|
105
|
+
"POST",
|
|
106
|
+
"/v1/emails/batch",
|
|
107
|
+
{ emails: emails.map(toWirePayload) },
|
|
108
|
+
void 0,
|
|
109
|
+
requestOptions
|
|
110
|
+
);
|
|
60
111
|
}
|
|
61
112
|
/** Batch progress: how many emails are in each status so far. */
|
|
62
113
|
get(id) {
|
|
@@ -75,7 +126,8 @@ var Sms = class {
|
|
|
75
126
|
return this.client.request("POST", "/v1/sms", {
|
|
76
127
|
from: options.from,
|
|
77
128
|
to: options.to,
|
|
78
|
-
text: options.text
|
|
129
|
+
text: options.text,
|
|
130
|
+
provider: options.provider
|
|
79
131
|
});
|
|
80
132
|
}
|
|
81
133
|
/** Retrieves an SMS with its current status and event history. */
|
|
@@ -115,7 +167,7 @@ var Whatsapp = class {
|
|
|
115
167
|
|
|
116
168
|
// src/retransmit.ts
|
|
117
169
|
var DEFAULT_BASE_URL = "https://api.retransmit.dev";
|
|
118
|
-
var USER_AGENT = "retransmit.dev-node/0.
|
|
170
|
+
var USER_AGENT = "retransmit.dev-node/0.6.0";
|
|
119
171
|
function readEnv(name) {
|
|
120
172
|
return typeof process !== "undefined" ? process.env?.[name] : void 0;
|
|
121
173
|
}
|
|
@@ -140,7 +192,7 @@ var Retransmit = class {
|
|
|
140
192
|
);
|
|
141
193
|
}
|
|
142
194
|
/** Internal transport shared by the resource classes. API failures are returned, never thrown. */
|
|
143
|
-
async request(method, path, body, query) {
|
|
195
|
+
async request(method, path, body, query, options = {}) {
|
|
144
196
|
const params = new URLSearchParams();
|
|
145
197
|
for (const [key, value] of Object.entries(query ?? {})) {
|
|
146
198
|
if (value === void 0) continue;
|
|
@@ -148,15 +200,19 @@ var Retransmit = class {
|
|
|
148
200
|
}
|
|
149
201
|
const encoded = params.toString();
|
|
150
202
|
const search = encoded ? `?${encoded}` : "";
|
|
203
|
+
const headers = {
|
|
204
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
205
|
+
"Content-Type": "application/json",
|
|
206
|
+
"User-Agent": USER_AGENT
|
|
207
|
+
};
|
|
208
|
+
if (options.idempotencyKey !== void 0) {
|
|
209
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
210
|
+
}
|
|
151
211
|
let response;
|
|
152
212
|
try {
|
|
153
213
|
response = await fetch(`${this.baseUrl}${path}${search}`, {
|
|
154
214
|
method,
|
|
155
|
-
headers
|
|
156
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
157
|
-
"Content-Type": "application/json",
|
|
158
|
-
"User-Agent": USER_AGENT
|
|
159
|
-
},
|
|
215
|
+
headers,
|
|
160
216
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
161
217
|
});
|
|
162
218
|
} catch (cause) {
|
|
@@ -212,12 +268,14 @@ var SMS_STATUSES = [
|
|
|
212
268
|
"rejected",
|
|
213
269
|
"failed"
|
|
214
270
|
];
|
|
271
|
+
var SMS_PROVIDERS = ["sns", "mtn", "orange"];
|
|
215
272
|
var WHATSAPP_STATUSES = ["queued", "sent", "delivered", "read", "failed"];
|
|
216
273
|
export {
|
|
217
274
|
Batch,
|
|
218
275
|
EMAIL_STATUSES,
|
|
219
276
|
Emails,
|
|
220
277
|
Retransmit,
|
|
278
|
+
SMS_PROVIDERS,
|
|
221
279
|
SMS_STATUSES,
|
|
222
280
|
Sms,
|
|
223
281
|
WHATSAPP_STATUSES,
|